Mega Code Archive

 
Categories / Delphi / Examples
 

Precise timer thread using messages

Title: Precise timer thread using messages Question: When programming realtime interfaces and computergames, you need a precise timing signal. The game engine must run at the same rate, no matter what your framerate are. One way of doing this is to calculate how many moves your game engine must have done since last frame was drawn. But if you have all the clock cycles in the world (common when doing smaller games), you can create 2 timers: One with high priority that controls all the game movements and one with low priority doing the screen updates. This example shows a game timer implementation using a thread and messaging. Answer: unit gametimer; // ----------------------------------------------------------------------------- // // Very precise timer running in its own thread. // // Usage: Add the following line to the public section of your game form: // procedure MoveGame( var Msg : TMsg ); message WM_MOVEGAME; // // MoveGame is the code where you do all the moves for your game. // // You could then create a timer that draws your game. This timer must // have a lower priority than your game timer. Your game will then have the same // game timer, but a slower framerate if the PC lacks resources. // // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- interface // ----------------------------------------------------------------------------- uses Windows, Messages, Classes, MmSystem, Sysutils, forms; const WM_MOVEGAME = WM_USER + 203; TIMERVALUE = 20; { 10 = 100 fps / 20 = 50 fps / 40 = 25 fps / 50 = 20 fps } Type TGameTimer = class( TThread ) private Msg : TMsg; FHeartbeat : UINT; fisBeating : boolean; FGameBeat : integer; fForm : TForm; fCount : longword; procedure Execute; override; public constructor Create( AForm : TForm ); overload; constructor Create( AForm : TForm; AFps : integer ); overload; destructor Destroy; override; procedure StartBeat; procedure StopBeat; procedure PauseBeat; property GameBeat : integer read fGameBeat write fGameBeat; property isBeating : boolean read fisBeating; property Count : longword read fCount; end; // ----------------------------------------------------------------------------- implementation // ----------------------------------------------------------------------------- var gSelf : TGameTimer; procedure GameUpdate(uTimerID, uMessage: UINT; dwUser, dw1, dw2: DWORD); stdcall; begin gSelf.Resume; end; // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- constructor TGameTimer.Create( AForm : TForm ); begin inherited Create( TRUE ); inherited priority := tpTimeCritical; fForm := AForm; fGameBeat := TIMERVALUE; fCount := 0; end; // ----------------------------------------------------------------------------- constructor TGameTimer.Create( AForm : TForm; AFps : integer ); begin Create( AForm ); fGameBeat := round( 1000 / AFps ); end; // ----------------------------------------------------------------------------- Destructor TGameTimer.Destroy; begin try gSelf := nil; finally inherited Destroy; end; end; // ----------------------------------------------------------------------------- procedure TGameTimer.Execute; begin while not Terminated do begin PostMessage( fForm.Handle, WM_MOVEGAME, 0, 0 ); inc( fCount ); Suspend; end; end; // ----------------------------------------------------------------------------- procedure TGameTimer.PauseBeat; var r : integer; begin if FHeartbeat 0 then begin r := TimeKillEvent(FHeartbeat); if r TIMERR_NOERROR then raise Exception.CreateFmt('Cannot stop heartbeat: %d', [r]) else fisBeating := FALSE; end; end; // ----------------------------------------------------------------------------- procedure TGameTimer.StartBeat; begin gSelf := self; FHeartbeat := TimeSetEvent(FGameBeat, FGameBeat, GameUpdate, UINT(Self), TIME_PERIODIC); if FHeartbeat = 0 then raise Exception.Create( 'Cannot start heartbeat' ) else fisBeating := TRUE; end; // ----------------------------------------------------------------------------- procedure TGameTimer.StopBeat; begin PauseBeat; FHeartbeat := 0; fCount := 0; gSelf := nil; end; end. TfrmMain = class(TForm) private { Private declarations } fGameTimer : TGameTimer; public { Public declarations } procedure MoveGame( var Msg : TMsg ); message WM_MOVEGAME; end; procedure TfrmMain.MoveGame( var Msg : TMsg ); begin { Do your game movements here } end;