game design, framerate architecture..

Pages: 12
So I've been mulling this around my head lately and a post earlier got me thinking further.

I had planned to have an infinite loop in main() which contains all the game calculations, taking of user inputs etc, then a 'frame' output, and then a sleep of however milliseconds I want the game to run at, eg. 30fps = .033s sleep.
But I realized this is pretty dumb; what if the calculations take longer? does that mean Ill only handle 1 input per .033 seconds? the game would never truly run at 30fps..

So I saw someones post about using CPU ticks, and cpu ticks per second since this number would vary from pc to pc, to then calculate the output frames per second.
So hypothetically, before the loop in main() we establish the number of ticks per second, and then poll the current tick at the start of the loop, and then end after the output, and use the difference to get a gauge of how many frames per second the game is running at.. now this is completely wide open at this point, so the fps could run wild into the thousands with a stupidly simple game right?

So at that point we have to think of how to put a limit to this and the only thing I can think of is to use the establish ticks per second number and the number of frames per second we want the game to run at, and at the end of each loop, if the number of ticks elapsed is less than the minimum allowed we have to dwell for a certain number of ticks. On the other hand, if the number of ticks elapsed is MORE than minimum, we end up running at a lower frame rate. this makes sense because any game ive ever played always started a low frame rate while everything loaded, lots of calculations and stuff going on, makes sense right? then I guess a little bit more math would go on to then output the actual frame rate..

But what I wonder is, would any of this math that has to go on to maintain and output the framerate actually affect it itself?

Am I going about this the right way?
Best way to handle this situation is to begin coding a clock class which uses <time.h>. I've made one myself that supports frame limiting and stop-watch (elapsed time) functionality.
What you want to do is at the beginning of each frame update the time then after each frame (rendering/logic/etc) limit the speed (soak up time) of the game to your desired fps. Just begin coding you seem to be in the right direction.
You should define your game's animation pace in a unit that is independent of factors such as CPU strength, the amount of calculations required, etc. . As you write your game, aim to measure all your animations in seconds.

Then, you need to write a timer class that will keep track of the amount of time that has passed from one completion of the game loop to the next, and will convert that to seconds. This will give you a measurement so that your animations can advance for that frame according to the speed you want them to have.

Also: the timer class should involve a frame limiter. What this does is that it does not necessarilly render a frame for each game loop completion. If you set your frame limiter to 60 frames per second (which means that you shouldn't render more than one frame per 1/60th of a second), and your timer calculates that at the moment the elapsed time between loop completions is less that 1/60 of a second, the frame limiter would allow the performance of all calculations such as game logic, handling of user input, etc , but would not render a frame for that loop -- since the frame limit of 60 frames per second is reached. The rendering process would be skipped for that loop, and in the next iteration the loop would once again check the time that elapsed. If it is equal or more than 1/60th of a second, then the frame would be rendered.

Also take a look at this thread, I mentioned the use of QueryPerformanceFrequency() and QueryPerformanceCounter() and also gave a link with some examples of implementations.

http://www.cplusplus.com/forum/general/90857/

Last edited on
I would consider writing a separate function that draws the screen and using main to initialize.
However, as said above, time.h is probably one of the best libraries to use in this situation.
On the other hand, if you use DirectX or OpenGL like nobody else does, all will be confused.
So, use time.h for checking if the time is equal to or under a second in a for loop, and draw the frame each iteration. Print the iteration number, and you should, theoretically speaking, have your frame-rate.
Of course it probably works differently. I haven't tested this myself.
Worth a try though.
You should also consider a FPS cap, otherwise there may be no memory left for game logic.
On that hand, the for loop might want to call a function that does the game logic for you. I wouldn't recommend using main yet again as main is mainly used for initialization. Also, consider a different function for each object you want to render if using OpenGL or DirectX.

If you're using ASCIII, which is quite unlikely, good luck with that.

Make sure not to mindlessly write everything in main.
Sorry, I don't read all your posts, but I guess you want to build up a FPS counter, right? :)
Many thanks for the replies Ogoyant and Computa.

Ogoyant, I believe you're the person who I initually referred to who mentioned the QueryPerformance functions.

I had thought of an frame limiter in the way you speak, but this instance had occurred to me:
Say the game is running through loop n, and the timer function determines that it ran the calculations in < 1/60 of a second (aiming for 60fps max), so it decides not to render the frame and instead jump to game loop n+1.
Now, in loop n, we performed everything just under 1/60, or 0.016s, let's say we did it in 0.012. Lets imagine loop n+1 also runs in 0.012s: logically we would have some sort of logic to draw the frame for sure since the previous wasnt drawn, but this brings up an issue; we just spent .024s to draw 1 frame, aka we dropped our frame rate to 41fps, not capped it.

This is where my mind goes a blur and I imagine some sort of mutlithread system where one does the drawing of whatever game data is currently available, and another does all the game calculations.

Computa:
I'll definitely be looking into time.h, and yes I am working with ascii, Id like to stick with the standard libraries and the inclusion of some wincon.h functions. Im writing this purely as a language learner, i dont intend for any cross compatibility, but that said, Id like to learn the really nitty gritty details of things rather than utilizing a library that has done all the thinking for me and all I have to do is link pictures and tell them what to do.. enh, thats a whole other subject :)

I had assumed my main to have a switch case statement, one for loading animations (logo etc), a second for a main menu, a third for the actual game running, and a fourth for ending credits.. each case would jump other "main" functions with their own sets of cases, ie, where and waht each menu item does, within the actual game, jumping to inventory, caption "asides" (ie, enter a lair, screen jumps to a close up of the chars face saying something, then jumps back to game)..

Its a long ways a way and Ive still got a lot of learning but its my goal xD

in the mean time Ill keep working my way through thinking in c++ and imagining how to make this game. At the moment I know how Im going to create the character objects dynamically, and im trying to get some grasp of the drawing logic right now as you can see.. im sure itll all change once I learn more and more :)
Imagine each frame as a set of characters in an x,y array.
1
2
3
4
5
6
7
8
9
cout << "________________
        |     Hello!     |
        |    ********    |
        |                |
        |                |
        |                |
        |                |
        |                |
        |________________| 

