bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
35.47k stars 3.51k forks source link

Provide a way to base Timer on some Time object #13420

Open jrmoserbaltimore opened 4 months ago

jrmoserbaltimore commented 4 months ago

What problem does this solve or what need does it fill?

The docs for Bevy 0.13 indicate Time<Virtual> provides a timer running at a multiple of the system clock. Res<Time<Virtual>> can be sped up, slowed down, or paused to control game time, so long as the game's objects are tied to Res<Time<Virtual>> for their time. The docs don't indicate a way to produce a Time<Virtual> based on Res<Time<Virtual>> or another Time<Virtual>.

Further, the docs for Timer don't indicate whether Timer follows Res<Time<Real>> or Res<Time<Virtual>>, and there is nothing in the docs indicating any way to control this.

For events with cooldown, it would make sense to be able to somehow time the cooldown relative to a virtual timer tied to game time. For example, Final Fantasy's active time battle system uses a character's speed to slowly fill a time gauge before the character's turn begins; while several games have cooldown times for using special moves. These times can be made faster or slower by status effects to increase or decrease a character's speed. A good approach to do so is not obvious.

There are also speedup and slowdown mechanics such as dialog box speed or battle speed in RPGs separate from the rest of game time. These somehow require timing against a clock relative to the main game clock.

What solution would you like?

A way to time events based on a given time source. One approach is to allow creation of a Time<Virtual> based on some other Time object specified by the programmer, and adjust the Time<Virtual> speed to compound with its base clock. For example, my_time can be set to 0.5 speed and based on Res<Time<Virtual>>; setting Res<Time<Virtual>> to 0.5 speed would have my_time run at 0.25 system speed.

A way to create a timer based on a Time object to go with this. my_timer could then be a Timer based on my_time, and speeding up or slowing down my_time would affect the length of real-time required for my_timer to finish.

What alternative(s) have you considered?

Writing a custom timer by simply reading Res<Time<Virtual>> and looking at the current virtual time versus a recorded virtual time at which the timer started. This offloads the entire function of Timer onto a custom implementation, which means every programmer needs to implement their own type of Timer.

jrmoserbaltimore commented 4 months ago

Okay, from reading the docs, I thought timers just ran on their own.

I looked through the source code to see if it'd be easy enough to just implement this myself and PR it; it turns out timers have to be manually ticked.

…I don't see how timers are anything other than an overcomplicated float. How are Timer and Stopwatch even useful at all in their current state? You could just check the clock and update a variable instead; the only difference right now seems to be updating your own variable requires an assignment operator to add the duration of time elapsed, while updating a Timer requires calling tick() with the duration of time elapsed.

alice-i-cecile commented 4 months ago

There are some invariants involved in keeping track of time, and you want to use a Duration, not a float.

But overall I agree with your analysis: it's frustrating and unintuitive that timers don't advance on their own. IMO the best way to resolve that would be to use trait queries (generalized to resources) and advance all objects that implement a derivable trait.

These aren't yet upstreamed (see https://github.com/bevyengine/rfcs/pull/39), but this is an interesting potential first-party use case.

MiniaczQ commented 1 month ago

This issue needs clarification. Is the goal to provide a simple way of making Time<CustomVirtual> from Time<Virtual> or is the goal to auto-tick timers like discussed in the comments?

14280 only closes this if it's about automatic ticking.