Skip to content

Commit

Permalink
Use FixedPostUpdate by default and simplify scheduling (#457)
Browse files Browse the repository at this point in the history
# Objective

Closes #263 (the removal detection issue should also already be fixed thanks to hooks and observers)

So far, Avian has run in `PostUpdate` by 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 own `FixedUpdate`, 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:

- Physics slows down at lower frame rates, for example when the window is not focused.
- Physics can be clearly indeterministic when running in `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 `FixedPostUpdate` by default. `TimestepMode` has been removed, and `Time<Physics>` no longer has a custom timestep. Instead, it follows the clock of the schedule where physics is run in. In `FixedPostUpdate`, physics uses `Time<Fixed>`, but if physics is instead configured to run in a schedule like `PostUpdate`, it will use `Time<Virtual>`.

Previously, the physics timestep could be configured like this:

```rust
app.insert_resource(Time::new_with(Physics::fixed_hz(60.0)));
```

In schedules with a fixed timestep, you even needed to use `fixed_once_hz`, which was rather confusing and footgunny:

```rust
app.insert_resource(Time::new_with(Physics::fixed_once_hz(60.0)));
```

Now, if you are running physics in `FixedPostUpdate`, you should simply configure `Time<Fixed>` directly:

```rust
app.insert_resource(Time::<Fixed>::from_hz(60.0)));
```

`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 `parallel` feature). More testing is required to determine if we have full cross-platform determinism though.

## Why `FixedPostUpdate` instead of `FixedUpdate`?

`FixedUpdate` is 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 in `FixedUpdate`, movement and anything else that affects physics could have an additional delay of one or more frames.

I believe that using `FixedPostUpdate` is 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 `FixedUpdate` and other fixed timestep schedules don't seem to run until the second update.

---

## Migration Guide

Previously, physics was run in `PostUpdate` with 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 `FixedFixedUpdate` by default. This further unifies physics with Bevy's own APIs and simplifies scheduling. However, it also means that physics now runs before `Update`, unlike before.

For most users, no changes should be necessary, and systems that were running in `Update` can remain there. If you want to run systems at the same fixed timestep as physics, consider using `FixedUpdate`.

The `Time<Physics>` clock now automatically follows the clock used by the schedule that physics is run in. In `FixedPostUpdate` and other schedules with a fixed timestep, `Time<Fixed>` is used, but if physics is instead configured to run in a schedule like `PostUpdate`, it will use `Time<Virtual>`.

Previously, the physics timestep could be configured like this:

```rust
app.insert_resource(Time::new_with(Physics::fixed_hz(60.0)));
```

Now, if you are running physics in `FixedPostUpdate`, you should simply configure `Time<Fixed>` directly:

```rust
app.insert_resource(Time::<Fixed>::from_hz(60.0)));
```

The following types and methods have also been removed as a part of this rework:

- `TimestepMode`
- `Physics::from_timestep`
- `Physics::fixed_hz`
- `Physics::fixed_once_hz`
- `Physics::variable`
- `Time::<Physics>::from_timestep`
- `Time::<Physics>::timestep_mode`
- `Time::<Physics>::timestep_mode_mut`
- `Time::<Physics>::set_timestep_mode`
  • Loading branch information
Jondolf authored Aug 11, 2024
1 parent 5041bea commit 4d082a7
Show file tree
Hide file tree
Showing 14 changed files with 550 additions and 797 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ expression: transform
---
Transform {
translation: Vec3(
8.333334,
8.328126,
0.0,
0.0,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ expression: transform
---
Transform {
translation: Vec3(
8.333334,
8.328126,
0.0,
0.0,
),
Expand Down
Loading

0 comments on commit 4d082a7

Please sign in to comment.