-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Merged by Bors] - ♻️ Timer
refactor to duration.✨ Add Stopwatch
struct.
#1151
Conversation
Refactor Timer to have Type parameter
My initial thoughts are that it all looks good, apart from the type parameter to I think the rest of the changes would be very welcome, pending full code review Edit: I appreciate your use of emoji too :) |
I'm missing the implementation of
I totally agree, this is something wierd. However, there are good motivations to explore this. The idea behind it is to prevent user from having to do this: pub MyTimer(pub Timer); // this could be avoided as well in many cases
fn main() {
let mut timer = MyTimer(Timer::from_seconds(1.0, false));
timer.0.tick(1.0); // <- this `.0` is a common boilerplate to all specialized timers
} This way they can attach multiples timers to an entity without this NewType pattern, and they're all equally easy to use. It's a fairly common use, even the 0.4 blog post points that out:
Though it makes some things harder as pointed out in the issue:
Which is a totally understandable concern to be honest. |
Hey there! I don't like all those
Also the issue with registering the |
/// ``` | ||
#[derive(Debug, Clone, Reflect)] | ||
#[reflect(Component)] | ||
pub struct Stopwatch<T: Send + Sync + 'static = ()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the = ()
should mean that the type can be ()
by default but I didn't get it to actually work in the short time I tried. I don't think it should be kept if it doesn't work or we don't want a default
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I'm not sure of what it does either. It seems that this feature is not intended for using the type but rather implementing it.
I'm going to remove them then.
examples/ecs/timers.rs
Outdated
@@ -32,12 +32,12 @@ impl Default for Countdown { | |||
|
|||
fn setup_system(commands: &mut Commands) { | |||
// Add an entity to the world with a timer | |||
commands.spawn((Timer::from_seconds(5.0, false),)); | |||
commands.spawn((Timer::<Entity>::from_seconds(5.0, false),)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I especially don't like using Entity
as the associated type, I find it confusing
Yeah me neither, the thing is that with such small scoped examples the
Oh yeah, I didn't have that in mind at all! It makes this solution far less desirable indeed |
As I'm working with system-local resources (i.e. Local) I'm increasingly wishing that Timer was just a trait that I could derive or implement for each separate system. Would that be a feasible direction to take this redesign? Here's the motivating example: https://github.com/alice-i-cecile/understanding-bevy/blob/main/src/ecs/communication/resources_code/examples/system_local_resources.rs |
Our whole Time and Timer API and related should also very much be using a Duration type, rather than an f32. f32s lose precision as they increase, resulting in progressively worse time resolution as the game runs for longer. After half an hour of game time, you only have millisecond precision. Duration also seems to support other useful edge cases and have nice utility functions (and provides for a better interface with other libraries). |
It seems confusing to make
I agree, it's a minor change though. Edit: I made this PR a draft because it needs more discussion clearly 😅 |
You may very well be right here. I was thinking an API like: #[derive(Timer)]
struct SlowTimer; which gives you access to the various timer methods on Upon reflection, a parametrized Timer type is definitely my preferred solution from an ergonomic perspective: what are the blockers on that? I read through the thread and commits but couldn't really grok it. |
Note: This is maybe only slightly related to this PR but might be worth keeping in mind. The most common use case for timers I have is to run systems every
What I would like to do is something like: #[bevy::once_every(5.0)]
fn my_system() {
// this system is run every 5 seconds
} With the current struct Wrapper(Timer);
impl Default for Wrapper {
fn default() -> Self {
Wrapper(Timer::from_seconds(5.0 /* the parameter */, true)
}
}
fn my_system(time: Res<Time>, mut timer: Local<Wrapper>, /* original parameters */) {
if !timer.0.tick(time.delta_seconds()).just_finished() {
return;
}
/* original body */
// this system is run every 5 seconds
} The first part of the desugaring (the wrapper and default impl) could also be another macro. That would probably solve a lot of other use cases. |
I'm not sure a type parameter is much better. I've seen this idiom in C++ (I've heard people call these type tags), but the newtype idiom is really the rust standard. Neither of those are great, but until language support comes in to solve the issue, I'd rather stick to the most common option |
There will be a third once #1144 lands: system sets. You'll be able to put I'm also opposed to plumbing in a type parameter into the stock timer. |
Is that really the case ? I find that timers as components are also a pretty common use case.
I would agree, but this doesn't allow for an entity to have multiple timers for different usage. |
You're right. It's probably not the most common case. In my mind other cases are more "custom" and I think this case should definitely be made as easy as possible. But maybe there are other (more efficient at runtime) ways than my example with a proc macro. But I think this is getting a bit off topic. |
List of changes: * Change `tick` now takes a duration as `delta` * Add `tick_f32`, `elapsed_f32` and `duration_f32` methods. * Update examples
So, I changed the // previously
timer.tick(time.delta_seconds());
// with the refactor
timer.tick(time.delta()); This is a breaking change though. For cases where we want to interact with I realize now that the code for
That's a very reasonable proposal! I'm wondering if we want the user to use such libraries if they want, or do we want to include a helper in bevy only for timers in order to smooth things out for the user. Okay so next thing I'll do will be removing the type parameter. I would be happy to hear what you all think about the other changes in this PR, to see if they are welcome in the engine or not 😅. |
I think the change to |
Some high level thoughts:
|
Any updates on this? I'm down to include the changes in Bevy 0.5 if someone is willing to make the adjustments above. |
Yeah surely! I will continue on this, probably in the following days |
rename *_f32 methods to *_secs methods
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. Just a couple of nits
use bevy_utils::Duration; | ||
|
||
/// A Stopwatch is a struct that track elapsed time when started. | ||
/// It requires a type `T` in order to be specialized for your systems and components. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is no longer true
crates/bevy_core/src/time/timer.rs
Outdated
pub fn finished(&self) -> bool { | ||
self.finished | ||
pub fn set_elapsed(&mut self, time: Duration) { | ||
self.stopwatch.set(time); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These method names should probably be consistent. I think set_elapsed
is clearer
Yup! I fixed that, if there is anything more let me know 👍 |
bors r+ |
This pull request is following the discussion on the issue #1127. Additionally, it integrates the change proposed by #1112. The list of change of this pull request: * 💥 Change `Timer` to `Timer<T>` in order to make specialization very explicit to the user, while removing the boilerplated NewType pattern for Timers. This is a breaking change * ✨ Add `Timer::times_finished` method that counts the number of wraps for repeating timers. * ♻️ Refactored `Timer` * 🐛 Fix a bug where 2 successive calls to `Timer::tick` which makes a repeating timer to finish makes `Timer::just_finished` to return `false` where it should return `true`. Minimal failing example: ```rust use bevy::prelude::*; let mut timer: Timer<()> = Timer::from_seconds(1.0, true); timer.tick(1.5); assert!(timer.finished()); assert!(timer.just_finished()); timer.tick(1.5); assert!(timer.finished()); assert!(timer.just_finished()); // <- This fails where it should not ``` * 📚 Add extensive documentation for Timer with doc examples. * ✨ Add `Cooldown` and `Stopwatch` structs similar to `Timer` with extensive doc and tests. Even if the type specialization is not retained for bevy, the doc, bugfix and added method are worth salvaging 😅. This is my first PR for bevy, please be kind to me ❤️ . Co-authored-by: Carter Anderson <mcanders1@gmail.com>
Build failed: |
Hum... What should I do about that? |
bors is doing a rebase and rerun tests before merging... to reproduce, could you rebase your pr? |
Sorry I resolved the merge conflict, but a path change on main broke another file. I'm pushing the fix now. |
Or you should be able to check out 6de8c03, somehow |
bors r+ |
This pull request is following the discussion on the issue #1127. Additionally, it integrates the change proposed by #1112. The list of change of this pull request: * ✨ Add `Timer::times_finished` method that counts the number of wraps for repeating timers. * ♻️ Refactored `Timer` * 🐛 Fix a bug where 2 successive calls to `Timer::tick` which makes a repeating timer to finish makes `Timer::just_finished` to return `false` where it should return `true`. Minimal failing example: ```rust use bevy::prelude::*; let mut timer: Timer<()> = Timer::from_seconds(1.0, true); timer.tick(1.5); assert!(timer.finished()); assert!(timer.just_finished()); timer.tick(1.5); assert!(timer.finished()); assert!(timer.just_finished()); // <- This fails where it should not ``` * 📚 Add extensive documentation for Timer with doc examples. * ✨ Add a `Stopwatch` struct similar to `Timer` with extensive doc and tests. Even if the type specialization is not retained for bevy, the doc, bugfix and added method are worth salvaging 😅. This is my first PR for bevy, please be kind to me ❤️ . Co-authored-by: Carter Anderson <mcanders1@gmail.com>
Build failed: |
hmm maybe you need to write |
bors retry |
🔒 Permission denied Existing reviewers: click here to make DJMcNab a reviewer |
haha oh well |
bors r+ |
This pull request is following the discussion on the issue #1127. Additionally, it integrates the change proposed by #1112. The list of change of this pull request: * ✨ Add `Timer::times_finished` method that counts the number of wraps for repeating timers. * ♻️ Refactored `Timer` * 🐛 Fix a bug where 2 successive calls to `Timer::tick` which makes a repeating timer to finish makes `Timer::just_finished` to return `false` where it should return `true`. Minimal failing example: ```rust use bevy::prelude::*; let mut timer: Timer<()> = Timer::from_seconds(1.0, true); timer.tick(1.5); assert!(timer.finished()); assert!(timer.just_finished()); timer.tick(1.5); assert!(timer.finished()); assert!(timer.just_finished()); // <- This fails where it should not ``` * 📚 Add extensive documentation for Timer with doc examples. * ✨ Add a `Stopwatch` struct similar to `Timer` with extensive doc and tests. Even if the type specialization is not retained for bevy, the doc, bugfix and added method are worth salvaging 😅. This is my first PR for bevy, please be kind to me ❤️ . Co-authored-by: Carter Anderson <mcanders1@gmail.com>
Pull request successfully merged into main. Build succeeded: |
Timer
refactor to duration.✨ Add Stopwatch
struct.Timer
refactor to duration.✨ Add Stopwatch
struct.
This pull request is following the discussion on the issue #1127. Additionally, it integrates the change proposed by #1112.
The list of change of this pull request:
Timer::times_finished
method that counts the number of wraps for repeating timers.Timer
Timer::tick
which makes a repeating timer to finish makesTimer::just_finished
to returnfalse
where it should returntrue
. Minimal failing example:Stopwatch
struct similar toTimer
with extensive doc and tests.Even if the type specialization is not retained for bevy, the doc, bugfix and added method are worth salvaging 😅.
This is my first PR for bevy, please be kind to me ❤️ .