Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trouble introducing a variable timestep

Tags:

c++

timer

i'm currently in university doing Computer Games Programming, in the process of creating a physics engine in C++.

I have been asked to introduce a timestep via the GetTickCount() method, despite the inaccuracies, as it is suitable for the current depth that we're going into time stepping

My problem is that now i have introduced timestep based movement, the objects that have been drawing to screen through two different overrides of an update function don't seem to work.

Through adding breakpoints in the code, it looks as if mElapsedTime doesn't seem to get passed any value and i'm stumped.

Sorry if this post is too wordy or some is off topic, i tried to provide as much context as possible and this is my first post, any help is appreciated.

(frameStartTime parameter just takes a tick count before the frame is updated and drawn)

Edit: mElapsed time of type float, for easier multiplication in the updates of the particle class (e.g. pos.x = velocity.x * timeStep) and this is the reason for the cast from an unsigned long to a float, is it still redundant?

void Simulation::gameLoopDelay(DWORD frameStartTime)
{
    DWORD presetFrameInterval = 16;
    DWORD frameProcessingTime = GetTickCount() - frameStartTime;

    if (frameProcessingTime < presetFrameInterval)
    {
        Sleep(presetFrameInterval - frameProcessingTime);
    }

    mElapsedTime = (float)frameProcessingTime / 1000.0f;
}
like image 609
Aaron Hopkinson Avatar asked Mar 19 '13 18:03

Aaron Hopkinson


2 Answers

I am not absolutely sure (because you did not provide enough information), but the first problem I see is that you do mElapsedTime = (float)frameProcessingTime / 1000.0f; regardless of Sleep(...). In other words, you forgot to take into account sleeping. To do this you would have to re-fetch GetTickCount() after calling Sleep(...). But even then, your code gets too boilerplate and error-prone.

Start approaching your problem by writing small, flexible, and reusable classes which do simple tasks:

class Stopwatch {
public:
  Stopwatch(): _start(0), _elapsed(0), _running(false) {}

  void
  start() {
    if (_running)
      return;

    _running = true;
    _start   = GetTickCount();
  }

  void
  stop() {
    if (!_running)
      return;

    _elapsed += GetTickCount() - _start;
    _running  = false;
  }

  void
  reset() {
    _running = false;
    _elapsed = 0;
  }

  void
  restart() {
    _running = true;
    _elapsed = 0;
    _start   = GetTickCount();
  }

  bool
  isRunning() const {
    return _running;
  }

  DWORD
  elapsed() const {
    return _running ? _elapsed + (GetTickCount() - _start) : _elapsed);
  }

  bool
  hasExpired(DWORD interval) const {
    return elapsed() > interval;
  }

private:
  DWORD _start;
  DWORD _elapsed;
  bool  _running;
};

It is straightforward how to utilize this class for your needs.

Secondly, I don't really get why do you use Sleep. The last time I've seen the example of frame delaying - was around 5 years ago in some crappy tutorials on writing toy games. This stuff was really popular in old games, but in modern games it does not make sense. Of course if this is the requirement for your assignment, then I apologize, but the remark still applies.

Finally, I'd like to give you additional advice from my own experience with real-time visualization. Never write time-dependent code with such raw facilities as GetTickCount or other popular crap among Windows API (or Linux API, does not matter) programmers, as it once again smells like out-dated tutorials on creating toy games.

What to use then, you ask? Well, these days we are lucky because there are well-designed, reliable, cross-platform libraries out there such as Boost. You should probably be familiar with it, however if you are not - you should definitely do. For your particular problem, there is a module Boost.Chrono which was carefully designed to work with time properly. I have used it in several projects involving real-time rendering, and I have to admit that it is very robust and worth learning.

Another, library - Boost.Units - that is a must for writing stable and accurate physics engine. This library provides type-safe support for units (force, mass, velocity, acceleration, etc.) and quantities for various unit system models (SI or CGS, for example). It is based on template-metaprogramming, and therefore results in no run-time overhead.

Once again, I understand that your assignment probably does not allow stuff like that. However, I believe that all that I've mentioned here would be valuable to you in the future. By the way, it is a good example of how much more you should know than university assignments offer to be able to solve real life problems.

If you still want more help on your problem in particular - I need more information. For instance, begin with showing us the whole code of your game loop.

like image 111
Alexander Shukaev Avatar answered Oct 07 '22 01:10

Alexander Shukaev


I think, basically, you need to change your code to this:

void Simulation::gameLoopDelay(DWORD frameStartTime)
{
    DWORD presetFrameInterval = 16;
    DWORD frameProcessingTime = GetTickCount() - frameStartTime;

    if (frameProcessingTime < presetFrameInterval)
    {
        Sleep(presetFrameInterval - frameProcessingTime);
    }

    frameProcessingTime = GetTickCount() - frameStartTime;

    mElapsedTime = (float)frameProcessingTime / 1000.0f;
}

This is not the most advised way to do this since Sleep here is probably locking out the whole thread. It's probably better to just do the frame at a fractional time, but I think this will fix your code.

like image 29
Joe Doliner Avatar answered Oct 07 '22 01:10

Joe Doliner