bevy/crates/bevy_time/src/stopwatch.rs
Gino Valente 9b32e09551
bevy_reflect: Add clone registrations project-wide (#18307)
# Objective

Now that #13432 has been merged, it's important we update our reflected
types to properly opt into this feature. If we do not, then this could
cause issues for users downstream who want to make use of
reflection-based cloning.

## Solution

This PR is broken into 4 commits:

1. Add `#[reflect(Clone)]` on all types marked `#[reflect(opaque)]` that
are also `Clone`. This is mandatory as these types would otherwise cause
the cloning operation to fail for any type that contains it at any
depth.
2. Update the reflection example to suggest adding `#[reflect(Clone)]`
on opaque types.
3. Add `#[reflect(clone)]` attributes on all fields marked
`#[reflect(ignore)]` that are also `Clone`. This prevents the ignored
field from causing the cloning operation to fail.
   
Note that some of the types that contain these fields are also `Clone`,
and thus can be marked `#[reflect(Clone)]`. This makes the
`#[reflect(clone)]` attribute redundant. However, I think it's safer to
keep it marked in the case that the `Clone` impl/derive is ever removed.
I'm open to removing them, though, if people disagree.
4. Finally, I added `#[reflect(Clone)]` on all types that are also
`Clone`. While not strictly necessary, it enables us to reduce the
generated output since we can just call `Clone::clone` directly instead
of calling `PartialReflect::reflect_clone` on each variant/field. It
also means we benefit from any optimizations or customizations made in
the `Clone` impl, including directly dereferencing `Copy` values and
increasing reference counters.

Along with that change I also took the liberty of adding any missing
registrations that I saw could be applied to the type as well, such as
`Default`, `PartialEq`, and `Hash`. There were hundreds of these to
edit, though, so it's possible I missed quite a few.

That last commit is **_massive_**. There were nearly 700 types to
update. So it's recommended to review the first three before moving onto
that last one.

Additionally, I can break the last commit off into its own PR or into
smaller PRs, but I figured this would be the easiest way of doing it
(and in a timely manner since I unfortunately don't have as much time as
I used to for code contributions).

## Testing

You can test locally with a `cargo check`:

```
cargo check --workspace --all-features
```
2025-03-17 18:32:35 +00:00

211 lines
6.2 KiB
Rust

#[cfg(feature = "bevy_reflect")]
use bevy_reflect::{prelude::*, Reflect};
use core::time::Duration;
/// A Stopwatch is a struct that tracks elapsed time when started.
///
/// Note that in order to advance the stopwatch [`tick`](Stopwatch::tick) **MUST** be called.
/// # Examples
///
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
///
/// stopwatch.tick(Duration::from_secs_f32(1.0)); // tick one second
/// assert_eq!(stopwatch.elapsed_secs(), 1.0);
///
/// stopwatch.pause();
/// stopwatch.tick(Duration::from_secs_f32(1.0)); // paused stopwatches don't tick
/// assert_eq!(stopwatch.elapsed_secs(), 1.0);
///
/// stopwatch.reset(); // reset the stopwatch
/// assert!(stopwatch.is_paused());
/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
/// ```
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Default, Clone, PartialEq)
)]
pub struct Stopwatch {
elapsed: Duration,
is_paused: bool,
}
impl Stopwatch {
/// Create a new unpaused `Stopwatch` with no elapsed time.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// let stopwatch = Stopwatch::new();
/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
/// assert_eq!(stopwatch.is_paused(), false);
/// ```
pub fn new() -> Self {
Default::default()
}
/// Returns the elapsed time since the last [`reset`](Stopwatch::reset)
/// of the stopwatch.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.tick(Duration::from_secs(1));
/// assert_eq!(stopwatch.elapsed(), Duration::from_secs(1));
/// ```
///
/// # See Also
///
/// [`elapsed_secs`](Stopwatch::elapsed_secs) - if an `f32` value is desirable instead.
/// [`elapsed_secs_f64`](Stopwatch::elapsed_secs_f64) - if an `f64` is desirable instead.
#[inline]
pub fn elapsed(&self) -> Duration {
self.elapsed
}
/// Returns the elapsed time since the last [`reset`](Stopwatch::reset)
/// of the stopwatch, in seconds.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.tick(Duration::from_secs(1));
/// assert_eq!(stopwatch.elapsed_secs(), 1.0);
/// ```
///
/// # See Also
///
/// [`elapsed`](Stopwatch::elapsed) - if a `Duration` is desirable instead.
/// [`elapsed_secs_f64`](Stopwatch::elapsed_secs_f64) - if an `f64` is desirable instead.
#[inline]
pub fn elapsed_secs(&self) -> f32 {
self.elapsed().as_secs_f32()
}
/// Returns the elapsed time since the last [`reset`](Stopwatch::reset)
/// of the stopwatch, in seconds, as f64.
///
/// # See Also
///
/// [`elapsed`](Stopwatch::elapsed) - if a `Duration` is desirable instead.
/// [`elapsed_secs`](Stopwatch::elapsed_secs) - if an `f32` is desirable instead.
#[inline]
pub fn elapsed_secs_f64(&self) -> f64 {
self.elapsed().as_secs_f64()
}
/// Sets the elapsed time of the stopwatch.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.set_elapsed(Duration::from_secs_f32(1.0));
/// assert_eq!(stopwatch.elapsed_secs(), 1.0);
/// ```
#[inline]
pub fn set_elapsed(&mut self, time: Duration) {
self.elapsed = time;
}
/// Advance the stopwatch by `delta` seconds.
/// If the stopwatch is paused, ticking will not have any effect
/// on elapsed time.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.tick(Duration::from_secs_f32(1.5));
/// assert_eq!(stopwatch.elapsed_secs(), 1.5);
/// ```
pub fn tick(&mut self, delta: Duration) -> &Self {
if !self.is_paused() {
self.elapsed = self.elapsed.saturating_add(delta);
}
self
}
/// Pauses the stopwatch. Any call to [`tick`](Stopwatch::tick) while
/// paused will not have any effect on the elapsed time.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.pause();
/// stopwatch.tick(Duration::from_secs_f32(1.5));
/// assert!(stopwatch.is_paused());
/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
/// ```
#[inline]
pub fn pause(&mut self) {
self.is_paused = true;
}
/// Unpauses the stopwatch. Resume the effect of ticking on elapsed time.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.pause();
/// stopwatch.tick(Duration::from_secs_f32(1.0));
/// stopwatch.unpause();
/// stopwatch.tick(Duration::from_secs_f32(1.0));
/// assert!(!stopwatch.is_paused());
/// assert_eq!(stopwatch.elapsed_secs(), 1.0);
/// ```
#[inline]
pub fn unpause(&mut self) {
self.is_paused = false;
}
/// Returns `true` if the stopwatch is paused.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// let mut stopwatch = Stopwatch::new();
/// assert!(!stopwatch.is_paused());
/// stopwatch.pause();
/// assert!(stopwatch.is_paused());
/// stopwatch.unpause();
/// assert!(!stopwatch.is_paused());
/// ```
#[inline]
pub fn is_paused(&self) -> bool {
self.is_paused
}
/// Resets the stopwatch. The reset doesn't affect the paused state of the stopwatch.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.tick(Duration::from_secs_f32(1.5));
/// stopwatch.reset();
/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
/// ```
#[inline]
pub fn reset(&mut self) {
self.elapsed = Default::default();
}
}