bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
34.33k stars 3.35k forks source link

Add a standardized way to track simulation time for interoperability #13306

Open NiseVoid opened 2 months ago

NiseVoid commented 2 months ago

What problem does this solve or what need does it fill?

There is currently no universal way to track the time of a running simulation. If it's just needed in user code it's simple enough to create a resource that tracks the number of ticks since the simulation started and use some constant for timesteps. However, when third-party plugins get involved there is no good interoperable solution. Some crates provide their own type, resulting in some vendor lock-in. Other crates can be configured for where to source their time, making them harder to set up.

These types are further complicated by conflicting needs of different crates. A networking crate most likely has a counter it only needs to go up, but a rollback crate depending on that networking crate would need it to also go backwards.

What solution would you like?

A centralized way to track the time in a simulation. It could be a fairly simple type like:

type SimulationClock {
    tick: u64,
    timestep: Duration,
}

It could either be it's own thing or a variant for Time, as Time<Simulation>. Incrementing it could either be left up to the user, or configured in a more out-of-the-box way like deciding between FixedUpdate and the main loop depending on the app's runner.

The tick value could also be a newtype on u64 (or u32) so it's clear what these values mean if they are requested in function signatures or stored in other types (for example storing the tick on which an event happened).

What alternative(s) have you considered?

Additional context

Here's a few crates that deal with some sort of simulation tick:

ggrs bevy_replicon lightyear bevy_bundlication (disclaimer: I wrote this crate, the next version will likely also no longer have this type since it has been rewritten as a layer on top of bevy_replcion)

alice-i-cecile commented 2 months ago

Can you explain why Time<()> doesn't meet the needs of networking crates here? This was intended to be a standard "in-game-time", which worked correctly both inside and outside of the fixed update loop.

NiseVoid commented 2 months ago

There's a few issues, but the main ones would be:

Even if someone decided to internally use Time<()> for their simulation tick, they would need to make a SystemParam containing a few other resources to work around these issues, this would in turn become yet another type lacking interoperability.

Adding this functionality to Time<()> would be another potential solution, but I'd imagine this would lead to confusing behavior with Time<Virtual> having ticks with a variable delta. It would also add one field that doesn't match with this line in the docs: The clock does not support time moving backwards, but I guess a Time<Simulation> would have the same problem.

NiseVoid commented 2 months ago

If there's interest in this I could pick this up once we have an idea of what it should look like (eg. part of Time, a separate Time<Simulation>, or a simple stand alone resource + plugin similar to FrameCount). I'd imagine @maniwani also has some opinions on this, since it relates to fixed timestep simulations and networking stuff.