bevy/crates/bevy_time/src/lib.rs
Lena Milizé 73605f43b6 Replace the bool argument of Timer with TimerMode (#6247)
As mentioned in #2926, it's better to have an explicit type that clearly communicates the intent of the timer mode rather than an opaque boolean, which can be only understood when knowing the signature or having to look up the documentation.

This also opens up a way to merge different timers, such as `Stopwatch`, and possibly future ones, such as `DiscreteStopwatch` and `DiscreteTimer` from #2683, into one struct.

Signed-off-by: Lena Milizé <me@lvmn.org>

# Objective

Fixes #2926.

## Solution

Introduce `TimerMode` which replaces the `bool` argument of `Timer` constructors. A `Default` value for `TimerMode` is `Once`.

---

## Changelog

### Added

- `TimerMode` enum, along with variants `TimerMode::Once` and `TimerMode::Repeating`

### Changed

- Replace `bool` argument of `Timer::new` and `Timer::from_seconds` with `TimerMode`
- Change `repeating: bool` field of `Timer` with `mode: TimerMode`

## Migration Guide

- Replace `Timer::new(duration, false)` with `Timer::new(duration, TimerMode::Once)`.
- Replace `Timer::new(duration, true)` with `Timer::new(duration, TimerMode::Repeating)`.
- Replace `Timer::from_seconds(seconds, false)` with `Timer::from_seconds(seconds, TimerMode::Once)`.
- Replace `Timer::from_seconds(seconds, true)` with `Timer::from_seconds(seconds, TimerMode::Repeating)`.
- Change `timer.repeating()` to `timer.mode() == TimerMode::Repeating`.
2022-10-17 13:47:01 +00:00

81 lines
2.6 KiB
Rust

mod fixed_timestep;
mod stopwatch;
#[allow(clippy::module_inception)]
mod time;
mod timer;
pub use fixed_timestep::*;
pub use stopwatch::*;
pub use time::*;
pub use timer::*;
use bevy_ecs::system::{Local, Res, ResMut};
use bevy_utils::{tracing::warn, Instant};
use crossbeam_channel::{Receiver, Sender};
pub mod prelude {
//! The Bevy Time Prelude.
#[doc(hidden)]
pub use crate::{Time, Timer, TimerMode};
}
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
/// Adds time functionality to Apps.
#[derive(Default)]
pub struct TimePlugin;
#[derive(Debug, PartialEq, Eq, Clone, Hash, SystemLabel)]
/// Updates the elapsed time. Any system that interacts with [Time] component should run after
/// this.
pub struct TimeSystem;
impl Plugin for TimePlugin {
fn build(&self, app: &mut App) {
app.init_resource::<Time>()
.init_resource::<FixedTimesteps>()
.register_type::<Timer>()
.register_type::<Time>()
.register_type::<Stopwatch>()
// time system is added as an "exclusive system" to ensure it runs before other systems
// in CoreStage::First
.add_system_to_stage(CoreStage::First, time_system.at_start().label(TimeSystem));
}
}
/// Channel resource used to receive time from render world
#[derive(Resource)]
pub struct TimeReceiver(pub Receiver<Instant>);
/// Channel resource used to send time from render world
#[derive(Resource)]
pub struct TimeSender(pub Sender<Instant>);
/// Creates channels used for sending time between render world and app world
pub fn create_time_channels() -> (TimeSender, TimeReceiver) {
// bound the channel to 2 since when pipelined the render phase can finish before
// the time system runs.
let (s, r) = crossbeam_channel::bounded::<Instant>(2);
(TimeSender(s), TimeReceiver(r))
}
/// The system used to update the [`Time`] used by app logic. If there is a render world the time is sent from
/// there to this system through channels. Otherwise the time is updated in this system.
fn time_system(
mut time: ResMut<Time>,
time_recv: Option<Res<TimeReceiver>>,
mut has_received_time: Local<bool>,
) {
if let Some(time_recv) = time_recv {
// TODO: Figure out how to handle this when using pipelined rendering.
if let Ok(new_time) = time_recv.0.try_recv() {
time.update_with_instant(new_time);
*has_received_time = true;
} else if *has_received_time {
warn!("time_system did not receive the time from the render world! Calculations depending on the time may be incorrect.");
}
} else {
time.update();
}
}