PDA

View Full Version : time based movement and WinAPI + DirectX



lordzero
27-03-2008, 05:02 AM
Hello

I'm trying fix the movement using WinAPI and DirectX...

My friend called me patrick showed a code of glscene that seems useful,

My code of rendering need be in main loop and the movement of sprites must be at on timer event to stay working properly on all machines?

loop program source:




procedure TConsole.OnProcess;
begin

Keyboard.Refresh();

Device.Render(0,0,255);

end;

procedure TConsole.DoMove();
begin

if (Keyboard.Key[200]) then
begin

Sprite.X := Sprite.X - 3;

Sprite.DoAnim(True, 0, 11, 0.5)

end;

end;

procedure TConsole.OnRender(Sender: TObject);
begin

Sprite.Draw;

DoMove();

end;

// Enter the message loop
FillChar(msg, SizeOf(msg), 0);
while &#40;msg.message <> WM_QUIT&#41; do
begin
if PeekMessage&#40;msg, 0, 0, 0, PM_REMOVE&#41; then
begin
TranslateMessage&#40;msg&#41;;

DispatchMessage&#40;msg&#41;;

end
else
begin
Console.OnProcess;
end;
end;




glscene's timer code:


unit AsyncTimer;

interface

&#123;$i GLScene.inc&#125;

uses Classes;

const
cDEFAULT_TIMER_INTERVAL = 1000;

&#123;$IFDEF UNIX&#125;
type
TThreadPriority = integer;
&#123;$ENDIF&#125;

type
// TAsyncTimer
//
&#123;&#58; Asynchronous timer component &#40;actual 1 ms resolution, if CPU fast enough&#41;.<p>
Keep in mind timer resolution is obtained <i>in-between</i> events, but
events are not triggered every x ms. For instance if you set the interval to
5 ms, and your Timer event takes 1 ms to complete, Timer events will actually
be triggered every 5+1=6 ms &#40;that's why it's "asynchronous"&#41;.<p>
This component is based on ThreadedTimer by Carlos Barbosa. &#125;
TAsyncTimer = class&#40;TComponent&#41;
private
FEnabled&#58; Boolean;
FOnTimer&#58; TNotifyEvent;
FTimerThread&#58; TThread;

protected
procedure SetEnabled&#40;Value&#58; Boolean&#41;;
function GetInterval&#58; Word;
procedure SetInterval&#40;Value&#58; Word&#41;;
function GetThreadPriority&#58; TThreadPriority;
procedure SetThreadPriority&#40;Value&#58; TThreadPriority&#41;;
procedure DoTimer;

public
constructor Create&#40;AOwner&#58; TComponent&#41;; override;
destructor Destroy; override;

published
property Enabled&#58; Boolean read FEnabled write SetEnabled default False;
property Interval&#58; Word read GetInterval write SetInterval default cDEFAULT_TIMER_INTERVAL;
property OnTimer&#58; TNotifyEvent read FOnTimer write FOnTimer;
property ThreadPriority&#58; TThreadPriority read GetThreadPriority write SetThreadPriority &#123;$IFDEF WINDOWS&#125; default tpTimeCritical&#123;$ENDIF&#125;;
end;

// ------------------------------------------------------------------
// ------------------------------------------------------------------
// ------------------------------------------------------------------
implementation
// ------------------------------------------------------------------
// ------------------------------------------------------------------
// ------------------------------------------------------------------

uses SysUtils, GLCrossPlatform;

type

// TTimerThread
//
TTimerThread = class&#40;TThread&#41;
private
FOwner&#58; TAsyncTimer;
FInterval&#58; Word;
protected
constructor Create&#40;CreateSuspended&#58; Boolean&#41;; virtual;
procedure Execute; override;
end;

// Create
//
constructor TTimerThread.Create&#40;CreateSuspended&#58; Boolean&#41;;
begin
inherited Create&#40;CreateSuspended&#41;;
end;

// Execute
//
procedure TTimerThread.Execute;
var
lastTick, nextTick, curTick, perfFreq &#58; Int64;
begin
QueryPerformanceFrequency&#40;perfFreq&#41;;
QueryPerformanceCounter&#40;lastTick&#41;;
nextTick&#58;=lastTick+&#40;FInterval*perfFreq&#41; div 1000;
while not Terminated do begin
while not Terminated do begin
QueryPerformanceCounter&#40;lastTick&#41;;
if lastTick>=nextTick then break;
Sleep&#40;1&#41;;
end;
if not Terminated then begin
// if time elapsed run user-event
Synchronize&#40;FOwner.DoTimer&#41;;
QueryPerformanceCounter&#40;curTick&#41;;
nextTick&#58;=lastTick+&#40;FInterval*perfFreq&#41; div 1000;
if nextTick<=curTick then begin
// CPU too slow... delay to avoid monopolizing what's left
nextTick&#58;=curTick+&#40;FInterval*perfFreq&#41; div 1000;
end;
end;
end;
end;