gtm wrote:
Say the game is running through loop n, and the timer function determines that it ran the calculations in < 1/60 of a second (aiming for 60fps max), so it decides not to render the frame and instead jump to game loop n+1.
Now, in loop n, we performed everything just under 1/60, or 0.016s, let's say we did it in 0.012. Lets imagine loop n+1 also runs in 0.012s: logically we would have some sort of logic to draw the frame for sure since the previous wasnt drawn, but this brings up an issue; we just spent .024s to draw 1 frame, aka we dropped our frame rate to 41fps, not capped it.

This is where my mind goes a blur and I imagine some sort of mutlithread system where one does the drawing of whatever game data is currently available, and another does all the game calculations.

You're welcome gtm. In the case where your n loop doesn't render a frame, you should not update the "prevTime" variable that you keep for storing the time that the last frame got rendered. So, in loop n+1, your currentTime-prevTime calculation will give you the time since the last frame you rendered, not since your last loop (being n). So you would get a rendered frame in n+1 of your example.

Indeed, in the case of your example, the elapsed time would be greater than 0.016s for n+1. However, your frames for that second wouldn't be 41, because that would assume that you are rendering a frame every 0.024s at the least. But, instead, what happened is that you rendered 0.008s later than the shortest possible amount of time (on average, since in practical application you see that we exceeded it in your example) you intended to differentiate your frames by. Which is a very small amount of time (and also keep in mind that most loops will complete much faster, and realize that they have exceeded their 1/60th of a second much faster). Keep in mind that animation over 24 frames per second is considered more-or-less "smooth", and the additional frames are added to make the overall result even more seamless, and also (in the case of real-time rendering) to account for abrupt changes in computational requirements like loading new environments, calculating a considerable amount of NPC behavior, etc. . So setting your frame limit at exactly 60 would involve the possible loss of the amount of time your game loop requires to complete one single iteration, and realize that it has exceeded the 0.012s limit set. The difference is negligible in effect though. Try it out and see the visual and interractive effect. The 60 fps limit essentially is set in that value to compensate for this occasional staggering without creating visual discomfort. You may want to set it a little higher if you wish, although keep in mind that your monitor also has a physical refresh rate that your program cannot exceed.

I don't think there's any need for multithreading for this purpose, I suggest you try out making a test application and test frame limiting at 60fps. You may notice that your fps counter might report something like 59-60fps, as it updates every second. This is the result of what you describe above. Try it out to test the actual effect, though.

Also, you should parametrically update your animations based on seconds to ensure that they update smoothly, because the actual frames per each second will vary, and this variation needs to be reflected (parametrically) to the per-frame updates of the animation. If you're doing 3D graphics, or rectangles moving across the screen, interpolate through your animation curves for the elapsed time since the last rendered frame. If you're doing 2D pre-rendered spritesheets, update the sprite based on it's own assumed frame-rate (e.g., if your spritesheet is designed for 30 fps, having one explicit sprite for each of those frames, and your app has a 60 fps limit, update the spritesheet after every 1/30th of a second, so that your animation of your character is timed correctly and as originally designed/intended).
Last edited on
Thanks for the info. As I said Im going to be working with ascii, I just figured out the drawing routines per the winapi and got this fun little program working, im quite proud of myself having only been at this around 2 weeks in my spare time :D

