Visit our Sponsor   Visit our Sponsor
delphi3000.com - the free delphi knowledge platform
delphi3000.com - the free delphi knowledge platform
Have a look at your member-status

connecting people's knowledge


  - Recent ArticlesRSS feed for Recent Articles on delphi3000.com
  - List of All Articles
  - Top Viewed Articles
  - Articles (+Attachem.)
  - Articles Of Interest
  - Categories
  - Top Uploader
  - Search
  - Index

  - My Home
  - Submit an Article
  - My Articles
  - My Personal Data
  - My Bookmarks
  - Activities
  - Login/Logout

  - Sign Up
  - Why Sign Up
  - Newsletter

  - Press
  - Advertise

  - Contact
  - Feedback





Community
Borland
ClubeDelphi
Dr. Bob
UK-BUG
Delphi Meetings
Planeta Delphi







Startblatt.de






Share this article with friendsShare this article with friends
Rate this articleRate this article - to keep the quality of delphi3000.com !
Comment this article or read through previous comments (4)


Precise timer thread using messagesGo to Brian Pedersen's websiteFormat this article printer-friendly!Bookmark function is only available for registered users!
Product:
Delphi 5.x (or higher)
Category:
Games
Skill Level:
Scoring:
Last Update:
07/15/2002
Search Keys:
delphi delphi3000 article borland vcl code-snippet TimeKillEvent TimeSetEvent PostMessage TThread
Times Scored:
6
Visits:
8346
Uploader: Brian Pedersen
Company: Pentia A/S
Reference: Caos Development
 
Question/Problem/Abstract:
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.


<<-- Here is an example of a usage -->>

  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;






Please rate this article!
Skill level:
BeginnerExpert

Useful:
No!Very!

Overall rating:
PoorExcellent



Comments to this article
Write a new comment
Is it good for me?
    Attila Ats (Apr 29 2006 10:03PM)

Hello,

in 1992 I developed a DOS application in Borland Pascal 7 which utilizes a routine written in Assembler to reprogram the timer tick interrupt to have precise heartbeat for querying an Advantech Labcard which measures analogue signals and converts them to digital numbers. The company for which I developed this application has been using it since then (they have been keeping a i286 laptop machine alive as it has an ISA slot for the A/D converter board).

Now they asked me to rewrite the application for Windows which does the same job.. I think that I may not do the same as I did with DOS (timer tick reprogramming). I tested the TTimer object and realized that it was not working precisely with low Intervals (eg. 10 millisecs). What I have to do: let the user set the frequency of sampling from the A/D converter board and then query the board for samples precisely with that frequency. If the sampling doesn't happen precisely then all the following analysis steps give incorrect results.

May the precise timer thread be a good solution for me?

Respond

Slight change to Destructor
    Michael Brooks (Apr 27 2005 12:48PM)

Good Stuff... Thank You!  

However, should the destructor look like this so that the internal timer is killed before the component is killed?

destructor TGameTimer.Destroy;
begin
  try
    Self.StopBeat();
  finally
    inherited Destroy;
  end;
end;

Respond

RE: Slight change to Destructor
Brian Pedersen (Apr 27 2005 1:06PM)

Hi Michael,

Well, it's some years since I used Delphi (and wrote this code) but AFAIR constructors and destructors "encapsulate" their creating and destroying, meaning that:

Your _constructor_ must call the inherited create as the _first_ thing and,
your _destructor_ must call the inherited destroy as the _last_ thing.
Respond

long process...
    Luis Ortega (Jul 15 2002 3:17PM)

Here is a problem... Since you are using post message... it will wait until the next message is popit from the stack, therefore if there is a long process, your thread will have to wait (the same as the timer). If instead of that you use a signal object with "Waitfor" with a expiration time you will be able to ignore and decide what to do with it...

Luis
Respond














 
Sign up to consume product discounts for Bronze memberships !

read more


  Visit our Sponsor

 

  Community Ad of
S. Kucherov
 
   














 







     
  Copyright © 2000 - 2007 delphi3000.com - All rights reserved. Terms of use. || Privacy
delphi3000.com is a service by bluestep.com IT-Services GmbH (Vienna)