TrueSync and Projectiles

Hey there

Projectiles are interesting. I'm still learning truesync, testing out its limitations and advantages. Implementing projectiles seems to be a really good way to put TrueSync to the test.

- Projectiles are rapid moving objects.
- There can be a lot of them active at the same time.
- To be efficient, you need to be able to pool and reuse projectile objects instead of just destroying and reinstating

So, I thought I would go ahead and implement some projectiles in my test scene. My test scene is a 3d scene where players control boxes. The players can hold a button to fire a machine gun. The projectiles should interact with other bodies in the scene.

If my projectiles are Rigidbodies with a sphere collider, things work relatively well. However, this is not a very efficient approach since there can be dozens of projectiles active at the same time.

A better way might be to treat each projectile as a raycast that sweeps through the scene as the projectile flies forward every synced update. This way I don't need dozens of Rigidbodies and sphere colliders. Just some raycasts.

Problem #1:

Raycasts are REALLY inefficient. 10 Raycasts and I loose more than half my FPS on my Pixel XL. Even on Pc, in the editor, the performance is not much better. It is almost unworkable. So if both players fire their machine guns, the game is unplayable.

Problem 2#:

You can't SyncedDestroy an object unless it has a rigidbody attached. This is really limiting. It means I have to attach pointless Rigidbodies on everything.

Problem 2.1#:

I can't attach a rigidbody without a collider. Why? This means, if I want to use SyncedDestroy, I need a rigidbody AND a collider, whether I need it or not.

Problem 2.2#:

There is no way to destroy an object that has a TrueSyncBehaviour on it without using SyncedDestroy.

My Projectile script is a TrueSyncBehaviour. The Projectile script moves the transform forward every SyncedUpdate by the velocity of the projectile. Every SyncedUpdate, it performs a Raycasts from the previous position to the new position. If the Raycasts detects a hit, it means the projectile hit an object.

The projectile prefab is just a gameobject with a mesh filter, mesh renderer and Projectile script. NO rigidbody and NO collider. Really lightweight. This works fine, but there is NO WAY to destroy the projectile when it hits something. I can't call Destroy, because TrueSync throws errors since it is still trying to track the Projectile TrueSyncBehaviour. I can't call SyncedDestroy, because the projectile doesn't have a Rigidbody.

This is very restrictive.

Problem 3#:

Rollbacks can happen at any time, but there is no way to hook into the rollback functionality. There doesn't seem to be any way to make my scripts "aware" of a rollback.

How would I implement a pooling system? In a singleplayer game, the projectile will hit a wall, and then return back to the object pool. When the player shoots again, the projectile is removed from the pool and is reused.

But how would I handle rollbacks in a multiplayer game? The projectile hits a player and returns to the pool. The client rolls back 3 frames. Uh oh. I need to somehow remove the projectile from the pool and restore it's state. Also, I have to make sure the object pool is aware that the projectile has been removed due to a rollback so that the object pool does not give anyone else the projectile instance.

I understand this is a relatively complex issue, but I feel like TrueSync does not provide me with the tools to solve the problem.

Comments

  • Hello @Prodigga, first thanks a lot for your feedback.

    Problem 1:
    We know we have huge limitations on things related to physics, this is strictly related to the use of fixed point math, because we lose performance in basically every calculation. Our project has a lot of parts, we have many things to do so sometimes you will a feature that is not well polished. Because of this we decide in the last release (1.1.0) open the sources of both physics engines and math library, so anyone has the chance to improve a specific part while we don't work on it.

    Problem 2:
    Related to what I said above, we will focus on things related to TrueSync's core, like this problem (#2). It should be fine destroy a game object, but it is a complex situation when we have rollbacks, I think you noticed that well.

    Problem 3:
    We have plans to simplify rollback hooks, so in someway you can track if an object is safe for remotion or if things done in frame are ok to be reused, this way you could know when to reuse an object in the pool.
  • Prodigga
    Prodigga
    edited March 2017
    Is there any chance that you can opensource the entire thing? I keep drilling into the source, trying to figure out how to do something or make improvements, but I keep hitting walls. For example - AbstractLockstep. I have no idea what the AbstractLockstep implementation is doing (the one generated by AbstractLockstep.NewInstance). A lot of the itneresting stuff happen in there - for example, when I destroy a gameobject, the next AbstractLockstep.Update triggers a rollback, but I cant see why. I can only see the callstack.

    I'd love to be able to submit improvements and such, :) But there isn't a whole lot I can do at the moment.

    I am trying to decide if TrueSync will work for our next project. It shows a lot of promise. The fact that everything is deterministic is awesome. It really simplifies everything. I don't have to worry about whether player A shot player B before he went around the corner or not, for example. It just works. But these performance issues and limitations are a worry, so I am on the fence.
  • I understand @Prodigga, we see a lot of possibilities with TrueSync also. For now AbstractLockstep will remain closed :/, but it can be opensourced later, you are right about important things that happen there. We will try expose them in next releases, thanks for all report you are doing, we undersant that those issues can impose great limitation in different projects.