clintbellanger / flare

Free Libre Action Roleplaying Engine
http://clintbellanger.net/rpg/
GNU General Public License v3.0
166 stars 41 forks source link

Add optional Frameskip #931

Closed clintbellanger closed 10 years ago

clintbellanger commented 12 years ago

We're not using variable frame rate, instead opting to use a fixed maximum frames per second. This essentially means if the computer can't keep up, the games slow down.

In situations like this, it's nice to offer a Frameskip option.

Currently in the main loop we call logic() then render(). Then we rest for a few milliseconds if necessary, to keep frame rate at a set maximum.

In Frameskip mode, we would call logic() twice then render(), and wait for up to twice the usual amount of milliseconds.

So imagine on a low-powered device, the game really can only run at about 15 fps instead of the desired 30. Most of that time is spent rendering. If we use Frameskip, the game can run at a choppy 15 fps but without the slowdown. This makes it much more playable.

Possibly we can have several frameskip options. e.g. 1:1 (render every frame), 1:2 (render every other frame, as described above), 1:3, etc.

Note 1: this will force us to STRICTLY keep all logic in the logic() routines and all rendering in the render() routines -- as it should be!

Note 2: I know having a fixed maximum frame rate is old school and some people find it a bad practice. It's more common on old console systems where everything was always in sync with the TV (60hz or 50hz depending on region). But unless it's a twitch action game it's not really a big deal. It's way easier and less bug-prone to code in frames than in delta-time. Flare's still a pretty simple engine, and I think that's quite a good thing.

stefanbeller commented 12 years ago

I would not have strict rates 1:1 1:2 etc, but make it adaptive.

while gaming:
    render()
    // actually logic for next step, skip as many renders as needed to get in timing again:
   time FrameTime, currentTime;
   while currentTime > FrameTime
      time t = calculateFinishTimeofNextFrame();
      all->logic();
      FrameTime = now();
   wait until FrameTime
clintbellanger commented 12 years ago

Adaptive frame skipping (as an option turned on by default) sounds okay to me. Someone can implement a proposed solution, and we'll try it on several devices.

stefanbeller commented 12 years ago

The commit referencing this issue basically implements the earlier proposed algorithm.

One problem, which we still need to care of: Some things depend on animations being in the first, mid or last frame, such as playing sounds or calling the takeHit function. We need to decouple the logical passed time, which will be used in logic() functions from the actual rendering.

In case we have an animation having 5 frames: The referencing patch will still go through the animations by 1 2 3 4 5, but then hitting will take slower as the other logic such as running.

So to have all game logic passing in the same speed, we would need to have a logic frame counter and an actual frame display counter, which skips the intermediate rendering frames.

clintbellanger commented 12 years ago

The animation incrementors, and logic based on animation frame, need to all be in the logic() routines. Basically the game should simulate perfectly if we never call the render() routines at all.

igorko commented 10 years ago

@clintbellanger , Do we still need this? @stefanbeller can you send pull request?

stefanbeller commented 10 years ago

I can try to redo this.

dorkster commented 10 years ago

Moved to: https://github.com/clintbellanger/flare-engine/issues/985