arcade icon indicating copy to clipboard operation
arcade copied to clipboard

Time enhancements

Open gran4 opened this issue 2 years ago • 7 comments

Enhancement request:

Make the timer in arcade better.

What should be added/changed?

P.S: dt stands for delta_time

  • clamped dt
  • paused dt
  • slow-mo - give both a delta_time and a real_delta_time?
  • expose Time object with these additional values, to avoid polluting update function signatures w/a bunch of args time.delta_time and time.real_delta_time, time.set_slowmo()

What would it help with?

  • There are huge dt's after debugging.
  • Loading games causes huge dt.
  • Makes slow-mo effect easy.

gran4 avatar May 23 '23 22:05 gran4

Should we apply the same scaling for arcade.schedule() / arcade.schedule_once()? Or should we deliberately keep them on real-world time for simplicity?

Could be tricky: arcade.schedule_once(thing, 5) # run in 5 seconds after 2s, we still have 3s left to go Set slowmo factor of 0.5 so that things run at half-speed. The scheduled function should thus run after 6s not 3s

Lends credence to @gran4's idea that this be implemented closer to pyglet. Maybe arcade implements its own subclass of pyglet's Clock?

Should we allow scheduled items to bypass time scaling, so they follow real-world time no matter what?

cspotcode avatar May 24 '23 22:05 cspotcode

Another option: fixed delta_time. Effectively, your on_update receives a constant value no matter if your computer slows down and cannot maintain the requested framerate.

cspotcode avatar May 24 '23 22:05 cspotcode

Linking to #1543

cspotcode avatar May 26 '23 14:05 cspotcode

Okay, here is my proposal for how we provide this functionality.

We have a PrimaryClock, which each window has one of. The window also now has a special method called _tick that always runs before any other method and at the same speed as the update_rate (Will need to be rescheduled whenever a view is shown).

Then, at any time, the dev can request the PrimaryClock to make a SubClock. These are ticked by the primary and can have their own delta_time, which could be clamped or any other funky feature.

You can not get the SubClock reference directly, but I'll figure out some not too expensive way to set the properties of the clock. (maybe a named tuple? with PrimaryClock implementing __set_item__, we will see).

Finally, there are ClockImages, which have a label and all instances with the same label point to the same SubClock.

The reason ClockImages and SubClocks are different is so you can manipulate what a bunch of different systems see as the same time without screwing with others.

Here is an example say your player has a slow downtime ability. When it activates, you don't want the player's clock to change, but you want the enemies/environment to slow down. So you have a gametime SubClock, and two ClockImages player, and gameenviroment. For 99% of the time, both ClockImages look at gametime, but when you activate the slowdown time ability, you make a new temp SubClock and tell only the gameenviroment ClockImage to switch. If you've coded your game correctly, this should slow down everything, but your player and none of your systems need to be aware of anything but their ClockImage delta_time.

While initially, it would appear to be an over complication. It is actually a wonderful bit of abstraction, so devs dont need to reason with the clock system when developing an unrelated system.

in other words, "It Just Works".

DragonMoffon avatar Aug 04 '23 03:08 DragonMoffon

My game_coro implements a similar clock/subclock API. It's called "context" but similar idea: an object that tracks time and exposes it on various properties.

For example, coroutines can create a sub-clock that starts tracking local time starting at zero: https://github.com/cspotcode/py_game_coroutines/blob/ab56fbbcdffe405c111f64ee9a001c8428ded09e/example/example.py#L27

https://github.com/cspotcode/py_game_coroutines https://pypi.org/project/game-coro/

cspotcode avatar Aug 04 '23 20:08 cspotcode

Has anything been done on this? If not, I like DragonMoffon's idea of a game clock being split off. One way you could implement it for speed, is to have 2 layers of clocks(It will be easier to show through code). Basically, each system/ or entity in the world chooses 1 clock: the player clock or the game clock. These 2 clocks are different objects(so we should give them different names since it is not the clock, it just references the clock.). These "clocks" will reference the main clock but not actually do the timing. The main clock will do the timing and the "clocks" will just reference the main clock.

When we need to slow mo, we can just change the reference of the "game clock" and change back to make it normal. Theoretically using this method, you could have as many clocks as you want(although I'm not sure why you would do that). This solves the speed(a lot) and complication(a little) problem since you do not need to change each reference one by one.

For the references to the clock maybe the names could be: game_clock_reference and player_clock_reference?

What if we just let people create their own references so it does not have to be in the arcade code? We can still make it easier though by adding a function for splitting off a clock

gran4 avatar May 23 '24 22:05 gran4

It could be something like this where the player could just handle it within an arcade framework Basically, this program would theoretically toggle slow mode every 1000 times the loop runs. We need to set the objects to reference one thing and that one thing to reference the actual clock, so we can change the clock without changing every reference of every entity. Also, there could definitely be better names for these


i = 0
slow_mode = False

player_clock = clock_reference()
game_clock = clock_reference()
player_clock.reference = Clock()
game_clock.reference = player_clock.reference

player.clock = player_clock
for animal in animals:
    animal.clock = game_clock

while True:
    i += 1
    if i > 1000:
        i = 0
        if slow_mode:
            game_clock.reference = player_clock_reference
        else:
            game_clock.reference = player_clock_reference.new_clock(speed=0.5)

gran4 avatar May 24 '24 15:05 gran4

The new clocks in 3.0 should help here

einarf avatar Jul 12 '24 16:07 einarf