Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
//! This example shows how to properly handle player input,
//! advance a physics simulation in a fixed timestep, and display the results.
//!
//! The classic source for how and why this is done is Glenn Fiedler's article
//! [Fix Your Timestep!](https://gafferongames.com/post/fix_your_timestep/).
//! For a more Bevy-centric source, see
//! [this cheatbook entry](https://bevy-cheatbook.github.io/fundamentals/fixed-timestep.html).
//!
//! ## Motivation
//!
//! The naive way of moving a player is to just update their position like so:
//! ```no_run
//! transform.translation += velocity;
//! ```
//! The issue here is that the player's movement speed will be tied to the frame rate.
//! Faster machines will move the player faster, and slower machines will move the player slower.
//! In fact, you can observe this today when running some old games that did it this way on modern hardware!
//! The player will move at a breakneck pace.
//!
//! The more sophisticated way is to update the player's position based on the time that has passed:
//! ```no_run
//! transform.translation += velocity * time.delta_seconds();
//! ```
//! This way, velocity represents a speed in units per second, and the player will move at the same speed
//! regardless of the frame rate.
//!
//! However, this can still be problematic if the frame rate is very low or very high.
//! If the frame rate is very low, the player will move in large jumps. This may lead to
//! a player moving in such large jumps that they pass through walls or other obstacles.
//! In general, you cannot expect a physics simulation to behave nicely with *any* delta time.
//! Ideally, we want to have some stability in what kinds of delta times we feed into our physics simulation.
//!
//! The solution is using a fixed timestep. This means that we advance the physics simulation by a fixed amount
//! at a time. If the real time that passed between two frames is less than the fixed timestep, we simply
//! don't advance the physics simulation at all.
//! If it is more, we advance the physics simulation multiple times until we catch up.
//! You can read more about how Bevy implements this in the documentation for
//! [`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
//!
//! This leaves us with a last problem, however. If our physics simulation may advance zero or multiple times
//! per frame, there may be frames in which the player's position did not need to be updated at all,
//! and some where it is updated by a large amount that resulted from running the physics simulation multiple times.
//! This is physically correct, but visually jarring. Imagine a player moving in a straight line, but depending on the frame rate,
//! they may sometimes advance by a large amount and sometimes not at all. Visually, we want the player to move smoothly.
//! This is why we need to separate the player's position in the physics simulation from the player's position in the visual representation.
//! The visual representation can then be interpolated smoothly based on the previous and current actual player position in the physics simulation.
//!
//! This is a tradeoff: every visual frame is now slightly lagging behind the actual physical frame,
//! but in return, the player's movement will appear smooth.
//! There are other ways to compute the visual representation of the player, such as extrapolation.
//! See the [documentation of the lightyear crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
//! for a nice overview of the different methods and their respective tradeoffs.
//!
//! ## Implementation
//!
2024-08-23 16:19:42 +00:00
//! - The player's inputs since the last physics update are stored in the `AccumulatedInput` component.
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
//! - The player's velocity is stored in a `Velocity` component. This is the speed in units per second.
//! - The player's current position in the physics simulation is stored in a `PhysicalTranslation` component.
//! - The player's previous position in the physics simulation is stored in a `PreviousPhysicalTranslation` component.
//! - The player's visual representation is stored in Bevy's regular `Transform` component.
//! - Every frame, we go through the following steps:
2024-08-23 16:19:42 +00:00
//! - Accumulate the player's input and set the current speed in the `handle_input` system.
//! This is run in the `RunFixedMainLoop` schedule, ordered in `RunFixedMainLoopSystem::BeforeFixedMainLoop`,
//! which runs before the fixed timestep loop. This is run every frame.
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
//! - Advance the physics simulation by one fixed timestep in the `advance_physics` system.
2024-08-23 16:19:42 +00:00
//! Accumulated input is consumed here.
//! This is run in the `FixedUpdate` schedule, which runs zero or multiple times per frame.
//! - Update the player's visual representation in the `interpolate_rendered_transform` system.
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
//! This interpolates between the player's previous and current position in the physics simulation.
2024-08-23 16:19:42 +00:00
//! It is run in the `RunFixedMainLoop` schedule, ordered in `RunFixedMainLoopSystem::AfterFixedMainLoop`,
//! which runs after the fixed timestep loop. This is run every frame.
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
//!
//!
//! ## Controls
//!
//! | Key Binding | Action |
//! |:---------------------|:--------------|
//! | `W` | Move up |
//! | `S` | Move down |
//! | `A` | Move left |
//! | `D` | Move right |
use bevy ::prelude ::* ;
fn main ( ) {
App ::new ( )
. add_plugins ( DefaultPlugins )
. add_systems ( Startup , ( spawn_text , spawn_player ) )
2024-08-23 16:19:42 +00:00
// Advance the physics simulation using a fixed timestep.
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
. add_systems ( FixedUpdate , advance_physics )
2024-08-23 16:19:42 +00:00
. add_systems (
// The `RunFixedMainLoop` schedule allows us to schedule systems to run before and after the fixed timestep loop.
RunFixedMainLoop ,
(
// The physics simulation needs to know the player's input, so we run this before the fixed timestep loop.
// Note that if we ran it in `Update`, it would be too late, as the physics simulation would already have been advanced.
// If we ran this in `FixedUpdate`, it would sometimes not register player input, as that schedule may run zero times per frame.
handle_input . in_set ( RunFixedMainLoopSystem ::BeforeFixedMainLoop ) ,
// The player's visual representation needs to be updated after the physics simulation has been advanced.
// This could be run in `Update`, but if we run it here instead, the systems in `Update`
// will be working with the `Transform` that will actually be shown on screen.
interpolate_rendered_transform . in_set ( RunFixedMainLoopSystem ::AfterFixedMainLoop ) ,
) ,
)
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
. run ( ) ;
}
2024-08-23 16:19:42 +00:00
/// A vector representing the player's input, accumulated over all frames that ran
/// since the last time the physics simulation was advanced.
#[ derive(Debug, Component, Clone, Copy, PartialEq, Default, Deref, DerefMut) ]
struct AccumulatedInput ( Vec2 ) ;
/// A vector representing the player's velocity in the physics simulation.
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
#[ derive(Debug, Component, Clone, Copy, PartialEq, Default, Deref, DerefMut) ]
struct Velocity ( Vec3 ) ;
/// The actual position of the player in the physics simulation.
/// This is separate from the `Transform`, which is merely a visual representation.
///
/// If you want to make sure that this component is always initialized
/// with the same value as the `Transform`'s translation, you can
/// use a [component lifecycle hook](https://docs.rs/bevy/0.14.0/bevy/ecs/component/struct.ComponentHooks.html)
#[ derive(Debug, Component, Clone, Copy, PartialEq, Default, Deref, DerefMut) ]
struct PhysicalTranslation ( Vec3 ) ;
/// The value [`PhysicalTranslation`] had in the last fixed timestep.
2024-08-23 16:19:42 +00:00
/// Used for interpolation in the `interpolate_rendered_transform` system.
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
#[ derive(Debug, Component, Clone, Copy, PartialEq, Default, Deref, DerefMut) ]
struct PreviousPhysicalTranslation ( Vec3 ) ;
/// Spawn the player sprite and a 2D camera.
fn spawn_player ( mut commands : Commands , asset_server : Res < AssetServer > ) {
2024-10-05 01:59:52 +00:00
commands . spawn ( Camera2d ) ;
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
commands . spawn ( (
Name ::new ( " Player " ) ,
SpriteBundle {
texture : asset_server . load ( " branding/icon.png " ) ,
transform : Transform ::from_scale ( Vec3 ::splat ( 0.3 ) ) ,
.. default ( )
} ,
2024-08-23 16:19:42 +00:00
AccumulatedInput ::default ( ) ,
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
Velocity ::default ( ) ,
PhysicalTranslation ::default ( ) ,
PreviousPhysicalTranslation ::default ( ) ,
) ) ;
}
/// Spawn a bit of UI text to explain how to move the player.
fn spawn_text ( mut commands : Commands ) {
commands
. spawn ( NodeBundle {
style : Style {
position_type : PositionType ::Absolute ,
bottom : Val ::Px ( 12.0 ) ,
left : Val ::Px ( 12.0 ) ,
.. default ( )
} ,
.. default ( )
} )
. with_children ( | parent | {
parent . spawn ( TextBundle ::from_section (
" Move the player with WASD " ,
TextStyle {
font_size : 25.0 ,
.. default ( )
} ,
) ) ;
} ) ;
}
2024-08-23 16:19:42 +00:00
/// Handle keyboard input and accumulate it in the `AccumulatedInput` component.
2024-09-24 11:42:59 +00:00
///
2024-08-23 16:19:42 +00:00
/// There are many strategies for how to handle all the input that happened since the last fixed timestep.
/// This is a very simple one: we just accumulate the input and average it out by normalizing it.
fn handle_input (
keyboard_input : Res < ButtonInput < KeyCode > > ,
mut query : Query < ( & mut AccumulatedInput , & mut Velocity ) > ,
) {
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
/// Since Bevy's default 2D camera setup is scaled such that
/// one unit is one pixel, you can think of this as
/// "How many pixels per second should the player move?"
const SPEED : f32 = 210.0 ;
2024-08-23 16:19:42 +00:00
for ( mut input , mut velocity ) in query . iter_mut ( ) {
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
if keyboard_input . pressed ( KeyCode ::KeyW ) {
2024-08-23 16:19:42 +00:00
input . y + = 1.0 ;
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
}
if keyboard_input . pressed ( KeyCode ::KeyS ) {
2024-08-23 16:19:42 +00:00
input . y - = 1.0 ;
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
}
if keyboard_input . pressed ( KeyCode ::KeyA ) {
2024-08-23 16:19:42 +00:00
input . x - = 1.0 ;
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
}
if keyboard_input . pressed ( KeyCode ::KeyD ) {
2024-08-23 16:19:42 +00:00
input . x + = 1.0 ;
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
}
// Need to normalize and scale because otherwise
// diagonal movement would be faster than horizontal or vertical movement.
2024-08-23 16:19:42 +00:00
// This effectively averages the accumulated input.
velocity . 0 = input . extend ( 0.0 ) . normalize_or_zero ( ) * SPEED ;
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
}
}
/// Advance the physics simulation by one fixed timestep. This may run zero or multiple times per frame.
///
/// Note that since this runs in `FixedUpdate`, `Res<Time>` would be `Res<Time<Fixed>>` automatically.
/// We are being explicit here for clarity.
fn advance_physics (
fixed_time : Res < Time < Fixed > > ,
mut query : Query < (
& mut PhysicalTranslation ,
& mut PreviousPhysicalTranslation ,
2024-08-23 16:19:42 +00:00
& mut AccumulatedInput ,
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
& Velocity ,
) > ,
) {
2024-08-23 16:19:42 +00:00
for (
mut current_physical_translation ,
mut previous_physical_translation ,
mut input ,
velocity ,
) in query . iter_mut ( )
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
{
previous_physical_translation . 0 = current_physical_translation . 0 ;
current_physical_translation . 0 + = velocity . 0 * fixed_time . delta_seconds ( ) ;
2024-08-23 16:19:42 +00:00
// Reset the input accumulator, as we are currently consuming all input that happened since the last fixed timestep.
input . 0 = Vec2 ::ZERO ;
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
}
}
2024-08-23 16:19:42 +00:00
fn interpolate_rendered_transform (
Add an example for doing movement in fixed timesteps (#14223)
_copy-pasted from my doc comment in the code_
# Objective
This example shows how to properly handle player input, advance a
physics simulation in a fixed timestep, and display the results.
The classic source for how and why this is done is Glenn Fiedler's
article [Fix Your
Timestep!](https://gafferongames.com/post/fix_your_timestep/).
## Motivation
The naive way of moving a player is to just update their position like
so:
```rust
transform.translation += velocity;
```
The issue here is that the player's movement speed will be tied to the
frame rate.
Faster machines will move the player faster, and slower machines will
move the player slower.
In fact, you can observe this today when running some old games that did
it this way on modern hardware!
The player will move at a breakneck pace.
The more sophisticated way is to update the player's position based on
the time that has passed:
```rust
transform.translation += velocity * time.delta_seconds();
```
This way, velocity represents a speed in units per second, and the
player will move at the same speed regardless of the frame rate.
However, this can still be problematic if the frame rate is very low or
very high. If the frame rate is very low, the player will move in large
jumps. This may lead to a player moving in such large jumps that they
pass through walls or other obstacles. In general, you cannot expect a
physics simulation to behave nicely with *any* delta time. Ideally, we
want to have some stability in what kinds of delta times we feed into
our physics simulation.
The solution is using a fixed timestep. This means that we advance the
physics simulation by a fixed amount at a time. If the real time that
passed between two frames is less than the fixed timestep, we simply
don't advance the physics simulation at all.
If it is more, we advance the physics simulation multiple times until we
catch up. You can read more about how Bevy implements this in the
documentation for
[`bevy::time::Fixed`](https://docs.rs/bevy/latest/bevy/time/struct.Fixed.html).
This leaves us with a last problem, however. If our physics simulation
may advance zero or multiple times per frame, there may be frames in
which the player's position did not need to be updated at all, and some
where it is updated by a large amount that resulted from running the
physics simulation multiple times. This is physically correct, but
visually jarring. Imagine a player moving in a straight line, but
depending on the frame rate, they may sometimes advance by a large
amount and sometimes not at all. Visually, we want the player to move
smoothly. This is why we need to separate the player's position in the
physics simulation from the player's position in the visual
representation. The visual representation can then be interpolated
smoothly based on the last and current actual player position in the
physics simulation.
This is a tradeoff: every visual frame is now slightly lagging behind
the actual physical frame, but in return, the player's movement will
appear smooth. There are other ways to compute the visual representation
of the player, such as extrapolation. See the [documentation of the
lightyear
crate](https://cbournhonesque.github.io/lightyear/book/concepts/advanced_replication/visual_interpolation.html)
for a nice overview of the different methods and their tradeoffs.
## Implementation
- The player's velocity is stored in a `Velocity` component. This is the
speed in units per second.
- The player's current position in the physics simulation is stored in a
`PhysicalTranslation` component.
- The player's previous position in the physics simulation is stored in
a `PreviousPhysicalTranslation` component.
- The player's visual representation is stored in Bevy's regular
`Transform` component.
- Every frame, we go through the following steps:
- Advance the physics simulation by one fixed timestep in the
`advance_physics` system.
This is run in the `FixedUpdate` schedule, which runs before the
`Update` schedule.
- Update the player's visual representation in the
`update_displayed_transform` system.
This interpolates between the player's previous and current position in
the physics simulation.
- Update the player's velocity based on the player's input in the
`handle_input` system.
## Relevant Issues
Related to #1259.
I'm also fairly sure I've seen an issue somewhere made by
@alice-i-cecile about showing how to move a character correctly in a
fixed timestep, but I cannot find it.
2024-07-09 14:23:10 +00:00
fixed_time : Res < Time < Fixed > > ,
mut query : Query < (
& mut Transform ,
& PhysicalTranslation ,
& PreviousPhysicalTranslation ,
) > ,
) {
for ( mut transform , current_physical_translation , previous_physical_translation ) in
query . iter_mut ( )
{
let previous = previous_physical_translation . 0 ;
let current = current_physical_translation . 0 ;
// The overstep fraction is a value between 0 and 1 that tells us how far we are between two fixed timesteps.
let alpha = fixed_time . overstep_fraction ( ) ;
let rendered_translation = previous . lerp ( current , alpha ) ;
transform . translation = rendered_translation ;
}
}