[Mono-list] Bug in GetTickCount...

Maurizio Colucci seguso.forever@tin.it
Thu, 22 May 2003 20:45:40 +0200


Hello,

After days debugging my game, I arrived at the conclusion that there
must be a terrible bug in System.Environment.GetTickCount().

I don't know where, but maybe this will help you find it.

When the frame rate of the game got above 60 FPS, the following Timer
class started misbehaving: the values returned by the Dt property were
wrong, and the game had a jerky motion.

With the Microsoft runtime,  everything was smooth.

After switching to SDL::GetTicks(), everything is smooth under linux
also. Therefore the bug must be in mono.

The misbehaving code is:

using System;
using SDLDotNet;
using N_Logica_del_tennis;
namespace N_Timer{
    public class Timer {
	    float dt_;
	    float[] delta_t_= new float[]{ 
		    1.0f/90000.0f, 
		    1.0f/90000.0f, 
		    1.0f/90000.0f
	    };
	    float previous_frame_time_;
	    float num_frames_since_last_fps_display_;
	    float time_of_latest_fps_calculation_;
	    float fps_;
	    readonly SDL sdl_; // per getticks
	    public float FPS  {
		    get {
			    return fps_;
		    }
	    }
	    /// <summary>
	    /// Deve essere chiamata all'inizio di ogni fotogramma.
	    /// </summary>
	    public void Update() {
		    #region update the current dt based on the draw time of the latest frame 
		    //int current_time=System.Environment.TickCount; // BUG!!
		    uint current_time=sdl_.GetTicks();
		    float delta_t_passed_since_last_frame_in_milliseconds =
 			    (float) (current_time - previous_frame_time_);
		    /// so far it is in milliseconds. COnvert to
		    /// seconds by DIVIDING by 1000.
		    float delta_t_passed_since_last_frame_in_seconds=
			    delta_t_passed_since_last_frame_in_milliseconds/1000.0f;
		    #region insert this lapse of time into the array
		    for (int i=1; i< delta_t_.Length; i++) 
			    delta_t_[i-1] = delta_t_[i];
		    /// nell'ultimo elemento metto il nuovo deltaT
		    delta_t_[delta_t_.Length-1] = 
			    delta_t_passed_since_last_frame_in_seconds;
		    #endregion
		    #region calculate dt by averaging the values in the array
		    dt_=0;
		    foreach (float f in delta_t_) 
			    dt_+= f;
		    dt_ /= (float) delta_t_.Length;
		    if(dt_>1.0f){
			    dt_=1.0f/90.0f;
			    Console.WriteLine("dt >1. I set dt equal to 1/90 seconds.");
		    }
		    #endregion
		    #endregion
		    previous_frame_time_ = current_time; 
		    #region Calculate frame rate
		    num_frames_since_last_fps_display_ ++;
		    if (current_time- 
			time_of_latest_fps_calculation_ 
			>=1000) {
			    /// if more than 1 second has passed...
			    time_of_latest_fps_calculation_ 
				    =current_time;
			    fps_=num_frames_since_last_fps_display_;
			    num_frames_since_last_fps_display_=0;								
		    }
		    #endregion
       	    }
	    public float Dt  {
		    get {
			    return dt_;
		    }
	    }
	    public Timer(SDL sdl)  {
		    Debug.Assert(sdl!=null, "sdl=null");
		    sdl_=sdl;
		    dt_=1.0f/900.0f;
		    previous_frame_time_= sdl_.GetTicks(); //System.Environment.TickCount; BUG!!
		    fps_ = 50;
		    num_frames_since_last_fps_display_ = 0; 
		    time_of_latest_fps_calculation_ = sdl_.GetTicks(); //System.Environment.TickCount;
	    }
	    /// <summary>
	    /// If you pause the game, call this function just before resuming.
	    /// </summary>
	    public void ResetAfterLongPause() {
		    previous_frame_time_=sdl_.GetTicks(); //System.Environment.TickCount;
	    }
    }
}