• Advertisement

Archived

This topic is now archived and is closed to further replies.

Game timing

This topic is 6449 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

hi, How do you guys set your games to run at the same speed on all systems ? I usually stick to the monitor''s frequency, gives me a rock solid/smooth top framerate and the game keeps the same speed on all other systems. Here''s my problem, whenever the user has a different default refresh rate other than 60hrz at 640x480, let''s say 75hrz, his game will run faster ( of course ). I''m not worried about lower end systems that might not be able to sustain the top framerate. I''ve tried sxengine''s timer wich allows you to specify a target framerate, well, it''s not as smooth as syncing to the monitor''s refresh(not even close), soo i ditched that angle. I guess the solution i''m looking for is to use the function SetDisplayMode(width, height, bpp, refresh rate, 0); in the IDirectDraw7 Interface. With that one you can specify a refresh rate. I wouldn''t go any higher than 60, and at least i would know everyone is maxing at 60fps. Problem is, i don''t know how to initialise it with DelphiX. Help ? How is everyone else keeping game the same speed ? Any other tip would also help. Thanks, Regards, Gunner. Sysimage inc.

Share this post


Link to post
Share on other sites
Advertisement
Frameskiping... plain and simple. Look how MAME and other emulators "fake" 60 FPS on 75Hz and 85Hz by drawing some frames twice (too fast) and not drawing others (too slow)



[ turbo | turbo.gamedev.net ]

Share this post


Link to post
Share on other sites
Use either GetTickCount() or QueryPerformanceCounter().

GetTickCount() is precise to about 1/1,000 of a sec.
QueryPerformanceCounter() is around 1/1,000,000 I think.
Use QueryPerformanceFrequency() to get the real number.

Here''s what I do with them.
First I calculate the framebudget, or the number of "ticks" allowed for each frame. For example, if useing GetTickCount, and you want 60 fps:

framebudget = 1000/60

So it''s about 16 thousanths of a second per frame.
Now I establish the time the frame starts:
framestarttime = GetTickCount();

Now immediately start the rendering. When the rendering is done, I check to see if the rendering process took too long (more than 16 ticks) by calling GetTickCount() again. If it''s done before hand, like it took 6 ticks for example, then I loop in a delay until the following is true:

(GetTickCount-framestarttime) >= framebudget

This is just what I personally do, I don''t know how anybody else here likes that method...it''s not capable of skipping frames, it''s just a speed limiter(although it wouldn''t be hard to implement a frame skipper inside of this).
I''m actually curious to see if anybody here knows any flaws with it??? Let me know, I always want to improve!


When life hands you lemons, throw them at God's head.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
The above post is correct. That is pretty much the standard that I have seen... However, good practice is to do any extra AI routines inside that loop....


DO
{
// Cycle through AI Routines here and do one per check... be sure
// not to make the AI Routines too complex or you will miss your
// check again
}
while(GetTickCount() < (StartTime - 30)) //??? or whatever

Enoch Dagor

Share this post


Link to post
Share on other sites
Hi there, the best idea I''ve heard yet (Which I have not implemented yet) is to have 2 threads, one painting everythin as fast as the darn computer can go (60fps, 75fps, 120fps?) However the second thread is respnsoble for updating the units, this thread will use some timer functions to stick to moving units I don''t know like 10pixels per second.. Just an example, of course you could have other units running much faster!
This works great cause you get the maximum fps but will still have the same speed on all computers.
Now here''s the only catch, if they run it on a system WAY below your requirement''s, like let''s say they have an old video card on a K6-2 400Mhz, so an OK computer but a crap card. Well the framerate will max out on the card at like 10-20 fps and your units will still move 10 pixels per second, so then it starts to get choppy, but it''s still running at the same speed.

Hope that helped some!
See ya,
Ben

Share this post


Link to post
Share on other sites
wow, thanks all for your input !

I'm going to try those approaches, looks pretty good.

Now i guess i won't be able to frag my friend anymore with our little network sniper game, i had my refresh at 80hrz and he had his at 60. i kept telling him his reflexes sucked, ahrm well, after this fps fix with the methods suggested above, it might another story all together

Thanks again.

Gunner.
Sysimage inc.

Edited by - Gunner on June 23, 2000 3:17:02 PM

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Well there is a more better way.

//Get the lastime with this functions
QueryPerformanceFrequency(Last_Time);
Time_Scale := 1.0 / Last_Time;

In every frame you get the new time and calculates the elapsed time

QueryPerformanceCounter(Current_Time);
Time_Elapsed := (Current_Time - Last_Time) * Time_Scale;
last_time := current_time;


Now you have a number (time_elapsed) which gives you the delay between every frame. In your move function you multiply all your movements with this number (time_elapsed). That''s the best way. Your game will be run at every fps (400 FPS or 2 FPS) with the same speed!

Neo
www.neobrothers.de

Share this post


Link to post
Share on other sites
    
DWORD start_time;

while(!done) //game loop;

{
start_time=GetickCount();

//stuff

while(GetTickCount()-start_time<33); //30 frames perscond (can be changed)
}


JoeMont001@aol.com

Share this post


Link to post
Share on other sites
Hey all.

To expand on the frameskipping thingamy.

Heres code snippet #1.

*************************************

Procedure CalcTiming;
begin
TimeInMilLast:=TimeInMil;
TimeInMil:=timegettime;

