5 votes

Mon SDL C++ (jeu 2d) utilisant opengl est lent, que faire ?

J'ai créé un jeu, comme Pong, en utilisant SDL et OpenGL sous c++ :

#include "SDL.h"
#include "SDL_opengl.h"
#include <iostream>
int main(int argc, char* args[])
{
//initialize SDL
SDL_Init(SDL_INIT_EVERYTHING);

//OpenGL memory
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute( SDL_GL_BUFFER_SIZE, 32);
SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1);

//caption of the window
SDL_WM_SetCaption( "Bine baaa", NULL );

//size
SDL_SetVideoMode(600,400,32, SDL_OPENGL);

//clearcolor
glClearColor(0,0,0,1); //RED,GREEN,BLUE,ALPHA

//portion of screen displayed
glViewport(0,0,600,400);

//for gradients
glShadeModel(GL_SMOOTH);

//2D rendering
glMatrixMode(GL_PROJECTION);
glLoadIdentity();//save

glDisable(GL_DEPTH_TEST);

bool isRunning = true;

SDL_Event event;

typedef struct player{

    float myX;
    float myY;
    float width=15;
    float height=60;
    bool up=false;
    bool down=false;
};
player player1,player2;

player1.myX=10;
player1.myY=160;
player2.myX=580;
player2.myY=160;

float ballX=300;
float ballY=200;
float vitezaX=0.5;
float vitezaY=0.5;
float latura =10;

//main loop
while(isRunning){
    //EVENTS
    while ( SDL_PollEvent(&event)){

        if( event.type == SDL_QUIT )
            isRunning=false;
        //escape button closes window
        if(event.type == SDL_KEYUP && event.key.keysym.sym == SDLK_ESCAPE)
            isRunning=false;
        if( event.type == SDL_KEYUP && event.key.keysym.sym == SDLK_r)
            glClearColor(1,0,0,1);
        if( event.type == SDL_KEYDOWN){
            if(event.key.keysym.sym==SDLK_UP)
                player2.up=true;
            if(event.key.keysym.sym==SDLK_DOWN)
                player2.down=true;
            if(event.key.keysym.sym==SDLK_w)
                player1.up=true;
            if(event.key.keysym.sym==SDLK_s)
                player1.down=true;
        }
        if( event.type == SDL_KEYUP){
            if(event.key.keysym.sym==SDLK_UP)
                player2.up=false;
            if(event.key.keysym.sym==SDLK_DOWN)
                player2.down=false;
            if(event.key.keysym.sym==SDLK_w)
                player1.up=false;
            if(event.key.keysym.sym==SDLK_s)
                player1.down=false;
        }
    }
    //LOGIC
    if(player1.up==true)
        player1.myY-=0.3;
    if(player1.down==true)
        player1.myY+=0.3;
    if(player2.up==true)
        player2.myY-=0.3;
    if(player2.down==true)
        player2.myY+=0.3;
    if(ballY<0)
        vitezaY=-vitezaY;
    if(ballY+latura>400)
        vitezaY=-vitezaY;
    if(ballX+latura>player2.myX && ballY+latura>player2.myY && ballY<player2.myY+player2.height){
        vitezaX=-vitezaX;
        if(ballX+latura-player2.myX>=1){
                if(vitezaY>0)
                    ballY=player2.myY-latura;
                else
                    ballY=player2.myY+player2.height;
            vitezaX=-vitezaX;
            vitezaY=-vitezaY;
        }
    }
    if(ballX<player1.myX+player1.width && ballY+latura>player1.myY && ballY<player1.myY+player1.height){
        vitezaX=-vitezaX;
        if((player1.myX+player1.width)-ballX>=1){
            if(vitezaY>0)
                ballY=player1.myY-latura;
            else
                ballY=player1.myY+player1.height;
            vitezaX=-vitezaX;
            vitezaY=-vitezaY;
        }
    }
    if(ballX<0 || ballX>600){
         ballX=300;
         ballY=200;
         SDL_Delay(500);
    }
    ballX+=vitezaX;
    ballY+=vitezaY;

    //RENDER
    glClear(GL_COLOR_BUFFER_BIT);

        glPushMatrix(); //Begin Render

        glColor4ub(255,255,255,255);
        glOrtho(0,600,400,0,-1,1);

        glBegin(GL_QUADS);//GL_LINES, GL_LINE_STRIP, GL_QUADS, GL_POLIGON,GL_TRIANGLES, GL_LINE_LOOP

        glVertex2f(player1.myX,player1.myY);
        glVertex2f(player1.myX+player1.width,player1.myY);
        glVertex2f(player1.myX+player1.width,player1.myY+player1.height);
        glVertex2f(player1.myX,player1.myY+player1.height);

        glEnd();//End Draw

         glBegin(GL_QUADS);//GL_LINES, GL_LINE_STRIP, GL_QUADS, GL_POLIGON,GL_TRIANGLES, GL_LINE_LOOP

        glVertex2f(player2.myX,player2.myY);
        glVertex2f(player2.myX+player2.width,player2.myY);
        glVertex2f(player2.myX+player2.width,player2.myY+player2.height);
        glVertex2f(player2.myX,player2.myY+player2.height);

        glEnd();//End Draw

        glBegin(GL_QUADS);//GL_LINES, GL_LINE_STRIP, GL_QUADS, GL_POLIGON,GL_TRIANGLES, GL_LINE_LOOP

        glVertex2f(ballX,ballY);
        glVertex2f(ballX+latura,ballY);
        glVertex2f(ballX+latura,ballY+latura);
        glVertex2f(ballX,ballY+latura);

        glEnd();//End Draw

        glPopMatrix(); //End Render

    SDL_GL_SwapBuffers();
    SDL_Delay(2);
}
SDL_Quit();

