Use FixedPostUpdate by default and simplify scheduling#457
Conversation
I think <Fixed> is missing. app.insert_resource(Time::<Fixed>::from_hz(60.0))); |
|
That's optional since it's inferred, but I added it to the description for clarity. |
Wouldn't this insert the resource |
|
No, If you did have multiple different clocks with |
ooo okay. I had assumed |
|
If you haven't merged this by then, I'll review it after the jam :) |
|
Why FixedUpdate instead of FixedPostUpdate for physics? |
FixedUpdate by default and simplify schedulingFixedPostUpdate by default and simplify scheduling
I responded on Discord already, but we discussed this briefly a few days ago, and came to the conclusion that |
Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
Objective
Closes #263 (the removal detection issue should also already be fixed thanks to hooks and observers)
So far, Avian has run in
PostUpdateby default, but with a custom fixed timestep solution. This has given us more control over scheduling and allowed working around some historical issues with Bevy's ownFixedUpdate, but nowadays Bevy's fixed timestep schedules are much more usable.Having two different fixed timesteps is confusing, annoying to maintain, and duplicates a lot of API surface area. The scheduling in Avian 0.1 also has other serious issues:
PostUpdate, even with the built-in fixed timestep.For a native experience, Avian should be using Bevy's own fixed timestep and scheduling.
Solution
Physics now runs in
FixedPostUpdateby default.TimestepModehas been removed, andTime<Physics>no longer has a custom timestep. Instead, it follows the clock of the schedule where physics is run in. InFixedPostUpdate, physics usesTime<Fixed>, but if physics is instead configured to run in a schedule likePostUpdate, it will useTime<Virtual>.Previously, the physics timestep could be configured like this:
In schedules with a fixed timestep, you even needed to use
fixed_once_hz, which was rather confusing and footgunny:Now, if you are running physics in
FixedPostUpdate, you should simply configureTime<Fixed>directly:Time<Physics>still exists to allow people to configure the simulation speed, pause and unpause the simulation independently of the schedule's default clock, and set up their own custom scheduling for physics.Running physics with Bevy's fixed timestep has also fixed the other issues mentioned earlier: physics no longer runs slower at lower frame rates, and behavior seems a lot more deterministic (at least without the
parallelfeature). More testing is required to determine if we have full cross-platform determinism though.Why
FixedPostUpdateinstead ofFixedUpdate?FixedUpdateis very often used for gameplay logic and various kinds of simulations. It is also commonly used for applying physics logic, like character movement, explosions, moving platforms, effects that apply forces/impulses, custom gravity, and so on.For a lot of these use cases, it is important to run logic before physics, and if physics was run in
FixedUpdate, systems would need to be ordered explicitly, which would not be a good experience. And if you didn't do that, you could get determinism issues caused by system ordering ambiguities, along with frame delay issues.And as for
FixedPreUpdate: if we ran physics before gameplay logic inFixedUpdate, movement and anything else that affects physics could have an additional delay of one or more frames.I believe that using
FixedPostUpdateis the sensible default, and it is also in line with engines like Unity and Godot, where internal physics is run near the end of the fixed update/process step. People can also always configure the ordering in their own applications if needed.Caveats
Bevy's fixed timestep is 64 Hz by default, unlike our old default of 60 Hz. This can lead to noticeable jitter on 60 Hz displays, as physics is sometimes run twice within a single frame. This can be partially worked around by configuring
Time<Fixed>, but I am also implementing transform interpolation and extrapolation, which should make it possible to fix the issue properly by smoothing out the visual result.Another change to the old scheduling is that physics no longer runs during the first frame. This is because Bevy's
FixedUpdateand other fixed timestep schedules don't seem to run until the second update.Migration Guide
Previously, physics was run in
PostUpdatewith a custom fixed timestep by default. The primary purpose of the fixed timestep is to make behavior consistent and frame rate independent.This custom scheduling logic has been removed, and physics now runs in Bevy's
FixedPostUpdateby default. This further unifies physics with Bevy's own APIs and simplifies scheduling. However, it also means that physics now runs beforeUpdate, unlike before.For most users, no changes should be necessary, and systems that were running in
Updatecan remain there. If you want to run systems at the same fixed timestep as physics, consider usingFixedUpdate.The
Time<Physics>clock now automatically follows the clock used by the schedule that physics is run in. InFixedPostUpdateand other schedules with a fixed timestep,Time<Fixed>is used, but if physics is instead configured to run in a schedule likePostUpdate, it will useTime<Virtual>.Previously, the physics timestep could be configured like this:
Now, if you are running physics in
FixedPostUpdate, you should simply configureTime<Fixed>directly:The following types and methods have also been removed as a part of this rework:
TimestepModePhysics::from_timestepPhysics::fixed_hzPhysics::fixed_once_hzPhysics::variableTime::<Physics>::from_timestepTime::<Physics>::timestep_modeTime::<Physics>::timestep_mode_mutTime::<Physics>::set_timestep_mode