if TimeInMil-timeinmillast>2000 then
TimeInMilLast:=TimeInMil; {saftey precaution}

Timepassed:=Timepassed+(TimeInMil-TimeInMilLast);
SecondsLast:=Seconds;
Seconds:=trunc(Timepassed/1000);

if Seconds<>SecondsLast then begin {new Second}
TicksPerSecond:=Ticks; Ticks:=0;
FramesPerSecond:=Frames; Frames:=0;
end;

TickTimeLast:=TickTime;
TickTime:=trunc(Timepassed/(1000/GoalTicks));
end;

*************************************

*Gosh I hope the messageboard doesn''t chew that up.

Ok. So we have some interesting variables here. This procedure is expecting to be called from within a high resolution timer, maybe set to 0 or something. (1000 times a second).

*************************************

FramesPerSecond <= Will always contain the current FRAMERATE
TicksPerSecond <= Will always contain the current TICKRATE

GoalTicks <= Set this to 60 if you want 60 ticks per second.

And the rest are just Floats/Singles/whatevers.

*************************************

Alright - so you''re timer code would look somthing like this.

********************************************

procedure YourTimerFiringOffThingy;
var
MainControl:Integer;
HaveTicked:boolean;

begin

HaveTicked:=FALSE;

CalcTiming; {defined up top there}

for MainControl:=1 to (Ticktime-ticktimeLast) do
begin
UpdateInput;

Ticks:=Ticks+1;
HaveTicked:=TRUE;

ProcessGameLogic;
{This is what you want to happen TICKRATE
number of times per second}

end;


if (HaveTicked) then RenderScene;
{Whatever is inside RenderScene
should only be related to rasterizing graphics}

end;


********************************************

And that, my friends, it that. Things to note are that A) This routine is only really useful if you''re engine is capable of processing the game logic at the specified TICKRATE on all target platforms (think QUAKE). B) That you''re RenderScene is designed to be the slower and more expendable portion of you''re engine.

But Im pretty sure that''s what everyone was after, anyway. This chunk of code was actully posted about a year ago on the old turbo forums, but, minus one little conditional check to see if the logic had actually been updated before re-rendering a pre-existing frame. You could use a check just after this bit of code, to see if HAVETICKED is still false, and yes, expand you''re AI''s path searching abilitys by 1 unit the next process. If it''s not, do nothing, if the While loop gets executed more than once, then scale back the AI. Actually, what would make more sense, is to scale the number of TICKS between updating the collision detection between objects (capping it of course). Or, whatever you like.

Just after doing some tests, if you''re TICK processing takes too long, (ie collision detecting 200 odd objects per TICK - sigh - which is what Im currently facing) - it will still maintain the underlying World Speed. Bet you''re glad to hear that huh?

Anyway - Nuff outta me, I realize Im only expanding on what was already said - I just thought It might be useful for me to post a full framework up here for ya''s.

Back off to C++ land (sigh) .

#27

Share this post


Link to post
Share on other sites
Actually - just some more things - Im pretty sure the above bit of code qualifies for "one of those things that looks like it shouldn''t work, but does" - things. All those variables up top are supposed to just be globals initialized to 0, except for the GoalTicks variable - which you should set to 30, 60, 200 or whatever. Also - the call to TimeGetTime comes from the MMSYSTEM unit, just in case you didn''t stumble across that one already.

I encourage anyone interested in this sort of thing to try it out and see =) Im kinda proud of it - but the one thing that irks me still, is that seeing it is basing the prediction for the next TICK upon whether the last FRAME ran overtime, quickly flicking between high intensity scenes and low intensity scenes causes slight waverings in the TICKRATE. But Im just being picky. If anyone else has a different approach, I''d love to hear it. (the whole thought of using of multiple threads, one to render, one to Logic, bothers me immensely. Ever heard of LockUP? )

Later.

#27



Share this post


Link to post
Share on other sites
Could reply a long ago but was too busy. Although I got my way of getting same speed on different refesh rates, I don''t think it''s useful to publish it - you got plenty anyway - mine will just mess up the thread ;-)
I heard an idea of using two threads - one for drawing and another for everything else. I used the exact method before until I found problem on fast machines:
When (in thread) you render everything - you use Rendering routines (let''s suppose from DelphiX) from MAIN THREAD. Again, when you use any render-independent processes (let''s say AI or network code) - you also use routines from MAIN THREAD (unless your networking component runs in its own thread). Now let''s assume your great accelerator card allows 85 Hz refresh rate (and some GREATEST card can even do 120) you use routines from main thread a lot. As a result, your main thread is occupied all the time so there''s NO POSSIBLE WAY to respond to key command (or to windows commands), etc. What''s more - your TIMING FUNCTIONS (to calculate ticks, etc) are also ran in main thread!!! So you completely block your application as well as your threads. Another thing - as you render and process your data your both threads may block each other reading and writing data, or at least (if you used critical sections) your processing or/and rendering engine will jerk and glick. I tried this and it "worked" ...
The solution: don''t do it
My suggestion: use threads only for internal processes that doesn''t use main thread a lot (remember - most VCL routines use it!).
I know my post doesn''t help to answer the question - it''s just a personal experience I wanted to share with.

Share this post


Link to post
Share on other sites
LP: Still good to know

Those solutions where pretty easy to put into my program.

Thanks all for your help !

Gunner.
Sysimage Inc.

Share this post


Link to post
Share on other sites

  • Advertisement