Is this a good implementation of the independent FPS game cycle?

Currently, I have something close to the next implementation of the FPS independent game cycle for physics-based games. It works very well on every computer on which I tested it, maintaining the speed of the game when the frame rate drops. However, I'm going to port the embedded devices, which are likely to fight the video more diligently, and I wonder if it will still cut the mustard.

edits:

For this question, suppose msecs () returns the elapsed time, in milliseconds, that the program started. The implementation of msecs is different on different platforms. This cycle also works differently on different platforms.

#define MSECS_PER_STEP 20 int stepCount, stepSize; // these are not globals in the real source void loop() { int i,j; int iterations =0; static int accumulator; // the accumulator holds extra msecs static int lastMsec; int deltatime = msec() - lastMsec; lastMsec = msec(); // deltatime should be the time since the last call to loop if (deltatime != 0) { // iterations determines the number of steps which are needed iterations = deltatime/MSECS_PER_STEP; // save any left over millisecs in the accumulator accumulator += deltatime%MSECS_PER_STEP; } // when the accumulator has gained enough msecs for a step... while (accumulator >= MSECS_PER_STEP) { iterations++; accumulator -= MSECS_PER_STEP; } handleInput(); // gathers user input from an event queue for (j=0; j<iterations; j++) { // here step count is a way of taking a more granular step // without effecting the overall speed of the simulation (step size) for (i=0; i<stepCount; i++) { doStep(stepSize/(float) stepCount); // forwards the sim } } } 
+4
source share
1 answer

I just have a few comments. Firstly, you do not have enough comments. There are places where itโ€™s not clear what you are trying to do, so itโ€™s hard to say if there is a better way to do this, but I will point to them when I come to them. Firstly though:

 #define MSECS_PER_STEP 20 int stepCount, stepSize; // these are not globals in the real source void loop() { int i,j; int iterations =0; static int accumulator; // the accumulator holds extra msecs static int lastMsec; 

They are not initialized by anything. They will probably appear as 0, but you should initialize them. In addition, instead of declaring them as static, you might want to put them in a structure that you pass to the loop by reference.

  int deltatime = msec() - lastMsec; 

Since lastMsec not (initialized and probably 0), it probably starts as a big delta.

  lastMsec = msec(); 

This line, like the last line, calls msec . This probably means "current time" and these calls are close enough that the return value is probably the same for both calls, which is probably also what you expected, but still you call this function twice. You should change these lines to int now = msec(); int deltatime = now - lastMsec; lastMsec = now; to avoid calling this function twice. At the moment, getting functions is often much higher than you think.

  if (deltatime != 0) { iterations = deltatime/MSECS_PER_STEP; accumulator += deltatime%MSECS_PER_STEP; } 

You should have a comment here that says it does, as well as the comment above which means what the variables mean.

  while (accumulator >= MSECS_PER_STEP) { iterations++; accumulator -= MSECS_PER_STEP; } 

This loop requires comment. It also should not be. It looks like it could be replaced with iterations += accumulator/MSECS_PER_STEP; accumulator %= MSECS_PER_STEP; . Separation and module must be performed at shorter and more sequential times than a cycle on any machine with hardware separation (which many do).

  handleInput(); // gathers user input from an event queue for (j=0; j<iterations; j++) { for (i=0; i<stepCount; i++) { doStep(stepSize/(float) stepCount); // forwards the sim } } 

Performing steps in a loop regardless of input will result in the game not responding if it does slow work and is lagging. It seems, at least, if the game is behind, all the input data will begin to be added and executed together, and all the game time will pass in one piece. This is a less graceful way to fail.

Also, I can guess what j (external loop) means, but the inner loop is less clear to me. also, the value passed to the doStep function is what it means.

 } 

This is the last brace. I think it looks lonely.

I donโ€™t know what happens if you name your loop function, which may be out of your control, and this may dictate what this function does and how it looks, but if not, I hope that you will review the structure. I believe that the best way to do this is to have a function that is called repeatedly, but with only one event at a time (regularly released in a relatively short period). These events can be either user input events or timer events. User input events have just been configured to respond to the next timer event. (when you do not have any events for sleep processing)

You should always assume that every timer event is processed in the same period, although there may be some drift if the processing is behind. The main oddity that you can notice here is that if the game lags behind the processing of timer events and then gets caught again, the time in the game may slow down (below real time), then accelerate (to real time) and then slowly back off ( in real time).

Ways to solve this problem include only one timer event, which will be in the event queue at a time, which will slow down time (below real time) and then speed up backups (up to real time) without an ultra-fast interval.

Another way to do this, functionally similar to what you have, would be the last step in processing each timer event in the queue of the next timer event (note that no one should send timer events (except for the first) if thatโ€™s how you decide to implement the game). This would mean getting rid of the regular time intervals between timer events, as well as limiting the ability to sleep in the program, because at least every time the event queue was checked, the timer processing should be processed.

+6
source

All Articles