ryanisaacg / quicksilver

A simple framework for 2D games on desktop and web
https://ryanisaacg.github.io/quicksilver
Other
782 stars 77 forks source link

Proper way to achieve framerate independence? (e.g., delta time) #609

Closed akc8012 closed 4 years ago

akc8012 commented 4 years ago

I'm trying to get framerate independent movement on a project using the latest alpha. In 0.3.x, the Window struct had methods for getting framerate, so you could easily calculate delta time yourself. However, the alpha doesn't have these methods.

The alternative in 0.4.x seems to be Timers. The best I could come up with is this, which is basically cobbled together by combining this comment and the Timers example.

async fn app(window: Window, mut gfx: Graphics, mut input: Input) -> Result<()> {
    let mut game = Game::new();
    let mut update_timer = Timer::time_per_second(60.0);
    let mut draw_timer = Timer::time_per_second(60.0);

    loop {
        while let Some(_) = input.next_event().await {}

        while update_timer.tick() {
            let delta_time = update_timer.remaining();
            println!("dt: {:?}", delta_time);
            game.update(&input, delta_time);
        }

        if draw_timer.exhaust().is_some() {
            game.draw(&mut gfx);
            gfx.present(&window)?;
        }
    }
}

However, this just continually prints None. Any advice would be appreciated, even if it's to tell me I'm doing everything completely wrong! I'm still not certain if I even need to track a delta time variable, when using timers in this way. By the way, kudos to everyone working on this framework! It's been a joy to work with.

lenscas commented 4 years ago

I don't think you need to know the delta time in this case as you already have code to catch up. There is no need to tell your updates that you are 2 ticks late, when you already compensate this by updating twice.

Despite this, I do see an use for getting the supposed time between updates. Even though this is easy to calculate yourself, the Timer::time_per_second function exists precisely so you don't have to do this yourself. A simple Timer::duration() -> Duration method should solve that.

Having said all this, when I look at the code again for the remainder function, I can't help but feel like I made a mistake with either its documentation and/or what it does.

The documentation says it tells you how long you still have before it is time to tick. While the actual code returns how late you are with the tick.

I'll see if I have time to make a pull request tomorrow that fixes this difference, as well as add the duration method. However in case someone wants to beat me to it. The problems lies in https://github.com/ryanisaacg/quicksilver/blob/master/src/timer.rs#L52

akc8012 commented 4 years ago

Thanks for looking into this! Very glad that my confusion and misuse of a method has somehow, indirectly, contributed to this project 😎

ryanisaacg commented 4 years ago

Anything else or should I close out the issue?

Thanks for the report, btw!

akc8012 commented 4 years ago

Looks good! The documentation changes clear things up for me, and should probably help others in the future who are similarly confused.

ryanisaacg commented 4 years ago

👍 Feel free to open an issue if you run into anything else.