5

C ++でSDLとOpenGLを使用するPongのように、私は自分自身をゲームにしました。

#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;}

注: 「latura」は長方形の幅または高さ、「viteza」は速度です。

問題は、他のマシンでゲームをテストすると、私の PC はそれほど悪くないとは言えますが、私の PC ではゲームの動作が非常に遅くなることです... (2GB RAM、8600GT nvidia、およびクアッドコアインテル)、他のマシンでは、コードが同じ速度に設定されているにもかかわらず、ゲームははるかに高速に動作します。この問題のロジックが見つからないようです。このゲームを異なるマシンで同じ速度で動作させる方法を知りたいです (時間依存のアニメーションなどを探しました...どれだけ役立つかわかりません; また、いくつか見つけましたソフトウェア/ハードウェア レンダリングに関するトピックの場合、これが問題になる可能性がありますか? 私の PC ではゲームがソフトウェア レンダリングを使用し、他のマシンではハードウェア ベースが使用されています)。

4

3 に答える 3

11

私が見る主な問題は、ゲームループにあります。なぜ 2 ミリ秒の静的遅延を使用しているのですか? それがあなたのボトルネックです。ハードウェア アクセラレーション OpenGL ドライバーを搭載していない可能性が高いマシンで、ゲームを 500 FPS でレンダリングしようとしています。

まず最初に、ここで使用している 500 Hz ではなく、50 から 100 Hz のようにゲームを遅らせます。ゲーム ループの最初の部分で、時間を維持する変数から始めます。

Uint32 time = SDL_GetTicks();

ここで、コードを追加するために最後までスキップします...

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

SDL に慣れていない人にとっては少し混乱するかもしれませんが、これにより、プログラムがループを通過する速度に関係なく、正確な時間だけコードが遅延されます。

もう少しわかりやすく説明するために、ゲーム ループが最初から最後まで実行するのに 4 ミリ秒かかるとしましょう (議論のためだけに、ループは実際にははるかに高速である可能性があります)。すでに 2 ミリ秒を追加しているため、合計遅延は 6 ミリ秒、つまり約 130 FPS になります。最新のコンピューター ディスプレイのほとんどは、リフレッシュ レートが約 60Hz、つまり 60 フレーム/秒しかありません。

ゲーム ループをできるだけ速くすることで、ゲームはほとんどのフレームを表示することなく、2 倍以上のフレームをレンダリングします。考えるのは非常に無駄ですが、上記の一般的な解決策を見てみましょう。

SDL_GetTicks() は、初期化以降の現在の実行時間を知らせる関数です。この時間のスナップショットをループの最初で取得して、開始時間を取得します。最後に SDL_GetTicks() が再び表示され、それを使用して開始値と比較します。

開始時刻が 15 で、終了時に SDL_GetTicks() によって返される時刻が 22 であるとします。この式は、最初に 22 から 15 を引いた 7 を比較し、その数値が 20 より小さいかどうかを比較します。次に、20 から 7 ミリ秒 (13) を差し引いた時間、システムを一時停止します。

これは、フレームレートが大きく変動しないため便利です。また、ゲーム ループの速度が最後に到達するまでに 20 ミリ秒以上かかる場合でも、まったく遅延しないため、貴重な処理能力やフレーム レンダリングを無駄にすることはありません。それと。

20 の代わりに、(1000/FPS) に置き換えることもできます。ここで、FPS は...まあ...ゲームを実行したい FPS です。これにより、最初に電卓を使用することなく、60 FPS などを簡単に実行できます。簡単な割り算、それだけです。

最終結果:

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!
        }
    }

これは、ゲームのレンダリングを制御する最も簡単な方法です。余談ですが、ゲームループを 100 で実行している間、60Hz での画面レンダリング用に別のスレッドを作成するという、もう少し複雑な方法を使用しています。計算。SDL_CreateThread() 関数を調べると、これが非常に簡単になります。

遭遇する可能性のある唯一の問題は、動的な数のオブジェクトを画面にレンダリングすることですが、静的ベクトルですべて解決できますが、余談になります。これで問題のほとんどが解決したことを願っていますが、この投稿は 3 か月前のものなので、同じ質問をする他の人にとってはより役立つかもしれません.

于 2013-06-19T16:50:22.610 に答える
1

他の回答を見ると、あなたのマシンには h/w アクセラレーション OpenGL ドライバーがないため、ゲームが MESA gl ライブラリを取得していると思います。

これを確認する方法は 1 つあります。コマンドラインから、次のように入力します。

ldd ./program_name

/usr/lib/libGL.so.1 ではなく /usr/lib/mesa/libGL.so.1 が表示された場合、なぜこれほど遅いのかがわかります。

于 2013-03-03T12:04:53.897 に答える
1

あなたのコードを私の PC でコンパイルし、内蔵グラフィックス カード (インテル HD 4000) でスムーズに (>500 fps) 動作しています。

openGL が実行されているかどうかを確認するには、サーフェスでフラグを使用します。

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

if( screen->flags & SDL_OPENGL ) printf("using openGL");
于 2013-03-03T09:41:15.737 に答える