return 0;}

NOTE : "latura" est la largeur ou la hauteur du rectangle, "viteza" est la vélocité.

Le problème est que lorsque je teste le jeu sur d'autres machines, sur mon PC le jeu se déplace très lentement même si je peux dire que mon PC n'est pas si mauvais...(2GB RAM, 8600GT nvidia, et un intel quad core), sur d'autres machines le jeu se déplace beaucoup plus rapidement, indépendamment du fait que le code a été réglé à la même vitesse. Je n'arrive pas à trouver la logique de ce problème. Je voudrais savoir comment faire fonctionner ce jeu à la même vitesse sur différentes machines (j'ai cherché quelque chose comme des animations dépendantes du temps... je ne sais pas si cela aidera beaucoup ; j'ai aussi trouvé des sujets sur le rendu logiciel/matériel, est-ce que cela pourrait être le problème ? que sur mon PC le jeu utilise un rendu logiciel et sur d'autres machines un rendu matériel...).

11voto

The Posh Ferret Points 126

Le principal problème que je vois est dans votre boucle de jeu. Pourquoi utilisez-vous un délai statique de 2 millisecondes ? C'est le goulot d'étranglement. Vous essayez de rendre votre jeu à 500 FPS sur une machine qui n'a probablement pas de pilotes OpenGL accélérés.

Tout d'abord, retardez votre jeu à quelque chose comme 50 à 100Hz au lieu des 500 que vous utilisez ici. Commencez avec une variable de maintien du temps au tout début de votre boucle de jeu.

Uint32 time = SDL_GetTicks();

Maintenant, nous allons sauter jusqu'à la fin pour plus de code...

if(20>(SDL_GetTicks()-time))
{
    SDL_Delay(20-(SDL_GetTicks()-time)); //SDL_Delay pauses the execution.
}

Cela peut sembler un peu confus pour quiconque n'est pas familier avec SDL, mais cela a pour effet de retarder le code d'une durée exacte, quelle que soit la vitesse à laquelle le programme parcourt sa boucle.

Pour l'expliquer un peu mieux, disons que la boucle du jeu prend 4 millisecondes (pour les besoins de l'argumentation, la boucle est probablement beaucoup plus rapide en réalité) pour aller du début à la fin. Vous ajoutez déjà 2 millisecondes, ce qui fait un délai total de 6 millisecondes, soit ~130 FPS. La plupart des écrans d'ordinateur modernes n'ont qu'un taux de rafraîchissement d'environ 60 Hz, soit 60 images par seconde.

