views:

212

answers:

3

I'm creating a 2D game using OpenGL and C++.

I want it so that the game runs at the same speed on different computers, At the moment my game runs faster on my desktop than my laptop (i.e. my player moves faster on my desktop)

I was told about QueryPerformanceCounter() but I don't know how to use this.

how do I use that or is there a better/easier way?

My Display function

void display()                                  
{
static long timestamp = clock();
// First frame will have zero delta, but this is just an example.
float delta = (float)(clock() - timestamp) / CLOCKS_PER_SEC;

glClear(GL_COLOR_BUFFER_BIT);

glLoadIdentity();

createBackground();

int curSpeed = (player.getVelocity()/player.getMaxSpeed())*100;

glColor3f(1.0,0.0,0.0);
glRasterPos2i(-screenWidth+20,screenHeight-50);
glPrint("Speed: %i",curSpeed);

glRasterPos2i(screenWidth-200,screenHeight-50);
glPrint("Lives: %i",lives);

glRasterPos2i(screenWidth-800,screenHeight-50);
glPrint("Heading: %f",player.getHeading());

for(int i = 0;i<90;i++){
    if (numBullets[i].fireStatus == true){
        numBullets[i].updatePosition(player);
        if (numBullets[i].getXPos() > screenWidth || numBullets[i].getXPos() < -screenWidth || numBullets[i].getYPos() > screenHeight || numBullets[i].getYPos() < -screenHeight ){
            numBullets[i].fireStatus = false;
            numBullets[i].reset(player);
            numBullets[i].~Bullet();
        }
    }
}

player.updatePosition(playerTex,delta);

glFlush();

timestamp = clock();

}

My Update positiom method

void Player::updatePosition(GLuint playerTex, float factor){
//draw triangle
glPushMatrix();

glEnable(GL_TEXTURE_2D);    
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindTexture(GL_TEXTURE_2D, playerTex);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

glTranslatef(factor*XPos, factor*YPos, 0.0);
glRotatef(heading, 0,0,1);
glColor3f(1.0,0.0,0.0);
    glBegin(GL_POLYGON);
        glTexCoord2f(0.0, 1.0); glVertex2f(-40,40);
        glTexCoord2f(0.0, 0.0); glVertex2f(-40,-40);
        glTexCoord2f(1.0, 0.0); glVertex2f(40,-40);
        glTexCoord2f(1.0, 1.0); glVertex2f(40,40);
    glEnd();

glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
glPopMatrix();

XPos += speed*cos((90+heading)*(PI/180.0f));
YPos += speed*sin((90+heading)*(PI/180.0f));
}
+4  A: 

Most games use a frame time-scaling factor. Essentially, you find the length of the frame your physics and movement are set at and divide it between the actual length of the frame the game is running at (1/fps). This produces a scalar factor which you can multiply by changes in movement to keep all movements consistent while maintaining the benefits from increasing the FPS.

A good example is here.

Mason Blier
What the rest of us call *fixed* time step loop.
Andreas Magnusson
+9  A: 

As a rule, you want to do all gameplay calculations based on a time delta, i.e. the amount of time that has passed since the last frame. This will standardize speed on all machines. Unless you want extreme precision, you can use clock() (from <ctime>) to get the current timestamp.

Example:

void main_loop() {
   static long timestamp = clock();
   // First frame will have zero delta, but this is just an example.
   float delta = (float)(clock() - timestamp) / CLOCKS_PER_SEC;

   calculate_physics(delta);
   render();

   timestamp = clock();
}

void calculate_physics(float delta) {
   // Calculate expected displacement per second.
   applyDisplacement(displacement * delta);
}

void render() {
   // Do rendering.
}

EDIT: If you want higher precision, you should use your OS timer features. On Windows, the most precise method is using QueryPerformanceCounter(). Example

#include <windows.h>

void main_loop(double delta) {
  // ...
}

int main() {
  LARGE_INTEGER freq, last, current;
  double delta;
  QueryPerformanceFrequency(&freq);

  QueryPerformanceCounter(&last);
  while (1) {
    QueryPerformanceCounter(&current);
    delta = (double)(current.QuadPart - last.QuadPart) / (double)freq.QuadPart;

    main_loop(delta);

    last = current;
  }
}
Max Shawabkeh
What is applyDisplacement?
Chris
It's just a placeholder for any function that does your animation.
Max Shawabkeh
so delta is a value that I apply to all animations etc so they work at the same speed everywhere?
Chris
Yes. The value of delta in a typical call will vary from machine to machine, depending on the framerate each can squeeze out. Applying the delta to the magnitude of your animations will even out the differences.
Max Shawabkeh
thanks, can I just do this once and reuse the same value? or does it need to be calculated on each loop?
Chris
It's calculated on each frame, just as it's done in the example code.
Max Shawabkeh
I'm getting 0 to be the value of delta most of the time so it is multiplying my translation by 0 and therefore my player isn't moving
Chris
Then you're probably doing something wrong. Even for a framerate of 300, you'd still get a perfectly representable 0.003 delta per frame. Edit your delta calculating code into your question.
Max Shawabkeh
I get a frame rate of about 1200 fps
Chris
That'd give ~0.0008, still perfectly fine. However, in your code you're multiplying the current position by the delta - that's wrong. You should multiply the change you are applying per frame (the `XPos += ...` and `YPos += ...` lines).
Max Shawabkeh
Ahh I see, I did that but I get very jagged movement, it isn't very smooth
Chris
That shouldn't be happening. Make sure you're not doing integer calculations instead of floating point ones somewhere. BTW, what's the deal with `90+heading`? `sin()` and `cos()` operate on radians, not degrees.
Max Shawabkeh
I'm not doing integer calculations, I have checked the outputs of my XPos and YPos I'm getting lots of 0s and then 0.something then lots of 0s again. the 90+heading bit is converted into radians, its the way I was taught to do it
Chris
Edited in an (unportable) example with more precision.
Max Shawabkeh
Actually, most *real* games use a fixed time step loop as it prevents physics calculations from exploding and makes multiplayer gaming possible.
Andreas Magnusson
Andreas, that is true, but for a hobby game (as in the question) without very complex physics simulation, a variable time step is perfectly fine.
Max Shawabkeh
A: 

The best solution to your problem is to only update the positions of your objects once every 10 ms (or something similar) but render the objects as often as possible. This is called "fixed time step" as you only update the game state at fixed intervals. This requires you to decouple the rendering code from the update code (which is a good idea anyway).

Basically (in psudoe code) what you would do is something like:

accumulatedTimeSinceLastUpdate = 0;
while(gameIsRunning)
{
  accumulatedTimeSinceLastUpdate += timeSinceLastFrame();
  while(accumulatedTimeSinceLastUpdate >= 10) // or another value
  {
    updatePositions();
    accumulatedTimeSinceLastUpdate -= 10;
  }
  display();
}

This means that if your computer is running super-duper fast display() will be called a lot of times and every now and then updatePositions(). If your computer is ultra slow updatePositions may be called several times for each time display() is called.

Here's another good read (in addition to Mason Blier's):

Andreas Magnusson