Now to just clean things up, and add in that timer function. I did notice on larger console size settings that it began to lag a bit.. interesting for something so simple to say the least.

Sends a character X and trailing dots... bouncing around off the edges of the set screen, was more or less figuring out how to draw what I want and update the screen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include <iostream>
#define _WIN32_WINNT 0x0500
#include <windows.h> // WinApi header
using namespace std; // std::cout, std::cin


int main() {
//set handles & waitBuffer
HANDLE hBuffer1, hBuffer2;
hBuffer1 = GetStdHandle(STD_OUTPUT_HANDLE);

//Set Window Title
SetConsoleTitle("go johnny go, go");

//Set Buffer Size, Then we can set the window size
int conX = 40;
int conY = 10;
COORD newSize;
newSize.X = conX;
newSize.Y = conY;
SetConsoleScreenBufferSize(hBuffer1, newSize);
//Set the window Size based on buffer sizes-1
SMALL_RECT newWinSize;
newWinSize.Left = 0;
newWinSize.Top = 0;
newWinSize.Right = conX-1;
newWinSize.Bottom = conY-1;
SetConsoleWindowInfo(hBuffer1, TRUE, &newWinSize);
///////////////////////////////////////////////////////////////////////////////
//Write Buffer Info::
int arraySize = conX * conY;
CHAR_INFO writeArray[arraySize];
COORD writeArrayBufferSize;
  writeArrayBufferSize.X = conX;
  writeArrayBufferSize.Y = conY;
COORD bufferUpperLeftCell;
  bufferUpperLeftCell.X = 0;
  bufferUpperLeftCell.Y = 0;
SMALL_RECT bufferWriteLocation;
  bufferWriteLocation.Left = 0;
  bufferWriteLocation.Top = 0;
  bufferWriteLocation.Right = conX-1;
  bufferWriteLocation.Bottom = conY-1;
  
//Initialize writeArray
for (int i = 0; i < conX; i++){
  for (int j = 0; j < conY; j++){
    writeArray[i + conX * j].Attributes = 0; //black on black
    writeArray[i + conX * j].Char.AsciiChar = 0x20; //0x20 is ascii space char
  }
}

////MAIN FUNCTION
BOOL firstRun;
COORD trailCoord = {0,0};
COORD trailCoord2 = {0,0};
COORD ovrWriteCoord = {0,0};
COORD prevCoord = {0,0};
COORD curCoord = {0,0};
//Direction Cases: 1 down-right, 2 up-right, 3 up-left, 4 down-left
int curCase = 1; //starting from top left, down-right is starting case
//within each direction case we must check for 3 seperate cases, max/min x, max/min y, and corner

while(true) { //infinte loop
  if(firstRun == TRUE) {
    writeArray[prevCoord.X + conX * prevCoord.Y].Attributes = 15; //white on black bg
    writeArray[prevCoord.X + conX * prevCoord.Y].Char.AsciiChar = 'X'; //set char X at top left
    firstRun == FALSE;
  }else{
    ovrWriteCoord = trailCoord2;
    trailCoord2 = trailCoord;
    trailCoord = prevCoord;
    prevCoord = curCoord;
    switch(curCase) {
      case 1: //down-right
        if(prevCoord.X == conX-1 && prevCoord.Y == conY-1) { // bottom right corner
          curCoord.X = prevCoord.X - 1;
          curCoord.Y = prevCoord.Y - 1;
          curCase = 3; //up left
        }else if (prevCoord.X == conX-1) {
          curCoord.X = prevCoord.X - 1;
          curCoord.Y = prevCoord.Y + 1;
          curCase = 4;
        }else if (prevCoord.Y == conY-1) {
          curCoord.X = prevCoord.X + 1;
          curCoord.Y = prevCoord.Y - 1;
          curCase = 2;
        }else{
          curCoord.X = prevCoord.X + 1;
          curCoord.Y = prevCoord.Y + 1;
        }
        break;
      case 2: //up-right
        if(prevCoord.X == conX-1 && prevCoord.Y == 0) { //top right corner
          curCoord.X = prevCoord.X - 1;
          curCoord.Y = prevCoord.Y + 1;
          curCase = 4; //down left
        }else if (prevCoord.X == conX-1) {
          curCoord.X = prevCoord.X - 1;
          curCoord.Y = prevCoord.Y - 1;
          curCase = 3;
        }else if (prevCoord.Y == 0) {
          curCoord.X = prevCoord.X + 1;
          curCoord.Y = prevCoord.Y + 1;
          curCase = 1;
        }else{
          curCoord.X = prevCoord.X + 1;
          curCoord.Y = prevCoord.Y - 1;
        }
        break;
      case 3: //up-left
        if(prevCoord.X == 0 && prevCoord.Y == 0) { //top left corner
          curCoord.X = prevCoord.X + 1;
          curCoord.Y = prevCoord.Y + 1;
          curCase = 1; 
        }else if (prevCoord.X == 0) {
          curCoord.X = prevCoord.X + 1;
          curCoord.Y = prevCoord.Y - 1;
          curCase = 2;
        }else if (prevCoord.Y == 0) {
          curCoord.X = prevCoord.X - 1;
          curCoord.Y = prevCoord.Y + 1;
          curCase = 4;
        }else{
          curCoord.X = prevCoord.X - 1;
          curCoord.Y = prevCoord.Y - 1;
        }
        break;
      case 4: //down-left
        if(prevCoord.X == 0 && prevCoord.Y == conY-1) { //bottom left corner
          curCoord.X = prevCoord.X + 1;
          curCoord.Y = prevCoord.Y - 1;
          curCase = 2;
        }else if (prevCoord.X == 0) {
          curCoord.X = prevCoord.X + 1;
          curCoord.Y = prevCoord.Y + 1;
          curCase = 1;
        }else if (prevCoord.Y == conY-1) {
          curCoord.X = prevCoord.X - 1;
          curCoord.Y = prevCoord.Y - 1;
          curCase = 3;
        }else{
          curCoord.X = prevCoord.X - 1;
          curCoord.Y = prevCoord.Y + 1;
        }
        break;
    }
  }
  //write over old non-trailing positions with space
  writeArray[ovrWriteCoord.X + conX * ovrWriteCoord.Y].Char.AsciiChar = 0x20; //0x20 is ascii space char
  //write trailing dots in prev positions
  writeArray[trailCoord2.X + conX * trailCoord2.Y].Attributes = 8; //trail 3
  writeArray[trailCoord2.X + conX * trailCoord2.Y].Char.AsciiChar = '.';
  writeArray[trailCoord.X + conX * trailCoord.Y].Attributes = 7; //trail 2
  writeArray[trailCoord.X + conX * trailCoord.Y].Char.AsciiChar = '.';
  writeArray[prevCoord.X + conX * prevCoord.Y].Attributes = 15; //trail 1
  writeArray[prevCoord.X + conX * prevCoord.Y].Char.AsciiChar = '.';
  //write new X in new position
  writeArray[curCoord.X + conX * curCoord.Y].Char.AsciiChar = 0xE9; //set char X at top left
  writeArray[curCoord.X + conX * curCoord.Y].Attributes = 14; //yellow on black bg
  //write to buffer
  WriteConsoleOutput(hBuffer1, writeArray, writeArrayBufferSize, bufferUpperLeftCell, &bufferWriteLocation);
  //output to screen
  SetConsoleActiveScreenBuffer(hBuffer1);
  Sleep(30);
}

cin.get(); // wait
return 0;
}

