Make `Resource` trait opt-in, requiring `#[derive(Resource)]` V2 (#5577)
*This PR description is an edited copy of #5007, written by @alice-i-cecile.*
# Objective
Follow-up to https://github.com/bevyengine/bevy/pull/2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds.
While ergonomic, this results in several drawbacks:
* it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource
* it is challenging to discover if a type is intended to be used as a resource
* we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component).
* dependencies can use the same Rust type as a resource in invisibly conflicting ways
* raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values
* we cannot capture a definitive list of possible resources to display to users in an editor
## Notes to reviewers
* Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits.
*ira: My commits are not as well organized :')*
* I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does.
* I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with https://github.com/bevyengine/bevy/issues/4981.
## Changelog
`Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro.
## Migration Guide
Add `#[derive(Resource)]` to all types you are using as a resource.
If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics.
`ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing.
Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead.
Co-authored-by: Alice <alice.i.cecile@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: devil-ira <justthecooldude@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-08-08 21:36:35 +00:00
|
|
|
use bevy_ecs::{reflect::ReflectResource, system::Resource};
|
bevy_reflect: `FromReflect` Ergonomics Implementation (#6056)
# Objective
**This implementation is based on
https://github.com/bevyengine/rfcs/pull/59.**
---
Resolves #4597
Full details and motivation can be found in the RFC, but here's a brief
summary.
`FromReflect` is a very powerful and important trait within the
reflection API. It allows Dynamic types (e.g., `DynamicList`, etc.) to
be formed into Real ones (e.g., `Vec<i32>`, etc.).
This mainly comes into play concerning deserialization, where the
reflection deserializers both return a `Box<dyn Reflect>` that almost
always contain one of these Dynamic representations of a Real type. To
convert this to our Real type, we need to use `FromReflect`.
It also sneaks up in other ways. For example, it's a required bound for
`T` in `Vec<T>` so that `Vec<T>` as a whole can be made `FromReflect`.
It's also required by all fields of an enum as it's used as part of the
`Reflect::apply` implementation.
So in other words, much like `GetTypeRegistration` and `Typed`, it is
very much a core reflection trait.
The problem is that it is not currently treated like a core trait and is
not automatically derived alongside `Reflect`. This makes using it a bit
cumbersome and easy to forget.
## Solution
Automatically derive `FromReflect` when deriving `Reflect`.
Users can then choose to opt-out if needed using the
`#[reflect(from_reflect = false)]` attribute.
```rust
#[derive(Reflect)]
struct Foo;
#[derive(Reflect)]
#[reflect(from_reflect = false)]
struct Bar;
fn test<T: FromReflect>(value: T) {}
test(Foo); // <-- OK
test(Bar); // <-- Panic! Bar does not implement trait `FromReflect`
```
#### `ReflectFromReflect`
This PR also automatically adds the `ReflectFromReflect` (introduced in
#6245) registration to the derived `GetTypeRegistration` impl— if the
type hasn't opted out of `FromReflect` of course.
<details>
<summary><h4>Improved Deserialization</h4></summary>
> **Warning**
> This section includes changes that have since been descoped from this
PR. They will likely be implemented again in a followup PR. I am mainly
leaving these details in for archival purposes, as well as for reference
when implementing this logic again.
And since we can do all the above, we might as well improve
deserialization. We can now choose to deserialize into a Dynamic type or
automatically convert it using `FromReflect` under the hood.
`[Un]TypedReflectDeserializer::new` will now perform the conversion and
return the `Box`'d Real type.
`[Un]TypedReflectDeserializer::new_dynamic` will work like what we have
now and simply return the `Box`'d Dynamic type.
```rust
// Returns the Real type
let reflect_deserializer = UntypedReflectDeserializer::new(®istry);
let mut deserializer = ron::de::Deserializer::from_str(input)?;
let output: SomeStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?;
// Returns the Dynamic type
let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(®istry);
let mut deserializer = ron::de::Deserializer::from_str(input)?;
let output: DynamicStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?;
```
</details>
---
## Changelog
* `FromReflect` is now automatically derived within the `Reflect` derive
macro
* This includes auto-registering `ReflectFromReflect` in the derived
`GetTypeRegistration` impl
* ~~Renamed `TypedReflectDeserializer::new` and
`UntypedReflectDeserializer::new` to
`TypedReflectDeserializer::new_dynamic` and
`UntypedReflectDeserializer::new_dynamic`, respectively~~ **Descoped**
* ~~Changed `TypedReflectDeserializer::new` and
`UntypedReflectDeserializer::new` to automatically convert the
deserialized output using `FromReflect`~~ **Descoped**
## Migration Guide
* `FromReflect` is now automatically derived within the `Reflect` derive
macro. Items with both derives will need to remove the `FromReflect`
one.
```rust
// OLD
#[derive(Reflect, FromReflect)]
struct Foo;
// NEW
#[derive(Reflect)]
struct Foo;
```
If using a manual implementation of `FromReflect` and the `Reflect`
derive, users will need to opt-out of the automatic implementation.
```rust
// OLD
#[derive(Reflect)]
struct Foo;
impl FromReflect for Foo {/* ... */}
// NEW
#[derive(Reflect)]
#[reflect(from_reflect = false)]
struct Foo;
impl FromReflect for Foo {/* ... */}
```
<details>
<summary><h4>Removed Migrations</h4></summary>
> **Warning**
> This section includes changes that have since been descoped from this
PR. They will likely be implemented again in a followup PR. I am mainly
leaving these details in for archival purposes, as well as for reference
when implementing this logic again.
* The reflect deserializers now perform a `FromReflect` conversion
internally. The expected output of `TypedReflectDeserializer::new` and
`UntypedReflectDeserializer::new` is no longer a Dynamic (e.g.,
`DynamicList`), but its Real counterpart (e.g., `Vec<i32>`).
```rust
let reflect_deserializer =
UntypedReflectDeserializer::new_dynamic(®istry);
let mut deserializer = ron::de::Deserializer::from_str(input)?;
// OLD
let output: DynamicStruct = reflect_deserializer.deserialize(&mut
deserializer)?.take()?;
// NEW
let output: SomeStruct = reflect_deserializer.deserialize(&mut
deserializer)?.take()?;
```
Alternatively, if this behavior isn't desired, use the
`TypedReflectDeserializer::new_dynamic` and
`UntypedReflectDeserializer::new_dynamic` methods instead:
```rust
// OLD
let reflect_deserializer = UntypedReflectDeserializer::new(®istry);
// NEW
let reflect_deserializer =
UntypedReflectDeserializer::new_dynamic(®istry);
```
</details>
---------
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2023-06-29 01:31:34 +00:00
|
|
|
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
2020-11-22 00:38:24 +00:00
|
|
|
use bevy_utils::{Duration, Instant};
|
2019-12-03 08:30:30 +00:00
|
|
|
|
2022-10-24 14:14:25 +00:00
|
|
|
/// A clock that tracks how much it has advanced (and how much real time has elapsed) since
|
|
|
|
/// its previous update and since its creation.
|
2023-08-15 21:48:37 +00:00
|
|
|
///
|
|
|
|
/// See [`TimeUpdateStrategy`], which allows you to customize the way that this is updated each frame.
|
|
|
|
///
|
|
|
|
/// [`TimeUpdateStrategy`]: crate::TimeUpdateStrategy
|
bevy_reflect: `FromReflect` Ergonomics Implementation (#6056)
# Objective
**This implementation is based on
https://github.com/bevyengine/rfcs/pull/59.**
---
Resolves #4597
Full details and motivation can be found in the RFC, but here's a brief
summary.
`FromReflect` is a very powerful and important trait within the
reflection API. It allows Dynamic types (e.g., `DynamicList`, etc.) to
be formed into Real ones (e.g., `Vec<i32>`, etc.).
This mainly comes into play concerning deserialization, where the
reflection deserializers both return a `Box<dyn Reflect>` that almost
always contain one of these Dynamic representations of a Real type. To
convert this to our Real type, we need to use `FromReflect`.
It also sneaks up in other ways. For example, it's a required bound for
`T` in `Vec<T>` so that `Vec<T>` as a whole can be made `FromReflect`.
It's also required by all fields of an enum as it's used as part of the
`Reflect::apply` implementation.
So in other words, much like `GetTypeRegistration` and `Typed`, it is
very much a core reflection trait.
The problem is that it is not currently treated like a core trait and is
not automatically derived alongside `Reflect`. This makes using it a bit
cumbersome and easy to forget.
## Solution
Automatically derive `FromReflect` when deriving `Reflect`.
Users can then choose to opt-out if needed using the
`#[reflect(from_reflect = false)]` attribute.
```rust
#[derive(Reflect)]
struct Foo;
#[derive(Reflect)]
#[reflect(from_reflect = false)]
struct Bar;
fn test<T: FromReflect>(value: T) {}
test(Foo); // <-- OK
test(Bar); // <-- Panic! Bar does not implement trait `FromReflect`
```
#### `ReflectFromReflect`
This PR also automatically adds the `ReflectFromReflect` (introduced in
#6245) registration to the derived `GetTypeRegistration` impl— if the
type hasn't opted out of `FromReflect` of course.
<details>
<summary><h4>Improved Deserialization</h4></summary>
> **Warning**
> This section includes changes that have since been descoped from this
PR. They will likely be implemented again in a followup PR. I am mainly
leaving these details in for archival purposes, as well as for reference
when implementing this logic again.
And since we can do all the above, we might as well improve
deserialization. We can now choose to deserialize into a Dynamic type or
automatically convert it using `FromReflect` under the hood.
`[Un]TypedReflectDeserializer::new` will now perform the conversion and
return the `Box`'d Real type.
`[Un]TypedReflectDeserializer::new_dynamic` will work like what we have
now and simply return the `Box`'d Dynamic type.
```rust
// Returns the Real type
let reflect_deserializer = UntypedReflectDeserializer::new(®istry);
let mut deserializer = ron::de::Deserializer::from_str(input)?;
let output: SomeStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?;
// Returns the Dynamic type
let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(®istry);
let mut deserializer = ron::de::Deserializer::from_str(input)?;
let output: DynamicStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?;
```
</details>
---
## Changelog
* `FromReflect` is now automatically derived within the `Reflect` derive
macro
* This includes auto-registering `ReflectFromReflect` in the derived
`GetTypeRegistration` impl
* ~~Renamed `TypedReflectDeserializer::new` and
`UntypedReflectDeserializer::new` to
`TypedReflectDeserializer::new_dynamic` and
`UntypedReflectDeserializer::new_dynamic`, respectively~~ **Descoped**
* ~~Changed `TypedReflectDeserializer::new` and
`UntypedReflectDeserializer::new` to automatically convert the
deserialized output using `FromReflect`~~ **Descoped**
## Migration Guide
* `FromReflect` is now automatically derived within the `Reflect` derive
macro. Items with both derives will need to remove the `FromReflect`
one.
```rust
// OLD
#[derive(Reflect, FromReflect)]
struct Foo;
// NEW
#[derive(Reflect)]
struct Foo;
```
If using a manual implementation of `FromReflect` and the `Reflect`
derive, users will need to opt-out of the automatic implementation.
```rust
// OLD
#[derive(Reflect)]
struct Foo;
impl FromReflect for Foo {/* ... */}
// NEW
#[derive(Reflect)]
#[reflect(from_reflect = false)]
struct Foo;
impl FromReflect for Foo {/* ... */}
```
<details>
<summary><h4>Removed Migrations</h4></summary>
> **Warning**
> This section includes changes that have since been descoped from this
PR. They will likely be implemented again in a followup PR. I am mainly
leaving these details in for archival purposes, as well as for reference
when implementing this logic again.
* The reflect deserializers now perform a `FromReflect` conversion
internally. The expected output of `TypedReflectDeserializer::new` and
`UntypedReflectDeserializer::new` is no longer a Dynamic (e.g.,
`DynamicList`), but its Real counterpart (e.g., `Vec<i32>`).
```rust
let reflect_deserializer =
UntypedReflectDeserializer::new_dynamic(®istry);
let mut deserializer = ron::de::Deserializer::from_str(input)?;
// OLD
let output: DynamicStruct = reflect_deserializer.deserialize(&mut
deserializer)?.take()?;
// NEW
let output: SomeStruct = reflect_deserializer.deserialize(&mut
deserializer)?.take()?;
```
Alternatively, if this behavior isn't desired, use the
`TypedReflectDeserializer::new_dynamic` and
`UntypedReflectDeserializer::new_dynamic` methods instead:
```rust
// OLD
let reflect_deserializer = UntypedReflectDeserializer::new(®istry);
// NEW
let reflect_deserializer =
UntypedReflectDeserializer::new_dynamic(®istry);
```
</details>
---------
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2023-06-29 01:31:34 +00:00
|
|
|
#[derive(Resource, Reflect, Debug, Clone)]
|
|
|
|
#[reflect(Resource, Default)]
|
2019-12-03 08:30:30 +00:00
|
|
|
pub struct Time {
|
2022-10-22 18:52:29 +00:00
|
|
|
startup: Instant,
|
|
|
|
first_update: Option<Instant>,
|
2020-11-28 21:08:31 +00:00
|
|
|
last_update: Option<Instant>,
|
2022-10-24 14:14:25 +00:00
|
|
|
// pausing
|
2022-10-22 18:52:29 +00:00
|
|
|
paused: bool,
|
2022-10-24 14:14:25 +00:00
|
|
|
// scaling
|
|
|
|
relative_speed: f64, // using `f64` instead of `f32` to minimize drift from rounding errors
|
2022-10-22 18:52:29 +00:00
|
|
|
delta: Duration,
|
2020-11-28 21:08:31 +00:00
|
|
|
delta_seconds: f32,
|
2022-10-22 18:52:29 +00:00
|
|
|
delta_seconds_f64: f64,
|
|
|
|
elapsed: Duration,
|
|
|
|
elapsed_seconds: f32,
|
|
|
|
elapsed_seconds_f64: f64,
|
|
|
|
raw_delta: Duration,
|
|
|
|
raw_delta_seconds: f32,
|
|
|
|
raw_delta_seconds_f64: f64,
|
|
|
|
raw_elapsed: Duration,
|
|
|
|
raw_elapsed_seconds: f32,
|
|
|
|
raw_elapsed_seconds_f64: f64,
|
|
|
|
// wrapping
|
|
|
|
wrap_period: Duration,
|
|
|
|
elapsed_wrapped: Duration,
|
|
|
|
elapsed_seconds_wrapped: f32,
|
|
|
|
elapsed_seconds_wrapped_f64: f64,
|
|
|
|
raw_elapsed_wrapped: Duration,
|
|
|
|
raw_elapsed_seconds_wrapped: f32,
|
|
|
|
raw_elapsed_seconds_wrapped_f64: f64,
|
2019-12-03 08:30:30 +00:00
|
|
|
}
|
|
|
|
|
2020-05-13 23:35:38 +00:00
|
|
|
impl Default for Time {
|
2022-10-22 18:52:29 +00:00
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
2020-05-31 04:32:47 +00:00
|
|
|
startup: Instant::now(),
|
2022-10-22 18:52:29 +00:00
|
|
|
first_update: None,
|
|
|
|
last_update: None,
|
|
|
|
paused: false,
|
2022-10-24 14:14:25 +00:00
|
|
|
relative_speed: 1.0,
|
2022-10-22 18:52:29 +00:00
|
|
|
delta: Duration::ZERO,
|
2019-12-03 08:30:30 +00:00
|
|
|
delta_seconds: 0.0,
|
2022-10-22 18:52:29 +00:00
|
|
|
delta_seconds_f64: 0.0,
|
|
|
|
elapsed: Duration::ZERO,
|
|
|
|
elapsed_seconds: 0.0,
|
|
|
|
elapsed_seconds_f64: 0.0,
|
|
|
|
raw_delta: Duration::ZERO,
|
|
|
|
raw_delta_seconds: 0.0,
|
|
|
|
raw_delta_seconds_f64: 0.0,
|
|
|
|
raw_elapsed: Duration::ZERO,
|
|
|
|
raw_elapsed_seconds: 0.0,
|
|
|
|
raw_elapsed_seconds_f64: 0.0,
|
|
|
|
wrap_period: Duration::from_secs(3600), // 1 hour
|
|
|
|
elapsed_wrapped: Duration::ZERO,
|
|
|
|
elapsed_seconds_wrapped: 0.0,
|
|
|
|
elapsed_seconds_wrapped_f64: 0.0,
|
|
|
|
raw_elapsed_wrapped: Duration::ZERO,
|
|
|
|
raw_elapsed_seconds_wrapped: 0.0,
|
|
|
|
raw_elapsed_seconds_wrapped_f64: 0.0,
|
2019-12-03 08:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-13 23:35:38 +00:00
|
|
|
}
|
2019-12-03 08:30:30 +00:00
|
|
|
|
2020-05-13 23:35:38 +00:00
|
|
|
impl Time {
|
2022-10-22 18:52:29 +00:00
|
|
|
/// Constructs a new `Time` instance with a specific startup `Instant`.
|
|
|
|
pub fn new(startup: Instant) -> Self {
|
|
|
|
Self {
|
|
|
|
startup,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-02 20:36:40 +00:00
|
|
|
/// Updates the internal time measurements.
|
2022-04-24 23:15:27 +00:00
|
|
|
///
|
2022-10-22 18:52:29 +00:00
|
|
|
/// Calling this method as part of your app will most likely result in inaccurate timekeeping,
|
|
|
|
/// as the `Time` resource is ordinarily managed by the [`TimePlugin`](crate::TimePlugin).
|
2020-05-31 04:15:39 +00:00
|
|
|
pub fn update(&mut self) {
|
2022-10-22 18:52:29 +00:00
|
|
|
let now = Instant::now();
|
|
|
|
self.update_with_instant(now);
|
2020-11-28 21:08:31 +00:00
|
|
|
}
|
|
|
|
|
2022-10-22 18:52:29 +00:00
|
|
|
/// Updates time with a specified [`Instant`].
|
2022-04-24 23:15:27 +00:00
|
|
|
///
|
2022-10-22 18:52:29 +00:00
|
|
|
/// This method is provided for use in tests. Calling this method as part of your app will most
|
|
|
|
/// likely result in inaccurate timekeeping, as the `Time` resource is ordinarily managed by the
|
|
|
|
/// [`TimePlugin`](crate::TimePlugin).
|
2022-04-24 23:15:27 +00:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
2022-05-26 00:27:18 +00:00
|
|
|
/// # use bevy_time::prelude::*;
|
2022-04-24 23:15:27 +00:00
|
|
|
/// # use bevy_ecs::prelude::*;
|
|
|
|
/// # use bevy_utils::Duration;
|
|
|
|
/// # fn main () {
|
|
|
|
/// # test_health_system();
|
|
|
|
/// # }
|
Make `Resource` trait opt-in, requiring `#[derive(Resource)]` V2 (#5577)
*This PR description is an edited copy of #5007, written by @alice-i-cecile.*
# Objective
Follow-up to https://github.com/bevyengine/bevy/pull/2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds.
While ergonomic, this results in several drawbacks:
* it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource
* it is challenging to discover if a type is intended to be used as a resource
* we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component).
* dependencies can use the same Rust type as a resource in invisibly conflicting ways
* raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values
* we cannot capture a definitive list of possible resources to display to users in an editor
## Notes to reviewers
* Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits.
*ira: My commits are not as well organized :')*
* I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does.
* I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with https://github.com/bevyengine/bevy/issues/4981.
## Changelog
`Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro.
## Migration Guide
Add `#[derive(Resource)]` to all types you are using as a resource.
If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics.
`ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing.
Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead.
Co-authored-by: Alice <alice.i.cecile@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: devil-ira <justthecooldude@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-08-08 21:36:35 +00:00
|
|
|
/// #[derive(Resource)]
|
2022-04-24 23:15:27 +00:00
|
|
|
/// struct Health {
|
|
|
|
/// // Health value between 0.0 and 1.0
|
|
|
|
/// health_value: f32,
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// fn health_system(time: Res<Time>, mut health: ResMut<Health>) {
|
|
|
|
/// // Increase health value by 0.1 per second, independent of frame rate,
|
|
|
|
/// // but not beyond 1.0
|
|
|
|
/// health.health_value = (health.health_value + 0.1 * time.delta_seconds()).min(1.0);
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// // Mock time in tests
|
|
|
|
/// fn test_health_system() {
|
|
|
|
/// let mut world = World::default();
|
|
|
|
/// let mut time = Time::default();
|
|
|
|
/// time.update();
|
|
|
|
/// world.insert_resource(time);
|
|
|
|
/// world.insert_resource(Health { health_value: 0.2 });
|
|
|
|
///
|
Migrate engine to Schedule v3 (#7267)
Huge thanks to @maniwani, @devil-ira, @hymm, @cart, @superdump and @jakobhellermann for the help with this PR.
# Objective
- Followup #6587.
- Minimal integration for the Stageless Scheduling RFC: https://github.com/bevyengine/rfcs/pull/45
## Solution
- [x] Remove old scheduling module
- [x] Migrate new methods to no longer use extension methods
- [x] Fix compiler errors
- [x] Fix benchmarks
- [x] Fix examples
- [x] Fix docs
- [x] Fix tests
## Changelog
### Added
- a large number of methods on `App` to work with schedules ergonomically
- the `CoreSchedule` enum
- `App::add_extract_system` via the `RenderingAppExtension` trait extension method
- the private `prepare_view_uniforms` system now has a public system set for scheduling purposes, called `ViewSet::PrepareUniforms`
### Removed
- stages, and all code that mentions stages
- states have been dramatically simplified, and no longer use a stack
- `RunCriteriaLabel`
- `AsSystemLabel` trait
- `on_hierarchy_reports_enabled` run criteria (now just uses an ad hoc resource checking run condition)
- systems in `RenderSet/Stage::Extract` no longer warn when they do not read data from the main world
- `RunCriteriaLabel`
- `transform_propagate_system_set`: this was a nonstandard pattern that didn't actually provide enough control. The systems are already `pub`: the docs have been updated to ensure that the third-party usage is clear.
### Changed
- `System::default_labels` is now `System::default_system_sets`.
- `App::add_default_labels` is now `App::add_default_sets`
- `CoreStage` and `StartupStage` enums are now `CoreSet` and `StartupSet`
- `App::add_system_set` was renamed to `App::add_systems`
- The `StartupSchedule` label is now defined as part of the `CoreSchedules` enum
- `.label(SystemLabel)` is now referred to as `.in_set(SystemSet)`
- `SystemLabel` trait was replaced by `SystemSet`
- `SystemTypeIdLabel<T>` was replaced by `SystemSetType<T>`
- The `ReportHierarchyIssue` resource now has a public constructor (`new`), and implements `PartialEq`
- Fixed time steps now use a schedule (`CoreSchedule::FixedTimeStep`) rather than a run criteria.
- Adding rendering extraction systems now panics rather than silently failing if no subapp with the `RenderApp` label is found.
- the `calculate_bounds` system, with the `CalculateBounds` label, is now in `CoreSet::Update`, rather than in `CoreSet::PostUpdate` before commands are applied.
- `SceneSpawnerSystem` now runs under `CoreSet::Update`, rather than `CoreStage::PreUpdate.at_end()`.
- `bevy_pbr::add_clusters` is no longer an exclusive system
- the top level `bevy_ecs::schedule` module was replaced with `bevy_ecs::scheduling`
- `tick_global_task_pools_on_main_thread` is no longer run as an exclusive system. Instead, it has been replaced by `tick_global_task_pools`, which uses a `NonSend` resource to force running on the main thread.
## Migration Guide
- Calls to `.label(MyLabel)` should be replaced with `.in_set(MySet)`
- Stages have been removed. Replace these with system sets, and then add command flushes using the `apply_system_buffers` exclusive system where needed.
- The `CoreStage`, `StartupStage, `RenderStage` and `AssetStage` enums have been replaced with `CoreSet`, `StartupSet, `RenderSet` and `AssetSet`. The same scheduling guarantees have been preserved.
- Systems are no longer added to `CoreSet::Update` by default. Add systems manually if this behavior is needed, although you should consider adding your game logic systems to `CoreSchedule::FixedTimestep` instead for more reliable framerate-independent behavior.
- Similarly, startup systems are no longer part of `StartupSet::Startup` by default. In most cases, this won't matter to you.
- For example, `add_system_to_stage(CoreStage::PostUpdate, my_system)` should be replaced with
- `add_system(my_system.in_set(CoreSet::PostUpdate)`
- When testing systems or otherwise running them in a headless fashion, simply construct and run a schedule using `Schedule::new()` and `World::run_schedule` rather than constructing stages
- Run criteria have been renamed to run conditions. These can now be combined with each other and with states.
- Looping run criteria and state stacks have been removed. Use an exclusive system that runs a schedule if you need this level of control over system control flow.
- For app-level control flow over which schedules get run when (such as for rollback networking), create your own schedule and insert it under the `CoreSchedule::Outer` label.
- Fixed timesteps are now evaluated in a schedule, rather than controlled via run criteria. The `run_fixed_timestep` system runs this schedule between `CoreSet::First` and `CoreSet::PreUpdate` by default.
- Command flush points introduced by `AssetStage` have been removed. If you were relying on these, add them back manually.
- Adding extract systems is now typically done directly on the main app. Make sure the `RenderingAppExtension` trait is in scope, then call `app.add_extract_system(my_system)`.
- the `calculate_bounds` system, with the `CalculateBounds` label, is now in `CoreSet::Update`, rather than in `CoreSet::PostUpdate` before commands are applied. You may need to order your movement systems to occur before this system in order to avoid system order ambiguities in culling behavior.
- the `RenderLabel` `AppLabel` was renamed to `RenderApp` for clarity
- `App::add_state` now takes 0 arguments: the starting state is set based on the `Default` impl.
- Instead of creating `SystemSet` containers for systems that run in stages, simply use `.on_enter::<State::Variant>()` or its `on_exit` or `on_update` siblings.
- `SystemLabel` derives should be replaced with `SystemSet`. You will also need to add the `Debug`, `PartialEq`, `Eq`, and `Hash` traits to satisfy the new trait bounds.
- `with_run_criteria` has been renamed to `run_if`. Run criteria have been renamed to run conditions for clarity, and should now simply return a bool.
- States have been dramatically simplified: there is no longer a "state stack". To queue a transition to the next state, call `NextState::set`
## TODO
- [x] remove dead methods on App and World
- [x] add `App::add_system_to_schedule` and `App::add_systems_to_schedule`
- [x] avoid adding the default system set at inappropriate times
- [x] remove any accidental cycles in the default plugins schedule
- [x] migrate benchmarks
- [x] expose explicit labels for the built-in command flush points
- [x] migrate engine code
- [x] remove all mentions of stages from the docs
- [x] verify docs for States
- [x] fix uses of exclusive systems that use .end / .at_start / .before_commands
- [x] migrate RenderStage and AssetStage
- [x] migrate examples
- [x] ensure that transform propagation is exported in a sufficiently public way (the systems are already pub)
- [x] ensure that on_enter schedules are run at least once before the main app
- [x] re-enable opt-in to execution order ambiguities
- [x] revert change to `update_bounds` to ensure it runs in `PostUpdate`
- [x] test all examples
- [x] unbreak directional lights
- [x] unbreak shadows (see 3d_scene, 3d_shape, lighting, transparaency_3d examples)
- [x] game menu example shows loading screen and menu simultaneously
- [x] display settings menu is a blank screen
- [x] `without_winit` example panics
- [x] ensure all tests pass
- [x] SubApp doc test fails
- [x] runs_spawn_local tasks fails
- [x] [Fix panic_when_hierachy_cycle test hanging](https://github.com/alice-i-cecile/bevy/pull/120)
## Points of Difficulty and Controversy
**Reviewers, please give feedback on these and look closely**
1. Default sets, from the RFC, have been removed. These added a tremendous amount of implicit complexity and result in hard to debug scheduling errors. They're going to be tackled in the form of "base sets" by @cart in a followup.
2. The outer schedule controls which schedule is run when `App::update` is called.
3. I implemented `Label for `Box<dyn Label>` for our label types. This enables us to store schedule labels in concrete form, and then later run them. I ran into the same set of problems when working with one-shot systems. We've previously investigated this pattern in depth, and it does not appear to lead to extra indirection with nested boxes.
4. `SubApp::update` simply runs the default schedule once. This sucks, but this whole API is incomplete and this was the minimal changeset.
5. `time_system` and `tick_global_task_pools_on_main_thread` no longer use exclusive systems to attempt to force scheduling order
6. Implemetnation strategy for fixed timesteps
7. `AssetStage` was migrated to `AssetSet` without reintroducing command flush points. These did not appear to be used, and it's nice to remove these bottlenecks.
8. Migration of `bevy_render/lib.rs` and pipelined rendering. The logic here is unusually tricky, as we have complex scheduling requirements.
## Future Work (ideally before 0.10)
- Rename schedule_v3 module to schedule or scheduling
- Add a derive macro to states, and likely a `EnumIter` trait of some form
- Figure out what exactly to do with the "systems added should basically work by default" problem
- Improve ergonomics for working with fixed timesteps and states
- Polish FixedTime API to match Time
- Rebase and merge #7415
- Resolve all internal ambiguities (blocked on better tools, especially #7442)
- Add "base sets" to replace the removed default sets.
2023-02-06 02:04:50 +00:00
|
|
|
/// let mut schedule = Schedule::new();
|
2023-03-18 01:45:34 +00:00
|
|
|
/// schedule.add_systems(health_system);
|
2022-04-24 23:15:27 +00:00
|
|
|
///
|
|
|
|
/// // Simulate that 30 ms have passed
|
|
|
|
/// let mut time = world.resource_mut::<Time>();
|
|
|
|
/// let last_update = time.last_update().unwrap();
|
|
|
|
/// time.update_with_instant(last_update + Duration::from_millis(30));
|
|
|
|
///
|
|
|
|
/// // Run system
|
Migrate engine to Schedule v3 (#7267)
Huge thanks to @maniwani, @devil-ira, @hymm, @cart, @superdump and @jakobhellermann for the help with this PR.
# Objective
- Followup #6587.
- Minimal integration for the Stageless Scheduling RFC: https://github.com/bevyengine/rfcs/pull/45
## Solution
- [x] Remove old scheduling module
- [x] Migrate new methods to no longer use extension methods
- [x] Fix compiler errors
- [x] Fix benchmarks
- [x] Fix examples
- [x] Fix docs
- [x] Fix tests
## Changelog
### Added
- a large number of methods on `App` to work with schedules ergonomically
- the `CoreSchedule` enum
- `App::add_extract_system` via the `RenderingAppExtension` trait extension method
- the private `prepare_view_uniforms` system now has a public system set for scheduling purposes, called `ViewSet::PrepareUniforms`
### Removed
- stages, and all code that mentions stages
- states have been dramatically simplified, and no longer use a stack
- `RunCriteriaLabel`
- `AsSystemLabel` trait
- `on_hierarchy_reports_enabled` run criteria (now just uses an ad hoc resource checking run condition)
- systems in `RenderSet/Stage::Extract` no longer warn when they do not read data from the main world
- `RunCriteriaLabel`
- `transform_propagate_system_set`: this was a nonstandard pattern that didn't actually provide enough control. The systems are already `pub`: the docs have been updated to ensure that the third-party usage is clear.
### Changed
- `System::default_labels` is now `System::default_system_sets`.
- `App::add_default_labels` is now `App::add_default_sets`
- `CoreStage` and `StartupStage` enums are now `CoreSet` and `StartupSet`
- `App::add_system_set` was renamed to `App::add_systems`
- The `StartupSchedule` label is now defined as part of the `CoreSchedules` enum
- `.label(SystemLabel)` is now referred to as `.in_set(SystemSet)`
- `SystemLabel` trait was replaced by `SystemSet`
- `SystemTypeIdLabel<T>` was replaced by `SystemSetType<T>`
- The `ReportHierarchyIssue` resource now has a public constructor (`new`), and implements `PartialEq`
- Fixed time steps now use a schedule (`CoreSchedule::FixedTimeStep`) rather than a run criteria.
- Adding rendering extraction systems now panics rather than silently failing if no subapp with the `RenderApp` label is found.
- the `calculate_bounds` system, with the `CalculateBounds` label, is now in `CoreSet::Update`, rather than in `CoreSet::PostUpdate` before commands are applied.
- `SceneSpawnerSystem` now runs under `CoreSet::Update`, rather than `CoreStage::PreUpdate.at_end()`.
- `bevy_pbr::add_clusters` is no longer an exclusive system
- the top level `bevy_ecs::schedule` module was replaced with `bevy_ecs::scheduling`
- `tick_global_task_pools_on_main_thread` is no longer run as an exclusive system. Instead, it has been replaced by `tick_global_task_pools`, which uses a `NonSend` resource to force running on the main thread.
## Migration Guide
- Calls to `.label(MyLabel)` should be replaced with `.in_set(MySet)`
- Stages have been removed. Replace these with system sets, and then add command flushes using the `apply_system_buffers` exclusive system where needed.
- The `CoreStage`, `StartupStage, `RenderStage` and `AssetStage` enums have been replaced with `CoreSet`, `StartupSet, `RenderSet` and `AssetSet`. The same scheduling guarantees have been preserved.
- Systems are no longer added to `CoreSet::Update` by default. Add systems manually if this behavior is needed, although you should consider adding your game logic systems to `CoreSchedule::FixedTimestep` instead for more reliable framerate-independent behavior.
- Similarly, startup systems are no longer part of `StartupSet::Startup` by default. In most cases, this won't matter to you.
- For example, `add_system_to_stage(CoreStage::PostUpdate, my_system)` should be replaced with
- `add_system(my_system.in_set(CoreSet::PostUpdate)`
- When testing systems or otherwise running them in a headless fashion, simply construct and run a schedule using `Schedule::new()` and `World::run_schedule` rather than constructing stages
- Run criteria have been renamed to run conditions. These can now be combined with each other and with states.
- Looping run criteria and state stacks have been removed. Use an exclusive system that runs a schedule if you need this level of control over system control flow.
- For app-level control flow over which schedules get run when (such as for rollback networking), create your own schedule and insert it under the `CoreSchedule::Outer` label.
- Fixed timesteps are now evaluated in a schedule, rather than controlled via run criteria. The `run_fixed_timestep` system runs this schedule between `CoreSet::First` and `CoreSet::PreUpdate` by default.
- Command flush points introduced by `AssetStage` have been removed. If you were relying on these, add them back manually.
- Adding extract systems is now typically done directly on the main app. Make sure the `RenderingAppExtension` trait is in scope, then call `app.add_extract_system(my_system)`.
- the `calculate_bounds` system, with the `CalculateBounds` label, is now in `CoreSet::Update`, rather than in `CoreSet::PostUpdate` before commands are applied. You may need to order your movement systems to occur before this system in order to avoid system order ambiguities in culling behavior.
- the `RenderLabel` `AppLabel` was renamed to `RenderApp` for clarity
- `App::add_state` now takes 0 arguments: the starting state is set based on the `Default` impl.
- Instead of creating `SystemSet` containers for systems that run in stages, simply use `.on_enter::<State::Variant>()` or its `on_exit` or `on_update` siblings.
- `SystemLabel` derives should be replaced with `SystemSet`. You will also need to add the `Debug`, `PartialEq`, `Eq`, and `Hash` traits to satisfy the new trait bounds.
- `with_run_criteria` has been renamed to `run_if`. Run criteria have been renamed to run conditions for clarity, and should now simply return a bool.
- States have been dramatically simplified: there is no longer a "state stack". To queue a transition to the next state, call `NextState::set`
## TODO
- [x] remove dead methods on App and World
- [x] add `App::add_system_to_schedule` and `App::add_systems_to_schedule`
- [x] avoid adding the default system set at inappropriate times
- [x] remove any accidental cycles in the default plugins schedule
- [x] migrate benchmarks
- [x] expose explicit labels for the built-in command flush points
- [x] migrate engine code
- [x] remove all mentions of stages from the docs
- [x] verify docs for States
- [x] fix uses of exclusive systems that use .end / .at_start / .before_commands
- [x] migrate RenderStage and AssetStage
- [x] migrate examples
- [x] ensure that transform propagation is exported in a sufficiently public way (the systems are already pub)
- [x] ensure that on_enter schedules are run at least once before the main app
- [x] re-enable opt-in to execution order ambiguities
- [x] revert change to `update_bounds` to ensure it runs in `PostUpdate`
- [x] test all examples
- [x] unbreak directional lights
- [x] unbreak shadows (see 3d_scene, 3d_shape, lighting, transparaency_3d examples)
- [x] game menu example shows loading screen and menu simultaneously
- [x] display settings menu is a blank screen
- [x] `without_winit` example panics
- [x] ensure all tests pass
- [x] SubApp doc test fails
- [x] runs_spawn_local tasks fails
- [x] [Fix panic_when_hierachy_cycle test hanging](https://github.com/alice-i-cecile/bevy/pull/120)
## Points of Difficulty and Controversy
**Reviewers, please give feedback on these and look closely**
1. Default sets, from the RFC, have been removed. These added a tremendous amount of implicit complexity and result in hard to debug scheduling errors. They're going to be tackled in the form of "base sets" by @cart in a followup.
2. The outer schedule controls which schedule is run when `App::update` is called.
3. I implemented `Label for `Box<dyn Label>` for our label types. This enables us to store schedule labels in concrete form, and then later run them. I ran into the same set of problems when working with one-shot systems. We've previously investigated this pattern in depth, and it does not appear to lead to extra indirection with nested boxes.
4. `SubApp::update` simply runs the default schedule once. This sucks, but this whole API is incomplete and this was the minimal changeset.
5. `time_system` and `tick_global_task_pools_on_main_thread` no longer use exclusive systems to attempt to force scheduling order
6. Implemetnation strategy for fixed timesteps
7. `AssetStage` was migrated to `AssetSet` without reintroducing command flush points. These did not appear to be used, and it's nice to remove these bottlenecks.
8. Migration of `bevy_render/lib.rs` and pipelined rendering. The logic here is unusually tricky, as we have complex scheduling requirements.
## Future Work (ideally before 0.10)
- Rename schedule_v3 module to schedule or scheduling
- Add a derive macro to states, and likely a `EnumIter` trait of some form
- Figure out what exactly to do with the "systems added should basically work by default" problem
- Improve ergonomics for working with fixed timesteps and states
- Polish FixedTime API to match Time
- Rebase and merge #7415
- Resolve all internal ambiguities (blocked on better tools, especially #7442)
- Add "base sets" to replace the removed default sets.
2023-02-06 02:04:50 +00:00
|
|
|
/// schedule.run(&mut world);
|
2022-04-24 23:15:27 +00:00
|
|
|
///
|
|
|
|
/// // Check that 0.003 has been added to the health value
|
|
|
|
/// let expected_health_value = 0.2 + 0.1 * 0.03;
|
|
|
|
/// let actual_health_value = world.resource::<Health>().health_value;
|
|
|
|
/// assert_eq!(expected_health_value, actual_health_value);
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
pub fn update_with_instant(&mut self, instant: Instant) {
|
2022-10-22 18:52:29 +00:00
|
|
|
let raw_delta = instant - self.last_update.unwrap_or(self.startup);
|
|
|
|
let delta = if self.paused {
|
|
|
|
Duration::ZERO
|
|
|
|
} else if self.relative_speed != 1.0 {
|
|
|
|
raw_delta.mul_f64(self.relative_speed)
|
|
|
|
} else {
|
2022-10-24 14:14:25 +00:00
|
|
|
// avoid rounding when at normal speed
|
2022-10-22 18:52:29 +00:00
|
|
|
raw_delta
|
|
|
|
};
|
|
|
|
|
|
|
|
if self.last_update.is_some() {
|
|
|
|
self.delta = delta;
|
2020-06-04 02:53:41 +00:00
|
|
|
self.delta_seconds = self.delta.as_secs_f32();
|
2022-10-22 18:52:29 +00:00
|
|
|
self.delta_seconds_f64 = self.delta.as_secs_f64();
|
|
|
|
self.raw_delta = raw_delta;
|
|
|
|
self.raw_delta_seconds = self.raw_delta.as_secs_f32();
|
|
|
|
self.raw_delta_seconds_f64 = self.raw_delta.as_secs_f64();
|
|
|
|
} else {
|
|
|
|
self.first_update = Some(instant);
|
2020-05-31 04:15:39 +00:00
|
|
|
}
|
2020-05-31 04:32:47 +00:00
|
|
|
|
2022-10-22 18:52:29 +00:00
|
|
|
self.elapsed += delta;
|
|
|
|
self.elapsed_seconds = self.elapsed.as_secs_f32();
|
|
|
|
self.elapsed_seconds_f64 = self.elapsed.as_secs_f64();
|
|
|
|
self.raw_elapsed += raw_delta;
|
|
|
|
self.raw_elapsed_seconds = self.raw_elapsed.as_secs_f32();
|
|
|
|
self.raw_elapsed_seconds_f64 = self.raw_elapsed.as_secs_f64();
|
|
|
|
|
|
|
|
self.elapsed_wrapped = duration_div_rem(self.elapsed, self.wrap_period).1;
|
|
|
|
self.elapsed_seconds_wrapped = self.elapsed_wrapped.as_secs_f32();
|
|
|
|
self.elapsed_seconds_wrapped_f64 = self.elapsed_wrapped.as_secs_f64();
|
|
|
|
self.raw_elapsed_wrapped = duration_div_rem(self.raw_elapsed, self.wrap_period).1;
|
|
|
|
self.raw_elapsed_seconds_wrapped = self.raw_elapsed_wrapped.as_secs_f32();
|
|
|
|
self.raw_elapsed_seconds_wrapped_f64 = self.raw_elapsed_wrapped.as_secs_f64();
|
|
|
|
|
2020-11-28 21:08:31 +00:00
|
|
|
self.last_update = Some(instant);
|
|
|
|
}
|
|
|
|
|
2022-10-24 14:14:25 +00:00
|
|
|
/// Returns the [`Instant`] the clock was created.
|
|
|
|
///
|
|
|
|
/// This usually represents when the app was started.
|
2022-10-22 18:52:29 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn startup(&self) -> Instant {
|
|
|
|
self.startup
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the [`Instant`] when [`update`](#method.update) was first called, if it exists.
|
2022-10-24 14:14:25 +00:00
|
|
|
///
|
|
|
|
/// This usually represents when the first app update started.
|
2022-10-22 18:52:29 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn first_update(&self) -> Option<Instant> {
|
|
|
|
self.first_update
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the [`Instant`] when [`update`](#method.update) was last called, if it exists.
|
2022-10-24 14:14:25 +00:00
|
|
|
///
|
|
|
|
/// This usually represents when the current app update started.
|
2022-10-22 18:52:29 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn last_update(&self) -> Option<Instant> {
|
|
|
|
self.last_update
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns how much time has advanced since the last [`update`](#method.update), as a [`Duration`].
|
2020-11-28 21:08:31 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn delta(&self) -> Duration {
|
|
|
|
self.delta
|
|
|
|
}
|
|
|
|
|
2022-10-22 18:52:29 +00:00
|
|
|
/// Returns how much time has advanced since the last [`update`](#method.update), as [`f32`] seconds.
|
2020-11-28 21:08:31 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn delta_seconds(&self) -> f32 {
|
|
|
|
self.delta_seconds
|
|
|
|
}
|
|
|
|
|
2022-10-22 18:52:29 +00:00
|
|
|
/// Returns how much time has advanced since the last [`update`](#method.update), as [`f64`] seconds.
|
2020-11-28 21:08:31 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn delta_seconds_f64(&self) -> f64 {
|
|
|
|
self.delta_seconds_f64
|
|
|
|
}
|
|
|
|
|
2022-10-22 18:52:29 +00:00
|
|
|
/// Returns how much time has advanced since [`startup`](#method.startup), as [`Duration`].
|
|
|
|
#[inline]
|
|
|
|
pub fn elapsed(&self) -> Duration {
|
|
|
|
self.elapsed
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns how much time has advanced since [`startup`](#method.startup), as [`f32`] seconds.
|
2022-09-23 20:15:57 +00:00
|
|
|
///
|
2022-10-22 18:52:29 +00:00
|
|
|
/// **Note:** This is a monotonically increasing value. It's precision will degrade over time.
|
|
|
|
/// If you need an `f32` but that precision loss is unacceptable,
|
|
|
|
/// use [`elapsed_seconds_wrapped`](#method.elapsed_seconds_wrapped).
|
|
|
|
#[inline]
|
|
|
|
pub fn elapsed_seconds(&self) -> f32 {
|
|
|
|
self.elapsed_seconds
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns how much time has advanced since [`startup`](#method.startup), as [`f64`] seconds.
|
2020-11-28 21:08:31 +00:00
|
|
|
#[inline]
|
2022-10-22 18:52:29 +00:00
|
|
|
pub fn elapsed_seconds_f64(&self) -> f64 {
|
|
|
|
self.elapsed_seconds_f64
|
2020-11-28 21:08:31 +00:00
|
|
|
}
|
|
|
|
|
2022-10-22 18:52:29 +00:00
|
|
|
/// Returns how much time has advanced since [`startup`](#method.startup) modulo
|
|
|
|
/// the [`wrap_period`](#method.wrap_period), as [`Duration`].
|
|
|
|
#[inline]
|
|
|
|
pub fn elapsed_wrapped(&self) -> Duration {
|
|
|
|
self.elapsed_wrapped
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns how much time has advanced since [`startup`](#method.startup) modulo
|
|
|
|
/// the [`wrap_period`](#method.wrap_period), as [`f32`] seconds.
|
2022-09-23 20:15:57 +00:00
|
|
|
///
|
2022-10-22 18:52:29 +00:00
|
|
|
/// This method is intended for applications (e.g. shaders) that require an [`f32`] value but
|
|
|
|
/// suffer from the gradual precision loss of [`elapsed_seconds`](#method.elapsed_seconds).
|
|
|
|
#[inline]
|
|
|
|
pub fn elapsed_seconds_wrapped(&self) -> f32 {
|
|
|
|
self.elapsed_seconds_wrapped
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns how much time has advanced since [`startup`](#method.startup) modulo
|
|
|
|
/// the [`wrap_period`](#method.wrap_period), as [`f64`] seconds.
|
|
|
|
#[inline]
|
|
|
|
pub fn elapsed_seconds_wrapped_f64(&self) -> f64 {
|
|
|
|
self.elapsed_seconds_wrapped_f64
|
|
|
|
}
|
|
|
|
|
2022-10-24 14:14:25 +00:00
|
|
|
/// Returns how much real time has elapsed since the last [`update`](#method.update), as a [`Duration`].
|
2022-10-22 18:52:29 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn raw_delta(&self) -> Duration {
|
|
|
|
self.raw_delta
|
|
|
|
}
|
|
|
|
|
2022-10-24 14:14:25 +00:00
|
|
|
/// Returns how much real time has elapsed since the last [`update`](#method.update), as [`f32`] seconds.
|
2022-10-22 18:52:29 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn raw_delta_seconds(&self) -> f32 {
|
|
|
|
self.raw_delta_seconds
|
|
|
|
}
|
|
|
|
|
2022-10-24 14:14:25 +00:00
|
|
|
/// Returns how much real time has elapsed since the last [`update`](#method.update), as [`f64`] seconds.
|
2022-10-22 18:52:29 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn raw_delta_seconds_f64(&self) -> f64 {
|
|
|
|
self.raw_delta_seconds_f64
|
|
|
|
}
|
|
|
|
|
2022-10-24 14:14:25 +00:00
|
|
|
/// Returns how much real time has elapsed since [`startup`](#method.startup), as [`Duration`].
|
2022-10-22 18:52:29 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn raw_elapsed(&self) -> Duration {
|
|
|
|
self.raw_elapsed
|
|
|
|
}
|
|
|
|
|
2022-10-24 14:14:25 +00:00
|
|
|
/// Returns how much real time has elapsed since [`startup`](#method.startup), as [`f32`] seconds.
|
2022-09-23 20:15:57 +00:00
|
|
|
///
|
2022-10-22 18:52:29 +00:00
|
|
|
/// **Note:** This is a monotonically increasing value. It's precision will degrade over time.
|
|
|
|
/// If you need an `f32` but that precision loss is unacceptable,
|
|
|
|
/// use [`raw_elapsed_seconds_wrapped`](#method.raw_elapsed_seconds_wrapped).
|
2022-09-23 20:15:57 +00:00
|
|
|
#[inline]
|
2022-10-22 18:52:29 +00:00
|
|
|
pub fn raw_elapsed_seconds(&self) -> f32 {
|
|
|
|
self.raw_elapsed_seconds
|
2022-09-23 20:15:57 +00:00
|
|
|
}
|
|
|
|
|
2022-10-24 14:14:25 +00:00
|
|
|
/// Returns how much real time has elapsed since [`startup`](#method.startup), as [`f64`] seconds.
|
2020-11-28 21:08:31 +00:00
|
|
|
#[inline]
|
2022-10-22 18:52:29 +00:00
|
|
|
pub fn raw_elapsed_seconds_f64(&self) -> f64 {
|
|
|
|
self.raw_elapsed_seconds_f64
|
2020-11-28 21:08:31 +00:00
|
|
|
}
|
|
|
|
|
2022-10-24 14:14:25 +00:00
|
|
|
/// Returns how much real time has elapsed since [`startup`](#method.startup) modulo
|
2022-10-22 18:52:29 +00:00
|
|
|
/// the [`wrap_period`](#method.wrap_period), as [`Duration`].
|
2020-11-28 21:08:31 +00:00
|
|
|
#[inline]
|
2022-10-22 18:52:29 +00:00
|
|
|
pub fn raw_elapsed_wrapped(&self) -> Duration {
|
|
|
|
self.raw_elapsed_wrapped
|
|
|
|
}
|
|
|
|
|
2022-10-24 14:14:25 +00:00
|
|
|
/// Returns how much real time has elapsed since [`startup`](#method.startup) modulo
|
2022-10-22 18:52:29 +00:00
|
|
|
/// the [`wrap_period`](#method.wrap_period), as [`f32`] seconds.
|
|
|
|
///
|
|
|
|
/// This method is intended for applications (e.g. shaders) that require an [`f32`] value but
|
|
|
|
/// suffer from the gradual precision loss of [`raw_elapsed_seconds`](#method.raw_elapsed_seconds).
|
|
|
|
#[inline]
|
|
|
|
pub fn raw_elapsed_seconds_wrapped(&self) -> f32 {
|
|
|
|
self.raw_elapsed_seconds_wrapped
|
|
|
|
}
|
|
|
|
|
2022-10-24 14:14:25 +00:00
|
|
|
/// Returns how much real time has elapsed since [`startup`](#method.startup) modulo
|
2022-10-22 18:52:29 +00:00
|
|
|
/// the [`wrap_period`](#method.wrap_period), as [`f64`] seconds.
|
|
|
|
#[inline]
|
|
|
|
pub fn raw_elapsed_seconds_wrapped_f64(&self) -> f64 {
|
|
|
|
self.raw_elapsed_seconds_wrapped_f64
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the modulus used to calculate [`elapsed_wrapped`](#method.elapsed_wrapped) and
|
|
|
|
/// [`raw_elapsed_wrapped`](#method.raw_elapsed_wrapped).
|
|
|
|
///
|
|
|
|
/// **Note:** The default modulus is one hour.
|
|
|
|
#[inline]
|
|
|
|
pub fn wrap_period(&self) -> Duration {
|
|
|
|
self.wrap_period
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the modulus used to calculate [`elapsed_wrapped`](#method.elapsed_wrapped) and
|
|
|
|
/// [`raw_elapsed_wrapped`](#method.raw_elapsed_wrapped).
|
|
|
|
///
|
2022-10-24 14:14:25 +00:00
|
|
|
/// **Note:** This will not take effect until the next update.
|
|
|
|
///
|
2022-10-22 18:52:29 +00:00
|
|
|
/// # Panics
|
|
|
|
///
|
2022-10-24 14:14:25 +00:00
|
|
|
/// Panics if `wrap_period` is a zero-length duration.
|
2022-10-22 18:52:29 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn set_wrap_period(&mut self, wrap_period: Duration) {
|
2022-10-24 14:14:25 +00:00
|
|
|
assert!(!wrap_period.is_zero(), "division by zero");
|
2022-10-22 18:52:29 +00:00
|
|
|
self.wrap_period = wrap_period;
|
|
|
|
}
|
|
|
|
|
2022-10-24 14:14:25 +00:00
|
|
|
/// Returns the speed the clock advances relative to your system clock, as [`f32`].
|
|
|
|
/// This is known as "time scaling" or "time dilation" in other engines.
|
2022-10-22 18:52:29 +00:00
|
|
|
///
|
|
|
|
/// **Note:** This function will return zero when time is paused.
|
|
|
|
#[inline]
|
|
|
|
pub fn relative_speed(&self) -> f32 {
|
|
|
|
self.relative_speed_f64() as f32
|
|
|
|
}
|
|
|
|
|
2022-10-24 14:14:25 +00:00
|
|
|
/// Returns the speed the clock advances relative to your system clock, as [`f64`].
|
|
|
|
/// This is known as "time scaling" or "time dilation" in other engines.
|
2022-10-22 18:52:29 +00:00
|
|
|
///
|
|
|
|
/// **Note:** This function will return zero when time is paused.
|
|
|
|
#[inline]
|
|
|
|
pub fn relative_speed_f64(&self) -> f64 {
|
|
|
|
if self.paused {
|
|
|
|
0.0
|
|
|
|
} else {
|
|
|
|
self.relative_speed
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-24 14:14:25 +00:00
|
|
|
/// Sets the speed the clock advances relative to your system clock, given as an [`f32`].
|
|
|
|
///
|
|
|
|
/// For example, setting this to `2.0` will make the clock advance twice as fast as your system clock.
|
2022-10-22 18:52:29 +00:00
|
|
|
///
|
2022-10-24 14:14:25 +00:00
|
|
|
/// **Note:** This does not affect the `raw_*` measurements.
|
2022-10-22 18:52:29 +00:00
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Panics if `ratio` is negative or not finite.
|
|
|
|
#[inline]
|
|
|
|
pub fn set_relative_speed(&mut self, ratio: f32) {
|
|
|
|
self.set_relative_speed_f64(ratio as f64);
|
2019-12-03 08:30:30 +00:00
|
|
|
}
|
2020-05-31 04:32:47 +00:00
|
|
|
|
2022-10-24 14:14:25 +00:00
|
|
|
/// Sets the speed the clock advances relative to your system clock, given as an [`f64`].
|
2022-10-22 18:52:29 +00:00
|
|
|
///
|
2022-10-24 14:14:25 +00:00
|
|
|
/// For example, setting this to `2.0` will make the clock advance twice as fast as your system clock.
|
|
|
|
///
|
|
|
|
/// **Note:** This does not affect the `raw_*` measurements.
|
2022-10-22 18:52:29 +00:00
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Panics if `ratio` is negative or not finite.
|
|
|
|
#[inline]
|
|
|
|
pub fn set_relative_speed_f64(&mut self, ratio: f64) {
|
|
|
|
assert!(ratio.is_finite(), "tried to go infinitely fast");
|
2023-02-18 22:43:08 +00:00
|
|
|
assert!(ratio >= 0.0, "tried to go back in time");
|
2022-10-22 18:52:29 +00:00
|
|
|
self.relative_speed = ratio;
|
|
|
|
}
|
|
|
|
|
2022-10-24 14:14:25 +00:00
|
|
|
/// Stops the clock, preventing it from advancing until resumed.
|
|
|
|
///
|
2023-02-24 12:55:43 +00:00
|
|
|
/// **Note:** This does not affect the `raw_*` measurements.
|
2022-10-22 18:52:29 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn pause(&mut self) {
|
|
|
|
self.paused = true;
|
|
|
|
}
|
|
|
|
|
2022-10-24 14:14:25 +00:00
|
|
|
/// Resumes the clock if paused.
|
2021-12-07 01:30:08 +00:00
|
|
|
#[inline]
|
2022-10-22 18:52:29 +00:00
|
|
|
pub fn unpause(&mut self) {
|
|
|
|
self.paused = false;
|
2020-05-31 04:32:47 +00:00
|
|
|
}
|
2022-10-22 18:52:29 +00:00
|
|
|
|
2022-10-24 14:14:25 +00:00
|
|
|
/// Returns `true` if the clock is currently paused.
|
2022-10-22 18:52:29 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn is_paused(&self) -> bool {
|
|
|
|
self.paused
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn duration_div_rem(dividend: Duration, divisor: Duration) -> (u32, Duration) {
|
|
|
|
// `Duration` does not have a built-in modulo operation
|
|
|
|
let quotient = (dividend.as_nanos() / divisor.as_nanos()) as u32;
|
|
|
|
let remainder = dividend - (quotient * divisor);
|
|
|
|
(quotient, remainder)
|
2020-01-11 10:11:27 +00:00
|
|
|
}
|
2020-04-06 03:19:02 +00:00
|
|
|
|
2020-11-28 21:08:31 +00:00
|
|
|
#[cfg(test)]
|
2021-02-22 08:42:19 +00:00
|
|
|
#[allow(clippy::float_cmp)]
|
2020-11-28 21:08:31 +00:00
|
|
|
mod tests {
|
|
|
|
use super::Time;
|
|
|
|
use bevy_utils::{Duration, Instant};
|
|
|
|
|
2022-10-22 18:52:29 +00:00
|
|
|
fn assert_float_eq(a: f32, b: f32) {
|
|
|
|
assert!((a - b).abs() <= f32::EPSILON, "{a} != {b}");
|
|
|
|
}
|
|
|
|
|
2020-11-28 21:08:31 +00:00
|
|
|
#[test]
|
|
|
|
fn update_test() {
|
|
|
|
let start_instant = Instant::now();
|
2022-10-22 18:52:29 +00:00
|
|
|
let mut time = Time::new(start_instant);
|
2020-11-28 21:08:31 +00:00
|
|
|
|
2022-10-22 18:52:29 +00:00
|
|
|
// Ensure `time` was constructed correctly.
|
2020-11-28 21:08:31 +00:00
|
|
|
assert_eq!(time.startup(), start_instant);
|
2022-10-22 18:52:29 +00:00
|
|
|
assert_eq!(time.first_update(), None);
|
|
|
|
assert_eq!(time.last_update(), None);
|
|
|
|
assert_eq!(time.relative_speed(), 1.0);
|
|
|
|
assert_eq!(time.delta(), Duration::ZERO);
|
2020-11-28 21:08:31 +00:00
|
|
|
assert_eq!(time.delta_seconds(), 0.0);
|
2022-10-22 18:52:29 +00:00
|
|
|
assert_eq!(time.delta_seconds_f64(), 0.0);
|
|
|
|
assert_eq!(time.raw_delta(), Duration::ZERO);
|
|
|
|
assert_eq!(time.raw_delta_seconds(), 0.0);
|
|
|
|
assert_eq!(time.raw_delta_seconds_f64(), 0.0);
|
|
|
|
assert_eq!(time.elapsed(), Duration::ZERO);
|
|
|
|
assert_eq!(time.elapsed_seconds(), 0.0);
|
|
|
|
assert_eq!(time.elapsed_seconds_f64(), 0.0);
|
|
|
|
assert_eq!(time.raw_elapsed(), Duration::ZERO);
|
|
|
|
assert_eq!(time.raw_elapsed_seconds(), 0.0);
|
|
|
|
assert_eq!(time.raw_elapsed_seconds_f64(), 0.0);
|
|
|
|
|
|
|
|
// Update `time` and check results.
|
|
|
|
// The first update to `time` normally happens before other systems have run,
|
|
|
|
// so the first delta doesn't appear until the second update.
|
2020-11-28 21:08:31 +00:00
|
|
|
let first_update_instant = Instant::now();
|
|
|
|
time.update_with_instant(first_update_instant);
|
|
|
|
|
|
|
|
assert_eq!(time.startup(), start_instant);
|
2022-10-22 18:52:29 +00:00
|
|
|
assert_eq!(time.first_update(), Some(first_update_instant));
|
|
|
|
assert_eq!(time.last_update(), Some(first_update_instant));
|
|
|
|
assert_eq!(time.relative_speed(), 1.0);
|
|
|
|
assert_eq!(time.delta(), Duration::ZERO);
|
|
|
|
assert_eq!(time.delta_seconds(), 0.0);
|
2020-11-28 21:08:31 +00:00
|
|
|
assert_eq!(time.delta_seconds_f64(), 0.0);
|
2022-10-22 18:52:29 +00:00
|
|
|
assert_eq!(time.raw_delta(), Duration::ZERO);
|
|
|
|
assert_eq!(time.raw_delta_seconds(), 0.0);
|
|
|
|
assert_eq!(time.raw_delta_seconds_f64(), 0.0);
|
|
|
|
assert_eq!(time.elapsed(), first_update_instant - start_instant,);
|
2020-11-28 21:08:31 +00:00
|
|
|
assert_eq!(
|
2022-10-22 18:52:29 +00:00
|
|
|
time.elapsed_seconds(),
|
|
|
|
(first_update_instant - start_instant).as_secs_f32(),
|
2020-11-28 21:08:31 +00:00
|
|
|
);
|
2021-12-07 01:30:08 +00:00
|
|
|
assert_eq!(
|
2022-10-22 18:52:29 +00:00
|
|
|
time.elapsed_seconds_f64(),
|
|
|
|
(first_update_instant - start_instant).as_secs_f64(),
|
2021-12-07 01:30:08 +00:00
|
|
|
);
|
2022-10-22 18:52:29 +00:00
|
|
|
assert_eq!(time.raw_elapsed(), first_update_instant - start_instant,);
|
|
|
|
assert_eq!(
|
|
|
|
time.raw_elapsed_seconds(),
|
|
|
|
(first_update_instant - start_instant).as_secs_f32(),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
time.raw_elapsed_seconds_f64(),
|
|
|
|
(first_update_instant - start_instant).as_secs_f64(),
|
2022-09-23 20:15:57 +00:00
|
|
|
);
|
2020-11-28 21:08:31 +00:00
|
|
|
|
2022-10-22 18:52:29 +00:00
|
|
|
// Update `time` again and check results.
|
|
|
|
// At this point its safe to use time.delta().
|
2020-11-28 21:08:31 +00:00
|
|
|
let second_update_instant = Instant::now();
|
|
|
|
time.update_with_instant(second_update_instant);
|
|
|
|
assert_eq!(time.startup(), start_instant);
|
2022-10-22 18:52:29 +00:00
|
|
|
assert_eq!(time.first_update(), Some(first_update_instant));
|
|
|
|
assert_eq!(time.last_update(), Some(second_update_instant));
|
|
|
|
assert_eq!(time.relative_speed(), 1.0);
|
|
|
|
assert_eq!(time.delta(), second_update_instant - first_update_instant);
|
2020-11-28 21:08:31 +00:00
|
|
|
assert_eq!(
|
2022-10-22 18:52:29 +00:00
|
|
|
time.delta_seconds(),
|
|
|
|
(second_update_instant - first_update_instant).as_secs_f32(),
|
2020-11-28 21:08:31 +00:00
|
|
|
);
|
2021-12-07 01:30:08 +00:00
|
|
|
assert_eq!(
|
2022-10-22 18:52:29 +00:00
|
|
|
time.delta_seconds_f64(),
|
|
|
|
(second_update_instant - first_update_instant).as_secs_f64(),
|
2021-12-07 01:30:08 +00:00
|
|
|
);
|
2022-10-22 18:52:29 +00:00
|
|
|
assert_eq!(
|
|
|
|
time.raw_delta(),
|
|
|
|
second_update_instant - first_update_instant,
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
time.raw_delta_seconds(),
|
|
|
|
(second_update_instant - first_update_instant).as_secs_f32(),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
time.raw_delta_seconds_f64(),
|
|
|
|
(second_update_instant - first_update_instant).as_secs_f64(),
|
|
|
|
);
|
|
|
|
assert_eq!(time.elapsed(), second_update_instant - start_instant,);
|
|
|
|
assert_eq!(
|
|
|
|
time.elapsed_seconds(),
|
|
|
|
(second_update_instant - start_instant).as_secs_f32(),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
time.elapsed_seconds_f64(),
|
|
|
|
(second_update_instant - start_instant).as_secs_f64(),
|
|
|
|
);
|
|
|
|
assert_eq!(time.raw_elapsed(), second_update_instant - start_instant,);
|
|
|
|
assert_eq!(
|
|
|
|
time.raw_elapsed_seconds(),
|
|
|
|
(second_update_instant - start_instant).as_secs_f32(),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
time.raw_elapsed_seconds_f64(),
|
|
|
|
(second_update_instant - start_instant).as_secs_f64(),
|
2022-09-23 20:15:57 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-10-22 18:52:29 +00:00
|
|
|
fn wrapping_test() {
|
2022-09-23 20:15:57 +00:00
|
|
|
let start_instant = Instant::now();
|
|
|
|
|
|
|
|
let mut time = Time {
|
|
|
|
startup: start_instant,
|
|
|
|
wrap_period: Duration::from_secs(3),
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
|
2022-10-22 18:52:29 +00:00
|
|
|
assert_eq!(time.elapsed_seconds_wrapped(), 0.0);
|
2022-09-23 20:15:57 +00:00
|
|
|
|
|
|
|
time.update_with_instant(start_instant + Duration::from_secs(1));
|
2022-10-22 18:52:29 +00:00
|
|
|
assert_float_eq(time.elapsed_seconds_wrapped(), 1.0);
|
2022-09-23 20:15:57 +00:00
|
|
|
|
|
|
|
time.update_with_instant(start_instant + Duration::from_secs(2));
|
2022-10-22 18:52:29 +00:00
|
|
|
assert_float_eq(time.elapsed_seconds_wrapped(), 2.0);
|
2022-09-23 20:15:57 +00:00
|
|
|
|
|
|
|
time.update_with_instant(start_instant + Duration::from_secs(3));
|
2022-10-22 18:52:29 +00:00
|
|
|
assert_float_eq(time.elapsed_seconds_wrapped(), 0.0);
|
2022-09-23 20:15:57 +00:00
|
|
|
|
|
|
|
time.update_with_instant(start_instant + Duration::from_secs(4));
|
2022-10-22 18:52:29 +00:00
|
|
|
assert_float_eq(time.elapsed_seconds_wrapped(), 1.0);
|
2022-09-23 20:15:57 +00:00
|
|
|
}
|
|
|
|
|
2022-10-22 18:52:29 +00:00
|
|
|
#[test]
|
|
|
|
fn relative_speed_test() {
|
|
|
|
let start_instant = Instant::now();
|
|
|
|
let mut time = Time::new(start_instant);
|
|
|
|
|
|
|
|
let first_update_instant = Instant::now();
|
|
|
|
time.update_with_instant(first_update_instant);
|
|
|
|
|
|
|
|
// Update `time` again and check results.
|
|
|
|
// At this point its safe to use time.delta().
|
|
|
|
let second_update_instant = Instant::now();
|
|
|
|
time.update_with_instant(second_update_instant);
|
|
|
|
assert_eq!(time.startup(), start_instant);
|
|
|
|
assert_eq!(time.first_update(), Some(first_update_instant));
|
|
|
|
assert_eq!(time.last_update(), Some(second_update_instant));
|
|
|
|
assert_eq!(time.relative_speed(), 1.0);
|
|
|
|
assert_eq!(time.delta(), second_update_instant - first_update_instant);
|
|
|
|
assert_eq!(
|
|
|
|
time.delta_seconds(),
|
|
|
|
(second_update_instant - first_update_instant).as_secs_f32(),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
time.delta_seconds_f64(),
|
|
|
|
(second_update_instant - first_update_instant).as_secs_f64(),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
time.raw_delta(),
|
|
|
|
second_update_instant - first_update_instant,
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
time.raw_delta_seconds(),
|
|
|
|
(second_update_instant - first_update_instant).as_secs_f32(),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
time.raw_delta_seconds_f64(),
|
|
|
|
(second_update_instant - first_update_instant).as_secs_f64(),
|
|
|
|
);
|
|
|
|
assert_eq!(time.elapsed(), second_update_instant - start_instant,);
|
|
|
|
assert_eq!(
|
|
|
|
time.elapsed_seconds(),
|
|
|
|
(second_update_instant - start_instant).as_secs_f32(),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
time.elapsed_seconds_f64(),
|
|
|
|
(second_update_instant - start_instant).as_secs_f64(),
|
|
|
|
);
|
|
|
|
assert_eq!(time.raw_elapsed(), second_update_instant - start_instant,);
|
|
|
|
assert_eq!(
|
|
|
|
time.raw_elapsed_seconds(),
|
|
|
|
(second_update_instant - start_instant).as_secs_f32(),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
time.raw_elapsed_seconds_f64(),
|
|
|
|
(second_update_instant - start_instant).as_secs_f64(),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Make app time advance at 2x the rate of your system clock.
|
|
|
|
time.set_relative_speed(2.0);
|
|
|
|
|
|
|
|
// Update `time` again 1 second later.
|
|
|
|
let elapsed = Duration::from_secs(1);
|
|
|
|
let third_update_instant = second_update_instant + elapsed;
|
|
|
|
time.update_with_instant(third_update_instant);
|
|
|
|
|
|
|
|
// Since app is advancing 2x your system clock, expect time
|
|
|
|
// to have advanced by twice the amount of real time elapsed.
|
|
|
|
assert_eq!(time.startup(), start_instant);
|
|
|
|
assert_eq!(time.first_update(), Some(first_update_instant));
|
|
|
|
assert_eq!(time.last_update(), Some(third_update_instant));
|
|
|
|
assert_eq!(time.relative_speed(), 2.0);
|
|
|
|
assert_eq!(time.delta(), elapsed.mul_f32(2.0));
|
|
|
|
assert_eq!(time.delta_seconds(), elapsed.mul_f32(2.0).as_secs_f32());
|
|
|
|
assert_eq!(time.delta_seconds_f64(), elapsed.mul_f32(2.0).as_secs_f64());
|
|
|
|
assert_eq!(time.raw_delta(), elapsed);
|
|
|
|
assert_eq!(time.raw_delta_seconds(), elapsed.as_secs_f32());
|
|
|
|
assert_eq!(time.raw_delta_seconds_f64(), elapsed.as_secs_f64());
|
|
|
|
assert_eq!(
|
|
|
|
time.elapsed(),
|
|
|
|
second_update_instant - start_instant + elapsed.mul_f32(2.0),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
time.elapsed_seconds(),
|
|
|
|
(second_update_instant - start_instant + elapsed.mul_f32(2.0)).as_secs_f32(),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
time.elapsed_seconds_f64(),
|
|
|
|
(second_update_instant - start_instant + elapsed.mul_f32(2.0)).as_secs_f64(),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
time.raw_elapsed(),
|
|
|
|
second_update_instant - start_instant + elapsed,
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
time.raw_elapsed_seconds(),
|
|
|
|
(second_update_instant - start_instant + elapsed).as_secs_f32(),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
time.raw_elapsed_seconds_f64(),
|
|
|
|
(second_update_instant - start_instant + elapsed).as_secs_f64(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn pause_test() {
|
|
|
|
let start_instant = Instant::now();
|
|
|
|
let mut time = Time::new(start_instant);
|
|
|
|
|
|
|
|
let first_update_instant = Instant::now();
|
|
|
|
time.update_with_instant(first_update_instant);
|
|
|
|
|
|
|
|
assert!(!time.is_paused());
|
|
|
|
assert_eq!(time.relative_speed(), 1.0);
|
|
|
|
|
|
|
|
time.pause();
|
|
|
|
|
|
|
|
assert!(time.is_paused());
|
|
|
|
assert_eq!(time.relative_speed(), 0.0);
|
|
|
|
|
|
|
|
let second_update_instant = Instant::now();
|
|
|
|
time.update_with_instant(second_update_instant);
|
|
|
|
assert_eq!(time.startup(), start_instant);
|
|
|
|
assert_eq!(time.first_update(), Some(first_update_instant));
|
|
|
|
assert_eq!(time.last_update(), Some(second_update_instant));
|
|
|
|
assert_eq!(time.delta(), Duration::ZERO);
|
|
|
|
assert_eq!(
|
|
|
|
time.raw_delta(),
|
|
|
|
second_update_instant - first_update_instant,
|
|
|
|
);
|
|
|
|
assert_eq!(time.elapsed(), first_update_instant - start_instant);
|
|
|
|
assert_eq!(time.raw_elapsed(), second_update_instant - start_instant);
|
|
|
|
|
|
|
|
time.unpause();
|
|
|
|
|
|
|
|
assert!(!time.is_paused());
|
|
|
|
assert_eq!(time.relative_speed(), 1.0);
|
|
|
|
|
|
|
|
let third_update_instant = Instant::now();
|
|
|
|
time.update_with_instant(third_update_instant);
|
|
|
|
assert_eq!(time.startup(), start_instant);
|
|
|
|
assert_eq!(time.first_update(), Some(first_update_instant));
|
|
|
|
assert_eq!(time.last_update(), Some(third_update_instant));
|
|
|
|
assert_eq!(time.delta(), third_update_instant - second_update_instant);
|
|
|
|
assert_eq!(
|
|
|
|
time.raw_delta(),
|
|
|
|
third_update_instant - second_update_instant,
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
time.elapsed(),
|
|
|
|
(third_update_instant - second_update_instant) + (first_update_instant - start_instant),
|
|
|
|
);
|
|
|
|
assert_eq!(time.raw_elapsed(), third_update_instant - start_instant);
|
2020-11-28 21:08:31 +00:00
|
|
|
}
|
|
|
|
}
|