2
\$\begingroup\$

When writing good framerate-independent game code, you take some sort of deltaTime parameter in your main render() function and then scale your calculations based on that -- this is essential for things like physics engines.

However, in my game there are often discrete tasks -- the AI system might add components representing a "task" to the agents that should be removed (finished) after x seconds have passed. To make a contrived example, if a task/component takes 60ms to process, and the game is running at 60fps (16.6ms/frame), then the first three frames will run, bringing the "work done" on that task to 50ms, and then one last frame will do 10ms of work, but then the remaining 6.6ms will be "lost", because the AI system won't run and schedule new work until the next frame.

Ideally, I'd like to not lose the "loose change" of ms that occurs at the end of tasks.

How do people normally deal with this? I can think of a few options:

  1. Ignore it -- the shortest tasks will probably never be less than 100ms anyway, generally more in the 1-5 second range. It's not multiplayer, and provided the game can maintain 60 fps (which it probably can), the experience should be the same for all players.
  2. "Carry" the remainder over to the next frame. In the example above, give the agents 23.2ms of time to "spend" in the next frame.
  3. Amp up the framerate. At 60fps, the most time you can possibly waste is 16.6ms, but at 200fps you can lose at most 5ms. (Currently my logic loop and render loop are coupled and capped at 60fps, but I can easily change that.)
\$\endgroup\$
  • \$\begingroup\$ are you using threads? because once you are done with your tasks you should go right back to rendering as fast as you can, that 6.6ms could be used for sending commands to the GPU or starting another update cycle for the next frame. You should try to off load the heavy lifting to another thread. \$\endgroup\$ – Justin William Stanley Bryant Dec 22 '16 at 3:23
  • \$\begingroup\$ Not using threads yet, and...don't really want to unless I have to. Honestly, the game's just not that computationally intensive anyway. It's very early on, but still hitting 1000 fps easy, but even after I add more it shouldn't dip below, I dunno, 300. Just a simple top-down 2d game with sprites. It sounds like you're possibly advocating #3, above. \$\endgroup\$ – Max Dec 22 '16 at 3:27
  • \$\begingroup\$ After reading your comment it seems like a case of trying to fix something that isn't broken yet :P. I would just keep it in your mind and make sure your changes don't have a big impact on frame time. I run my update cycle at 60fps but let the render push out frames as fast as it can. \$\endgroup\$ – Justin William Stanley Bryant Dec 22 '16 at 3:40
  • \$\begingroup\$ @JustinWilliamStanleyBryant Yeah, probably -- that's why the first option is #1 :) it's just something that'll be a huge pain if I ever decide to go with the #2 option. #1 and #3 would be pretty easy to add later. Thanks for your input. \$\endgroup\$ – Max Dec 22 '16 at 3:42
  • \$\begingroup\$ Another route you could consider is using a fixed timestep for AI (decoupled from your rendering framerate). That way, no matter how you handle task chaining, it will have consistent results for players running at different framerates. It also lets you do things like round all tasks to a whole number of fixed steps, so you cap the total amount of "loose change" ever accrued to the delta between the most recent fixed time and render time. \$\endgroup\$ – DMGregory Dec 22 '16 at 17:07
1
\$\begingroup\$

I typically use a variation of #2 where I keep track of the amount of time since the task was last executed, and then subtract the interval as shown in the pseudo-code below:

private float timeSinceExecution = 0.00f;
private float taskLength = 5.00f; // 5 seconds
@Override public void render() {
  // Do render-y stuff.
  timeSinceExecution += Gdx.graphics.getDeltaTime();
  while(timeSinceExecution > taskLength) {
    executeTask();
    timeSinceExecution -= taskLength;
  }
}

This could easily be enhanced to be a task system or to include tasks that only run a set number of times. The reason I like using this method is that it makes sure that these interval-based tasks execute a consistent number of times regardless of the delays.

#1 and #3 I don't recommend because your game's behavior will change based on the host computer's speed. I've seen this before on older games and it's a bummer. That being said, with today's computers this is a lot less likely, and I agree with @JustinWilliamStanleyBryant that this is probably not a real issue.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.