image: http://i.imgur.com/8zPcQbf.jpg

now to work on a timing function.. slowly slowly.. :)

I guess this timer class is going to be getting called quite a lot by several aspects of the game :o still much to wrap my head around it seems.
I also definitely need to rewrite the switch case and what not in my program above.. I want to drop in random Blocks and have it bounce off those and I cant have fixed values in there.. akin to collision detection..
Last edited on
in line 68: firstRun == FALSE; Did you mean '='?

Also, Visual Studio doesn't compile it because of line 32 being CHAR_INFO writeArray[arraySize]; and says that it expects a constant value for allocating writeArray (not a variable, since this is not dynamically allocated). Making these minor changes, it compiled fine for me though.

Yes, you'd have to reference your timer every loop. In fact, you'd probably have it QueryPerformanceCounter() for each iteration, and then derive your elapsed time in seconds. Based on that value, everything in your program that is based on time would reference that and act accordingly. A stop-watch would simply add that value to it's current value, a count-down would subtract it, an animation would use it to calculate it's current position in the overall movement. And after all computations of the loop were conducted, you would check to see if you should render a frame for that iteration or skip it.


Also, if you used a timer, your speed and coordinate changes would have to be expressed in terms of units/sec . So instead of explicitly stating coordinate offsets, you would calculate the current offset for this iteration based on the elapsed time since the last iteration.

