在SDL中渲染静止图像

3
我正在学习SDL,尝试创建一个Pacman游戏。我想分步骤进行,以免陷入大量代码的泥潭。
到目前为止,我已经创建了一个空窗口,并将Pacman图像渲染到其中。我能够按箭头键并移动Pacman在窗口中移动。我已经将Pacman图像设置为SDL_Texture,使用RenderCopy放置到窗口中。每次用户按下箭头时,我都会移动图像的坐标并重新渲染整个图像。这很好用。但现在,我想在屏幕上放一些点让Pacman吃掉。然而,如果我加载点图像并将其存储为新的纹理,与Pacman一起放置在屏幕上,每次我移动Pacman时,点就会闪烁,因为它被擦除并重新渲染了。
我的问题是,如何避免这种“闪烁”?我是否可以仅重新渲染Pacman而不重新渲染屏幕的其余部分?还是有其他方法可以解决这个问题?我觉得当我尝试在后台创建迷宫时也会遇到同样的问题。我怎样才能制作一个静态背景,而每次重新渲染时不会闪烁失去稳定性?
以下是我到目前为止的代码。如果有任何不好的代码形式,请原谅我。正如我所说,我刚开始学习SDL(也是C ++的新手),因此如果有任何显眼的“您不应该那样做!”之类的问题,请指出 :)
#include <iostream>
#include <SDL2/SDL.h>
using namespace std;

const int WINDOW_HEIGHT = 480;
const int WINDOW_WIDTH = 640;
const int MOVE_WIDTH = 10;

int main(int argc, const char * argv[])
{
    SDL_Window* mainWindow = NULL; //To hold the main window
    SDL_Renderer* renderer = NULL; //To hold the renderer
    SDL_Rect targetRect; //Rectangle to which pacman image will be drawn
    SDL_Surface* bmpSurface = NULL; //To hold bmp image
    SDL_Texture* bmpTexture = NULL; //To hold bmp image

    //Initialize SDL and check for errors
    if ( SDL_Init(SDL_INIT_EVERYTHING) != 0 )
    {
        cout << "ERROR: could not initialize SDL." << endl;
    }

    //Create a window
    mainWindow = SDL_CreateWindow("BAM", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, 0);

    if (mainWindow == NULL)
    {
        cout << "ERROR: could not initialize mainWindow." << endl;
    }

    //Initialize renderer
    renderer = SDL_CreateRenderer(mainWindow, -1, SDL_RENDERER_ACCELERATED);

    //Load image and store in an SDL_Surface
    bmpSurface = SDL_LoadBMP("/Users/billgrenard/Desktop/Programs/SDL/SDL_KeyPresses/SDL_KeyPresses/pacman_closed.bmp");
    if ( bmpSurface == NULL )
    {
        cout << "ERROR: could not load bmp file." << endl;
    }

    //Convert surface to texture for rendering
    bmpTexture = SDL_CreateTextureFromSurface(renderer, bmpSurface);
    if ( bmpTexture == NULL )
    {
        cout << "ERROR: could not convert bmp surface." << endl;
    }

    SDL_FreeSurface(bmpSurface);

    //Define rectangle where pacman image is to be blitted
    targetRect.w = 30;
    targetRect.h = 30;
    targetRect.x = (WINDOW_WIDTH/2) - (targetRect.w/2);
    targetRect.y = (WINDOW_HEIGHT/2) - (targetRect.h/2);


    //Main game loop
    while (1)
    {
        SDL_Event e;
        if (SDL_PollEvent(&e))
        {
            //Quit when user x's out the window
            if (e.type == SDL_QUIT)
            {
                break;
            }

            //If user presses a key enter switch statement
            else if( e.type == SDL_KEYDOWN )
            {
                switch ( e.key.keysym.sym ) {
                    //If user presses up arrow and the resulting move is inside the window, then move the Pacman's position
                    case SDLK_UP:
                        if ( targetRect.y - MOVE_WIDTH > 0 )
                        {
                            targetRect.y -= MOVE_WIDTH;
                        }

                        break;

                    //If user presses down arrow and the resulting move is inside the window, then move the Pacman's position
                    case SDLK_DOWN:
                        if ( targetRect.y + MOVE_WIDTH < (WINDOW_HEIGHT - targetRect.w) )
                        {
                            targetRect.y += MOVE_WIDTH;
                        }

                        break;

                    //If user presses right arrow and the resulting move is inside the window, then move the Pacman's position
                    case SDLK_RIGHT:
                        if ( targetRect.x + MOVE_WIDTH < (WINDOW_WIDTH - targetRect.w) )
                        {
                            targetRect.x += MOVE_WIDTH;
                        }

                        break;

                    //If user presses left arrow and the resulting move is inside the window, then move the Pacman's position
                    case SDLK_LEFT:
                        if ( targetRect.x - MOVE_WIDTH > 0 )
                        {
                            targetRect.x -= MOVE_WIDTH;
                        }

                        break;

                    default:
                        break;
                }
            }
        }

        SDL_RenderClear(renderer);
        SDL_RenderCopy(renderer, bmpTexture, NULL, &targetRect);
        SDL_RenderPresent(renderer);       
    }

    SDL_DestroyWindow(mainWindow);
    SDL_DestroyTexture(bmpTexture);
    SDL_DestroyRenderer(renderer);
    SDL_Quit();

    return 0;
}

编辑:回答raser的评论,这里是我找到PollEvent示例的链接:http://wiki.libsdl.org/SDL_CreateRenderer?highlight=%28%5CbCategoryAPI%5Cb%29%7C%28SDLFunctionTemplate%29


我加载了点图像并将其存储在新纹理中。然后,我调用了第二个RenderCopy(紧接着我用于Pacman图像的RenderCopy),使用相同的渲染器进行渲染点纹理,并使用不同的targetRect。然后,我只需保留已经在代码中的RenderPresent函数来同时呈现点和Pacman。 - wgrenard
好的,Pacman正在移动,所以闪烁不是问题。当我重新渲染时,Pacman会从旧位置消失并出现在新位置,这正是我想要的。但是,由于点没有被移动,因此在重新渲染时它也会消失和重新出现。但是,由于点没有被移动,所以看起来就像它消失了又出现了。 - wgrenard
我直接从SDL的文档示例中复制了轮询事件循环。虽然我想在转换过程中可能会出现一些问题。 - wgrenard
哪些例子?我不认为在SDL文档中曾经看到过if(SDL_PollEvent(&evt))。你能提供引起问题的代码片段吗,其中包含点绘制代码? - Nick Beeuwsaert
1
我在SDL_CreateRenderer函数的文档中找到了PollEvent示例。我刚刚再次检查了它,看起来像是我的代码。无论如何,我刚刚组合了一些代码供您参考,现在点不再闪烁了。我想我找到了问题所在。在那段代码的中间某个地方,我试图添加一些动画(Pacman上的张嘴和闭嘴),因此我使用SDL_Delay(90)渲染了两个不同的Pacman图像。渲染之间的延迟可能就是问题所在。回想起来真是太愚蠢了。 - wgrenard
显示剩余5条评论
1个回答

0
上面用于渲染吃豆人的方法事实上也可以完美地用于在屏幕上呈现点。只需将点的图像存储到一个新纹理中,创建一个SDL_Rect来容纳此纹理,然后使用SDL_CreateRenderer将点图像与吃豆人一起渲染到屏幕上。只要您没有使用类似于SDL_Delay之类的东西来降低帧率,您应该不会注意到点在渲染之间闪烁。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接