Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon.

Pages: 1-4041-

timers in games...

Name: Anonymous 2006-09-30 18:16

so the only games I've written so far were all turn-based games.

I'm now going to try my hand at a real time game.  Nothing fancy, probably nothing fun either, just something to expand my know-how a little bit.  I've so far written an .md2 loader/viewer that animates the various animations of a model, but the animations are freakishly fast, more than they're supposed to be, due to the computer's speed

how do I go about starting off a game's main loop like this such that an uber-fast CPU doesn't cause everything to happen in the blink of an eye?  And what about making it somewhat tolerable to run on an underpowered computer without it bogging down the system (Quake 1 on my old old old 486 computer was playable in this regard... it seemed "real time aware" even if it was a slideshow, so an ogre would run the same speed in the world regardless of the graphics' framerate). 

I also probably want to not just make it smooth on a fast CPU but also throttled -- sure, it CAN render 300 frames per second, but I want to be able to cap that to 60fps as a maximum.

are there any reliable books I should look for that go into decent enough detail this subject?

Name: Anonymous 2006-09-30 20:18

Each time you update, use a function like gettimeofday() to determine the interval that has passed since the last update.  Use this interval when updating.

Name: Anonymous 2006-10-01 14:07

here is your basic logic

do
 x = system-timer + duration-of-one-frame
 call game-engine-one-frame
 call update-graphics-one-frame
 do : loop until system-timer > x
 loop
 
 

Name: Anonymous 2006-10-01 14:27 (sage)

do : loop until system-timer > x
good way to waste cpu?

Name: Anonymous 2006-10-01 17:07

>>4

Yeah, empty loops do waste CPU cycles.

Although I'm sure there's some blocking-based means of "sleeping" a program.

Name: Anonymous 2006-10-01 20:02

Virtually Empty loops are used so you can get better timing, you cannot expect the OS to do this for you.

Name: Anonymous 2006-10-01 20:12

>>3's method is retarded for games.  There really isn't such a thing as "one frame", rather you are rendering the state of the game world at that specific point in time.  The duration of one frame could be anything, and if the game engine update and rendering took longer than this duration, then the whole system gets fucked up and the game runs slow.  Do what >>2 said.

Name: Anonymous 2006-10-01 21:54

>>2,7
Can you get perfect realtime timing just with this kind of technique? I wonder how games like stepmania do it to get perfect timing no matter how much the graphics lag.
(not the OP btw)

Name: Anonymous 2006-10-01 23:50

Because they rely on the audio callbacks @_@

Name: Anonymous 2006-10-02 3:01

>>8
The timing is as close as you can get to perfect, depending on the accuracy of the function used to get the time.

Name: Anonymous 2006-10-02 3:43

You may want to kill time though, if, for example, you want to limit the engine to do 60 fps, and right after finishing a frame you notice there's still a (relative) lot of time before the next frame. In that case, you can either wait passively or wait actively. If you wait passively, you allow other processes to run or save CPU power/heat, but it's not guaranteed that you'll wait exactly the time you want. If you wait actively (by killing cycles), you'll more likely wait what you want, not always exactly due to task switching, but it'll be most accurate, but you'll be wasting CPU. A middle way could be to wait passively for 80%-90% of the time, then wait actively for the rest.

Name: Anonymous 2006-10-02 4:38

revised version:

fork(CGSUD) 'stands for continuous game state update daemon - this program would do nothing but continously update game state
do
 x = system-timer + duration-of-one-frame
 call pause-CGSUD
 thisState = fetchCGSUDstate()
 call restart-CGSUD
 call makeAllGraphicsFor(thisState)
 if system-timer < x
  blockUntilTimeExpires(x - system-timer)
  end if
 loop

Name: Anonymous 2006-10-02 9:14

OP - search comp.games.development.programming.misc for "frame rate", there's a wealth of information about this topic there

Name: Anonymous 2006-10-02 12:03

>>12, use CamelCase, not shitCase

Name: Anonymous 2006-10-02 12:21

>>14
NO, USE UPPERCASE!

Name: Anonymous 2006-10-02 12:31

>>12
Still retarded, if you try to make each frame 1/60 of a second, and the user's computer can only get 30 fps, then the game is going to go half as fast as it is supposed to.

Name: Anonymous 2006-10-02 13:36

>>16
No you fucking dipshit.  Read the pseudocode more carefully.

 if system-timer < x // <- IF WE HAVE TIME LEFT OVER TO WAIT
  blockUntilTimeExpires(x - system-timer) // <- THEN WAIT
  end if <- OTHERWISE DON'T WAIT

Name: Anonymous 2006-10-02 14:58

>>17

this made me lol.

Name: Anonymous 2006-10-02 19:49

>>17
Guess what happens if you don't have time left over to wait, fuckwad.  Then you only updated the game for a 1/60 sec interval even when something like 1/30 sec has passed since the last update, and the game gets all fucked up.

Name: Anonymous 2006-10-02 19:54

>>19

Maybe that was his intention. One game I can think of off the top of my head that works like that is GTA2.

Name: Anonymous 2006-10-02 20:56

>>19
Not really because CGSUD is a *background process*.  MULTITHREADING MOTHERFUCKER, DO YOU USE IT?

Name: Anonymous 2006-10-03 0:02

>>20
If it was intentional, then it is an extremely bad idea.

>>21
In that case, this system is completely retarded.  If you are updating slower than the defined interval, then CGSUD will never be able to run because the system will keep pausing and restarting it.

Name: Anonymous 2006-10-03 1:14

>>22
Hmm, how about this.  Forget CGSUD, put the graphics renderer in the background, since it's less important that we update the graphics rather than update state.

So, I guess since you want to keep the graphics render and state updater as busy as possible, we need to introduce a pipelining system.  So, here it is, you idiots:

struct gameState[2] // 0, 1, and 2
bool beingUpdated[2], beingRendered[2], validToRender[2]

fork screenupdatedaemon(beingUpdated, beingRendered, validToRender)
do
 for (int x=0, x=3 || !beingRendered[x], x++);
 beingUpdated[x] == true;
 updateState(&gameState[x]); validToRender[x] == true;
 beingUpdated[x] == false;
loop

void screenUpdateDaemon(&beingUpdated, &beingRendered, &validToRender, &gameState)
do
 x = system-timer + duration-of-one-frame

 for (int x=0, x=3 || (!beingUpdated[x]) && validToRender[x] , x++);
 beingRendered[x] == true;
 drawPrettyGraphicsForThisState(gameState[x]);
 beingRendered[x] == false;

 if system-timer < x
 blockUntilTimeExpires(x - system-timer)
 end if
loop

my c is rusty but you get the general idea

Name: Anonymous 2006-10-04 23:38

>>12's way is still the correct way, except if you have NEGATIVE time left to wait, then you may want to skip rendering the frame. if your game state update takes longer than a frame, then the game SHOULD run slow.
>>2's way is ideal, but not possible unless you can parameterize every bit of your game state as a function of time elapsed. (which can only be done in really simple cases, not actual games)

>>23
this is confusing :(

Name: Anonymous 2006-10-05 3:10

i probably expressed it in >>23 confusingly but basically, here is what i was trying to express programatically:

- the game state updater and screen updater can both update game state, but since they are not running one-after-the-other it must be made where either one can work on a copy of the game state (because the render may not be done with one copy or the updater may not have completely updated a copy yet)
- so we need a pipeline, with X slots, each slot holds a complete gamestate, and must be accessible to both updater and renderer.
- there are two processes running paralellely (is that how you spell that?).  the updater is running as fast as possible, filling the pipeline with fresh states.
- probably not totally correct to call this a pipeline though, as if we run out of slots in the pipeline, the updater can overwrite a slot as long as the renderer isn't using it.  this way the updater does not have to ever wait for renderer.  also means we need a min of 3 slots in our "pipeline."
- so renderer runs as background process.  renderer will look at status of slots and only take one that isn't being updated.  renderer will lock that slot.
- if slot is locked by renderer, updater skips that slot and searches for other free slot, or oldest non-locked slot if all slots are full.
- renderer draws that state and then waits for next frame if it needs to.

T H R E A D    O V E R

Name: Anonymous 2006-10-06 19:30

>>2's way is correct, and is the way most games work.

You need to decouple the rest of your game logic from the rendering/framerate.

>>23's is on the right track to one way of doing this, but it still doesn't actually address the original problem.

You need to take into account how much time has passed since the last time you updated the game state when you're moving or animating things, otherwise there is no way you're going to be able to control how fast those things are happening independently of framerate.

Usually it's done by calling the update gamestate function with a "time since last update" value, which allows you to work out how far along your animation/movement/whatever is supposed to be. So, if the framerate of your game slows down, the actual speed of the game doesn't, and if the framerate speeds up, again, it doesn't affect the actual speed of the game.

Multithreading is completly not required to solve this problem, and infact for a simple game I can almost guarantee that it will cause more problems than it's worth. This is why almost no games on the market are actually multithreaded, though they are slowly being forced to as CPUs are starting to expand with more cores instead of more speed. (imo, message-passing concurrency is the solution here, every game object on it's own microthread, whee!, but that's for another thread)

Name: meeelting 2006-10-06 20:12

Scale your update to the FPS by measuring the computercycles.

This is how to measure your fps using cycles; i presume you use C++ like a serious gameprogrammer.

in h file:
#include <algorithm>

variables used:
__int64 freq, last_tick;
double seconds;

put this in your constructor:
QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
QueryPerformanceCounter((LARGE_INTEGER*)&last_tick);

use this function

int Gfx::getFps()
{
    __int64 tick;
    QueryPerformanceCounter((LARGE_INTEGER*)&tick);
   
    seconds = (tick - last_tick) / double(freq);
    last_tick = tick;

    thisframe = GetTickCount();
    ff++;
    if((thisframe-lastframe) > 1000) {fps=ff; ff=0; lastframe=thisframe;}
    return fps;

}

gl hf

Name: meeelting 2006-10-06 20:13

i forgot to mention theres some junk in that code; im sure you can sort it out.

Name: Anonymous 2006-10-26 17:02

is there a common timer system call/function found on both Windows and Linux/Unix systems?  I can't seem to find a common one that's found on both.

I know the GLUT framework has its own timer for platform-independent millisecondGET but I am not using GLUT.

Name: Anonymous 2006-10-26 17:15

Name: Anonymous 2006-10-26 17:15

Ruby

Name: Anonymous 2006-10-26 17:43

>>30

Hmm no, fails.  Resolution only goes down to seconds, not down to milliseconds.

Name: Anonymous 2006-10-26 18:05

>>32

NO U!

Scroll down.

Name: Anonymous 2006-10-26 18:46

>>33

What am I supposed to be looking for?  The clock() function?  Guess what:

 APPLICATION USAGE

    In order to measure the time spent in a program, clock() should be called at the start of the program and its return value subtracted from the value returned by subsequent calls. The value returned by clock() is defined for compatibility across systems that have clocks with different resolutions. The resolution on any particular system need not be to microsecond accuracy.

    The value returned by clock() may wrap around on some systems. For example, on a machine with 32-bit values for clock_t it will wrap after 2147 seconds or 36 minutes.

LOL... that will certainly fuck things up.

From what I found from Googling, timers are very OS and hardware specific.  I'll just have to write a transparent Timer wrapper class of my own and finish this program in Windows... then when it's finished and I port it I can change the internals of the Timer class to whatever other platform I port it to without having to change much else elsewhere in the program.

Name: Anonymous 2006-10-26 18:49

Ah nevermind, I'm writing a shmup, not an FPS.  No level is going to take longer than at most 3 or 4 or 5 minutes to complete.  I'll stick with clock.

Still gonna encapsulate it in a Timer class just in case I feel like changing anything...

Name: Anonymous 2006-10-26 18:56

And... nevermind again, clock() is horribly inaccurate, going by my test I ran.  Seems to have an average error of ~15-20ms inaccuracy, which will make animation potentially choppy.

Time to hit the library

Name: Anonymous 2006-10-26 18:56

>>35

you need a proper timer to interpolate between the fps anyway, or its gonna run differently on different machines

Name: Anonymous 2006-10-26 20:10

>>34

l2read

Name: Anonymous 2006-10-27 4:15

After some more experimentation, timeGetTime seems to have the most decent granularity to its precision.  And it doesn't fuck up on my HT system the way QueryPerformanceCounter did (and no, setting process and  thread affinity to 1 CPU/virtual-CPU didn't fix that).  Aaand the models are animating fluidly and at a normal speed!  Well, their skeletons are... tomorrow comes the skinning, muahaha

Name: Anonymous 2006-10-27 9:11

RTDSC FTW

Name: Anonymous 2006-10-27 21:01

Yeah I read about RTDSC right before I went to bed.  But I also found out this morning that SDL, which I'm using, has a timer routine of its own, and it too is pretty accurate, so I'll stick with it for now.

Name: Anonymous 2010-11-14 13:36

<

Name: Anonymous 2011-01-31 20:51

<-- check em dubz

Name: Anonymous 2011-02-03 1:04

Name: Anonymous 2011-02-04 15:06

Name: Sgt.Kabu�哬kiman錱싀 2012-05-28 20:19

Bringing /prog/ back to its people
All work and no play makes Jack a dull boy
All work and no play makes Jack a dull boy
All work and no play makes Jack a dull boy
All work and no play makes Jack a dull boy

Don't change these.
Name: Email:
Entire Thread Thread List