Last edited on
whoa, odd that it actually worked for me with line68 set to that.. I totally meant to be '=' o.O I cant beleive it compiled and ran.. I guess it worked because I forgot to initialize the bool and I guess it defaults to FALSE.. therefor the if always jumped to else

I had a few bugs when I changed from a fixed value to variable size, but I wanted to be able to resize the window and see how it bounced around differently :o it crashed a few times after the first few iterations but after that seemed to work flawlessly. Weird.

I have much 'hardcoding' to get away from it seems, thanks again for the input
Last edited on
Yeah, very odd, I assumed it was a typo while copying the code in your post!

If you want to make the size change in run-time, you should use dynamic allocation -- either a dynamically allocated array or an std::vector. Vectors are very handy and would probably be the best choice in this case.

Visual Studio gave me these for your posted version:
main.cpp(32): error C2057: expected constant expression
main.cpp(32): error C2466: cannot allocate an array of constant size 0
main.cpp(32): error C2133: 'writeArray' : unknown size
main.cpp(159): warning C4309: '=' : truncation of constant value


Making writeArray either constant-sized or implementing vectors, only the warning for line 159 remains for a silent compilation.

well that array size is based on the number of rows and columns I want, hence arraySize being equal to conX * conY..
the errorC2133 saying its of unknown size doesnt make any sense to me because its clearly defined in like 31.

thing is its not really an array, its a CHAR_INFO type which holds a union of a char and a w_char called char.. so a C style char array it is, no vector allowed.. unless im missing something. The write console output takes a CHAR_INFO type as an argument, so Id have to cast a vector into a c style string and then load that into the char_type, but then im dynamically changing that too, so the vector route seems a bit round-a-bout.. please correct me if my logic is off here, I am fairly new to the c++ game :o

I guess I'll have to settle and find a row*column dimension I like and work with that..

Though it compiles fine for me :P

Last edited on
the errorC2133 saying its of unknown size doesnt make any sense to me because its clearly defined in like 31.


The size must be known at compile time. It is illegal to specify the size of an automatic array with a value that is not a compile-time constant. Likely you have a compiler extension enabled that allows variable length arrays, but it isn't legal c++.
So even though arraySize is equal to conX * conY which are both known at compile time, its not considered known then?

In what way could I get around this then? In my file there are a lot of things that are based on the conX and conY numbers, I assumed it only natural to use that in setting the array size.

I suppose it largely doesnt matter at all.. but itd be nice to know.
So even though arraySize is equal to conX * conY which are both known at compile time, its not considered known then?


conX is not const. conY is not const. arraySize is not const. Therefore you don't have a compile time constant.

Make them const.
ah. I had a feeling it was that simple.

thanks :)

in the mean time ill find another compiler..

edit::

wait, Im guessing its not as simple as setting:
const int conX = 30;
const int conY = 12;
const int arraySize = conX * conY;

or is it... or is this too not constant.. I assumed by your use of the short form identifier thats what you were pointing to without actually telling me but after ogoyants post im still not certain :s

Last edited on
Like cire pointed out, the size must be known at compile-time, not at run-time. Hence the error. The value of (conX * conY) is a variable value not known until during run-time.

Anyway, I'm quite unfamiliar with CHAR_INFO, although dynamic allocation of arrays in C can be done using malloc() and free(). I don't know to what extent that seems like a good way to go in this case, though.

http://www.cplusplus.com/reference/cstdlib/malloc/


EDIT: I see you got this clarified while I was writing this.

EDIT2: Oh, by the way:

main.cpp(65): warning C4700: uninitialized local variable 'firstRun' used

This makes your program crash when run in Visual Studio, because in line 65 you if(firstRun == TRUE) without having initialized firstRun. Setting line 54 as BOOL firstRun=TRUE; will fix that.
Last edited on
Yeah, I realized that the only way it did run with the false line of firstRun==True instead of firstRun = True was because I didnt initialize the bool. and because it only skipped over the first frame I never really noticed it

Guess I got lucky there.

What compiler are you using? Im guessing mine is way behind on the standards or is very relaxed out of the box..
Last edited on
1
2
3
const int conX = 30;
const int conY = 12;
const int arraySize = conX * conY;


or is it... or is this too not constant.. I assumed by your use of the short form identifier thats what you were pointing to without actually telling me but after ogoyants post im still not certain :s
Yes, the above should be sufficient for making it constant, and compiles fine in VS.
Pages: 12