&#123; TAsyncTimer &#125;

// Create
//
constructor TAsyncTimer.Create&#40;AOwner&#58; TComponent&#41;;
begin
inherited Create&#40;AOwner&#41;;
// create timer thread
FTimerThread&#58;=TTimerThread.Create&#40;True&#41;;
with TTimerThread&#40;FTimerThread&#41; do begin
FOwner&#58;=Self;
FreeOnTerminate&#58;=False;
&#123;$IFDEF WINDOWS&#125; Priority&#58;=tpTimeCritical;&#123;$ENDIF&#125;
FInterval&#58;=cDEFAULT_TIMER_INTERVAL;
end;
end;

// Destroy
//
destructor TAsyncTimer.Destroy;
begin
Enabled&#58;=False;
FTimerThread.Terminate;
// if stopped, resume
if FTimerThread.Suspended then
FTimerThread.Resume;
// wait & free
FTimerThread.WaitFor;
FTimerThread.Free;
inherited Destroy;
end;

// DoTimer
//
procedure TAsyncTimer.DoTimer;
begin
if Enabled and Assigned&#40;FOnTimer&#41; then
FOnTimer&#40;self&#41;;
end;

// SetEnabled
//
procedure TAsyncTimer.SetEnabled&#40;Value&#58; Boolean&#41;;
begin
if Value <FEnabled> 0 then begin
FTimerThread.Resume;
end;
end
else
// suspend thread
FTimerThread.Suspend;
end;
end;

function TAsyncTimer.GetInterval&#58; Word;
begin
Result&#58;=TTimerThread&#40;FTimerThread&#41;.FInterval;
end;

procedure TAsyncTimer.SetInterval&#40;Value&#58; Word&#41;;
begin
if Value<>TTimerThread&#40;FTimerThread&#41;.FInterval then begin
TTimerThread&#40;FTimerThread&#41;.FInterval&#58;=Value;
end;
end;

function TAsyncTimer.GetThreadPriority&#58; TThreadPriority;
begin
Result&#58;=FTimerThread.Priority;
end;

procedure TAsyncTimer.SetThreadPriority&#40;Value&#58; TThreadPriority&#41;;
begin
FTimerThread.Priority&#58;=Value;
end;

end.


Greetings

chronozphere
27-03-2008, 09:51 AM
I use the TApplication.OnIdle event for my rendering. Every time this loop is executed, the FPS and the frametime are calculated. FrameTime is the time needed to render a frame. I use QueryPerformanceCounter and QueryPerformanceFrequency for this. :)

And every time i move an object i do this:


Obj.x := Obj.x + 100*FrameTime;
Obj.y := Obj.y + 100*FrameTime;


So that the object is moved with respect to the time that passed. In my example, the object will move exactly 100 pixels to the right and 100 pixels to the bottom in one second. :)

Hope this helps. ;)

lordzero
27-03-2008, 02:48 PM
I use the TApplication.OnIdle event for my rendering. Every time this loop is executed, the FPS and the frametime are calculated. FrameTime is the time needed to render a frame. I use QueryPerformanceCounter and QueryPerformanceFrequency for this. :)

And every time i move an object i do this:


Obj.x := Obj.x + 100*FrameTime;
Obj.y := Obj.y + 100*FrameTime;


So that the object is moved with respect to the time that passed. In my example, the object will move exactly 100 pixels to the right and 100 pixels to the bottom in one second. :)

Hope this helps. ;)

you need specify max FPS to use this? with low machines you need skip frames?

chronozphere
27-03-2008, 03:50 PM
Nope i use this in my Engine and i didn't specify any max FPS. I should work for all FPSses, but when the FPS is really low, things might hitch a little. I don't know why you would skip frames :? ? I think you should not do that in any situation.

lordzero
27-03-2008, 04:40 PM
Nope i use this in my Engine and i didn't specify any max FPS. I should work for all FPSses, but when the FPS is really low, things might hitch a little. I don't know why you would skip frames :? ? I think you should not do that in any situation.

