Open TheYellowArchitect opened 1 month ago
On rewinding to any tick, ProjectileHistorian checks if a projectile which no longer exists on this tick (79) exists on the target tick (75) and if so, it instantiates its PackedScene.
I'm not convinced that destroying and re-instantiating nodes is the best way to go about managing liveness with rollback. Also pulling all projectiles into a centralized object like this goes against the current design philosophy of letting things to individually manage themselves by reacting to central signals. Either way, I'm also not convinced that projectiles should be pulled into a centralized ProjectileHistorian.
On data, this is also very game specific. I'm also not convinced that each projectile needs to have position stored, e.g. if the projectile's movement logic is very simple. Think of RTS games where projectiles get spawned at some location, and then continuously march toward a target unit. You don't need to store location for every tick, you can just calculate the position based on lifetime, starting position and the target unit's position.
There's also a reason why e.g. RollbackSynchronizer has configurable state properties - when developing netfox, I don't know the game you will be building. And I try to make the least amount of assumptions. Deciding that every projectile must have a position, rotation and velocity breaks that in a big way.
Extension of
NetworkWeapon
logic, sonetfox.extras
NetworkWeapon
does not care about its projectiles much, so I don't see how this would be an extension of it. But projectile logic in general fits in extras, that's true.
So to clarify, you're requesting an example on how to do projectiles with rollback, right?
I'm not convinced that destroying and re-instantiating nodes is the best way to go about managing liveness with rollback.
Pooling can be utilized, which stores like the last X projectiles of each PackedScene :+1:
Also pulling all projectiles into a centralized object like this goes against the current design philosophy of letting things to individually manage themselves by reacting to central signals.
I agree, but how would you implement this feature, when Projectiles themselves are freed? The only answer is to hide+disable the projectile on impact, and free it when rollback history limit is reached. But when a rollback happens after it is destroyed, it must hook to some central signals somewhere, to know to reactivate+reposition itself. NetworkRollback
itself shouldn't be related to Projectiles, so ProjectileHistorian can be used. Though I guess, the logic could move from the ProjectileHistorian to the Projectiles themselves, via some static function.
I'm also not convinced that each projectile needs to have position stored, e.g. if the projectile's movement logic is very simple. Think of RTS games where projectiles get spawned at some location, and then continuously march toward a target unit. You don't need to store location for every tick, you can just calculate the position based on lifetime, starting position and the target unit's position.
Didn't think of that, sounds like it saves a lot of memory :+1:
There's also a reason why e.g. RollbackSynchronizer has configurable state properties - when developing netfox, I don't know the game you will be building. And I try to make the least amount of assumptions. Deciding that every projectile must have a position, rotation and velocity breaks that in a big way.
I agree. Making usage of PropertyEntry
sounds good here, since that class already exists
So to clarify, you're requesting an example on how to do projectiles with rollback, right?
yup, I need this for my game (ideally with projectiles included in inputs so I can have a clean replay system)
I'm also not convinced that each projectile needs to have position stored, e.g. if the projectile's movement logic is very simple. Think of RTS games where projectiles get spawned at some location, and then continuously march toward a target unit. You don't need to store location for every tick, you can just calculate the position based on lifetime, starting position and the target unit's position.
There's also a reason why e.g. RollbackSynchronizer has configurable state properties - when developing netfox, I don't know the game you will be building. And I try to make the least amount of assumptions. Deciding that every projectile must have a position, rotation and velocity breaks that in a big way.
I agree. In my case, I want a grenade, so I do need its position since it bounces off walls/players. So having the position as a state property (not hardcoded) ftw
:sparkles: Description
Projectiles don't work with Rollback:
Rollback currently works per entity, using
RollbackSynchronizer
. I agree in the above that each Projectile should not have aRollbackSynchronizer
but what should exist to make them viable for rollback is critical events to track which tick it was created, which tick it was destroyed (this logic can be used for more entities btw)So, at the
NetworkRollback
level, there would be aProjectileHistorian
node, which stores a Dictionary of Projectiles, and important data for them.Code Use-Case
A projectile is spawned at tick 57, and explodes at tick 78. The game is at tick 79, and a rewind happens to tick 75. I assume this would happen if a new input is received for tick 75.
On rewinding to any tick,
ProjectileHistorian
checks if a projectile which no longer exists on this tick (79) exists on the target tick (75) and if so, it instantiates itsPackedScene
.Data
Taking the above code use-case in mind, here is what a
ProjectileHistorian
must store for every projectile:PackedScene
(for instantiating it on rollback)So
ProjectileHistorian
simply has aDictionary<ProjectileID, ProjectileData>
and aDictionary<ProjectileID, Dictionary<tick, ProjectileProperties>>
We could even extrapolate the creation ticks and death ticks completely, and hence probably skip/simplify
ProjectileData
. The first tick inDictionary<ProjectileID, Dictionary<tick, ProjectileProperties>>
is the creation tick. The death tick becomes a function, e.g.In this way,
ProjectileData
is needless. You need 2 primitive dictionaries instead in its place:And to register the projectile properties on the ticks:
The above 3 dictionaries is the only dictionaries the
ProjectileHistorian
needs.Btw, it is certain that this
ProjectileHistorian
must hook into some signals fromNetworkRollback
.Use-Case
I cannot think of any online game, 2D or 3D not using projectiles of some sort. Metroidvanias, Strategy, FPS, Action Games etc And given
NetworkWeapon
is included innetfox.extras
, this ensures projectiles work with the rest of netfox's rollback.Distribution
Extension of
NetworkWeapon
logic, sonetfox.extras
Notes
Requires the resolution of https://github.com/foxssake/netfox/issues/253 before starting a PR on this.
Once implemented, should be used by the forest brawl example.