Frank Lioty's picture

Timing in OpenTK

Hi everybody,
I need to implement a timing system to move a texture pixel per pixel on my screen. Firstly I used the OnUpdateFrame() in this way:

protected override void OnUpdateFrame(FrameEventArgs e) {
      base.OnUpdateFrame(e);
      //new position
      if (sprite.moving) {
         Change_Sprite_Position(sprite, position);
      }
}

it works but I can't make a move faster than 0.03s (because I'm running the application at 30 fps). So I was looking for another way and I'm trying to use a System.Diagnostics.StopWatch.

private void Sprite_Move(Sprite sprite, List<Point> path) {
      track.Clear();
      for (int i = 1; i < path.Count; i++) {
        //memorize all track pixel, so I can update pixel per pixel my sprite position
        Bresenham2(path[i-1], path[i]);
      }
      //moving flag
      sprite.moving = true;
      //start my StopWatch
      timer.Start();
      Loop(timer, sprite);
 
    }
 
    private void Loop(Stopwatch timer, Sprite sprite) {
      int i = 1;
      while (timer.IsRunning) {
        //moving each 0.03s?
        if ((sprite.moving) & (timer.Elapsed.Milliseconds >= 30)) {
          if (i < track.Count) {
            //change sprite position to the i-th track pixel
            Sprite_Position(skinny, track[i]);
            i++;
            timer.Reset();
            Console.WriteLine("Tick");
          }
          //if I'm on the end of the track I can exit
          else timer.Stop();
        }
      }
    }

I can't see my sprite moving. Everything is blocked until the last iteration, then I see my sprite on the last pixel of the track...teleport!


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
mOfl's picture

What do you expect to happen? If you call the while (timer.IsRunning) loop, it will of course not leave the loop and the function until it's finished, meaning that your main rendering loop stalls and the rendering function is not called until the sprite has stopped moving. When implementing some kind of movement, you have to keep in mind that you are already inside a loop (the main rendering loop) that needs to run for something to be displayed.

The correct way to move your sprite is to move it framerate-independently in your rendering function. If you use the GameWindow, that's void RenderFrame(FrameEventArgs e). Framerate-independent movement means that it does not depend on the framerate of the rendering or update loop, but on time itself, meaning that the movement of your texture for example will take exactly one second on each a high-end system and a wired potato.

Use the time span since the last render call, e.Time, to calculate the incremental update of your movement. Store the total elapsed time, e.g. elapsedTime += e.Time; and use the total elapsed time to calculate your progress: progress = elapsedTime / desiredDurationOfTextureMovement;. Now move your texture to the position where it would be according to progress and render it once.

You can also do this in the UpdateEvent as you already tried, however the update loop usually runs with a limited framerate (30 fps) and the movement can therefore be a bit bumpy, while the rendering loop should run with at least 60 fps.

Frank Lioty's picture

thanks a lot, it was really simple at the end :)