En faisant tourner le jeu en boucle aussi rapidement qu'il le fait, votre jeu rend plus du double d'images sans même afficher la plupart d'entre elles. C'est un gaspillage énorme, mais examinons la solution courante ci-dessus.

SDL_GetTicks() est une fonction qui vous indique le temps de fonctionnement actuel depuis l'initialisation. Vous prenez un instantané de ce temps au tout début de la boucle pour obtenir votre temps de départ. Vous voyez ensuite SDL_GetTicks() à nouveau à la fin et vous utilisez cette valeur pour la comparer à votre valeur de départ.

Disons que l'heure de départ est 15 et qu'à la fin, l'heure renvoyée par SDL_GetTicks() est 22. L'équation compare d'abord 22 moins 15, soit 7, et vérifie si ce nombre est inférieur à 20, ce qui est le cas. Elle met ensuite le système en pause pendant 20 moins 7 millisecondes, soit 13.

C'est utile parce que le taux de rafraîchissement ne fluctue pas énormément, et si la vitesse de la boucle du jeu prend plus de 20 millisecondes pour atteindre la fin, elle ne la retarde pas du tout, ce qui signifie que vous ne gaspillerez jamais une puissance de traitement précieuse ou un rendu de trame avec cette fonction.

Au lieu de 20, vous pouvez également le remplacer par (1000/FPS) où FPS est le... eh bien.... FPS auquel vous voulez que votre jeu tourne. Il est donc facile de faire quelque chose comme 60 FPS sans avoir à sortir sa calculatrice. Une simple division, c'est tout.

Résultat final :

int main(int argc, char *argv[])
{
    int FPS = 50 //Framerate
    //Setup stuff
    while(GAME_RUNNING)
    {
        Uint32 start_time = SDL_GetTicks();

        //Event handling

        //Logic stuff

        //Rendering things

        if((1000/FPS)>(SDL_GetTicks()-start_time))
        {
            SDL_Delay((1000/FPS)-(SDL_GetTicks()-start_time)) //Yay stable framerate!
        }
    }

C'est la façon la plus simple de contrôler le rendu de votre jeu. Par ailleurs, j'utilise une méthode un peu plus compliquée qui consiste à créer un thread séparé pour le rendu de l'écran à 60Hz pendant que je fais tourner la boucle du jeu à 100. Cela évite de gaspiller de la puissance de traitement et me permet d'obtenir un nombre rond pour mes calculs dans le jeu. Consultez la fonction SDL_CreateThread(), elle simplifie énormément les choses.

Le seul problème que vous pourriez rencontrer serait le rendu d'un nombre dynamique d'objets à l'écran, mais tout cela peut être résolu avec des vecteurs statiques, mais je m'éloigne du sujet. J'espère que cela a résolu la plupart de vos problèmes, mais comme ce post date de 3 mois, il pourrait être plus utile à toute personne tombant dessus et se posant les mêmes questions.

1voto

J'ai compilé votre code sur mon PC, et il fonctionne sans problème (>500 fps) sur une carte graphique intégrée (intel HD 4000).

Si vous voulez vérifier si openGL fonctionne, utilisez des drapeaux sur votre surface.

SDL_Surface* screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL );

if( screen->flags & SDL_OPENGL ) printf("using openGL");

1voto

Rahul Banerjee Points 2183

En regardant les autres réponses, je pense que votre machine n'a pas le pilote OpenGL accéléré h/w, ce qui fait que votre jeu récupère la bibliothèque MESA gl.

Voici une façon de le confirmer. À partir d'une ligne de commande, tapez :

ldd ./program_name

Si vous voyez /usr/lib/mesa/libGL.so.1 plutôt que /usr/lib/libGL.so.1, vous saurez pourquoi il est si lent.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X