thanks for pointing your suggestion nathan,

just more one question..

what do u think if i put all render calls inside application.onidle and all movement inside my custom thread timer?

arthurprs
27-03-2008, 05:12 PM
you cant avoid skipping frame (you can, but the game will run slower),

you can use DetaTime (same as chronozphere suggested), the game will not run smother, but will run at the correct speed

chronozphere
27-03-2008, 08:25 PM
what do u think if i put all render calls inside application.onidle and all movement inside my custom thread timer?


You could do that, but i do think it has any advantages. You are running away from the actual problem, instead of solving it (check my sig :D ). I think it is better to make a decision. Use time-based movement with OnIdle and DeltaTime or FrameTime, or use a timer with a fixed interval. I would choose the first one. :)

It is little harder to implement, but it is rewarding in the end. ;)

lordzero
27-03-2008, 11:41 PM
You could do that, but i do think it has any advantages. You are running away from the actual problem, instead of solving it (check my sig :D ). I think it is better to make a decision. Use time-based movement with OnIdle and DeltaTime or FrameTime, or use a timer with a fixed interval. I would choose the first one. :)

It is little harder to implement, but it is rewarding in the end. ;)

what is better? DeltaTime(FrameTime) or timer with fixed interval?

These methods have large differences?

arthurprs
28-03-2008, 02:11 AM
You could do that, but i do think it has any advantages. You are running away from the actual problem, instead of solving it (check my sig :D ). I think it is better to make a decision. Use time-based movement with OnIdle and DeltaTime or FrameTime, or use a timer with a fixed interval. I would choose the first one. :)

It is little harder to implement, but it is rewarding in the end. ;)

what is better? DeltaTime(FrameTime) or timer with fixed interval?

These methods have large differences?

both will result in same, just pick what you find easier (probably a Timer)

lordzero
28-03-2008, 02:33 AM
You could do that, but i do think it has any advantages. You are running away from the actual problem, instead of solving it (check my sig :D ). I think it is better to make a decision. Use time-based movement with OnIdle and DeltaTime or FrameTime, or use a timer with a fixed interval. I would choose the first one. :)

It is little harder to implement, but it is rewarding in the end. ;)

what is better? DeltaTime(FrameTime) or timer with fixed interval?

These methods have large differences?

both will result in same, just pick what you find easier (probably a Timer)

I suspected this... I will stay with the thread timer for a while

If anyone has any suggestions post more ...

Thanks arthur and nathan :)

chronozphere
28-03-2008, 03:43 PM
what is better? DeltaTime(FrameTime) or timer with fixed interval?




You could do that, but i do think it has any advantages. You are running away from the actual problem, instead of solving it (check my sig Very :D ). I think it is better to make a decision. Use time-based movement with OnIdle and DeltaTime or FrameTime, or use a timer with a fixed interval. I would choose the first one. :)

It is little harder to implement, but it is rewarding in the end. Wink


I think that frame-based movement is better than using a timer. For example when you are running the app on a realy slow PC, and you set your timer to 60 FPS, the PC might not handle that speed. The result is that your app runs slower than you want. This is not the case when using Frame-based movement.

Offcourse, if you want to use a timer, i'm not gonna stop you. I only think that frame-based movement is more elegant. :)

For a more detailed explanation about this technique: This thread (http://www.pascalgamedevelopment.com/viewtopic.php?t=5290)

Hope it helps. ;)

arthurprs
28-03-2008, 06:07 PM
what is better? DeltaTime(FrameTime) or timer with fixed interval?




You could do that, but i do think it has any advantages. You are running away from the actual problem, instead of solving it (check my sig Very :D ). I think it is better to make a decision. Use time-based movement with OnIdle and DeltaTime or FrameTime, or use a timer with a fixed interval. I would choose the first one. :)

It is little harder to implement, but it is rewarding in the end. Wink


I think that frame-based movement is better than using a timer. For example when you are running the app on a realy slow PC, and you set your timer to 60 FPS, the PC might not handle that speed. The result is that your app runs slower than you want. This is not the case when using Frame-based movement.

Offcourse, if you want to use a timer, i'm not gonna stop you. I only think that frame-based movement is more elegant. :)

For a more detailed explanation about this technique: This thread (http://www.pascalgamedevelopment.com/viewtopic.php?t=5290)

Hope it helps. ;)

A good timer will have same result on a slow pc