使用 SDL2 在每一帧上渲染部分屏幕

Rendering parts of the screen on each frame with SDL2

我正在使用 SDL2 在 C++ 中制作一个 2D 游戏,其中世界地图由 120x67 的图块组成。在任何给定时间,地图上只会有大约 20 个左右的精灵(每个只占据一个方块)移动,并且只有一小部分方块(主要是水方块)会进行动画处理。因此在绝大多数时间里,大部分磁贴将保持静态。目前,我有一个简单的绘制循环,它遍历所有图块并重新绘制它们:

/* Drawing loop, excluding frame rate limiting logic and updating gamestate */
while(true) {

    /* game state is updated (omitted) */

    /* clear the entire screen */
    SDL_RenderClear(renderer);

    /* loop through all the tiles and redraw them */
    for (int i = 0; i < rows; i++) {
        for(int j = 0; j < cols; j++) {

            /* call the tile's draw method, which calls SDL_renderCopy()
             * to draw the tile background and sprite (if there is one) */
            tiles[i][j].drawTile(renderer);
        }
    }

    /* output everything to the screen */
    SDL_RenderPresent(renderer);
}

但是,由于大多数图块大部分时间都将保持静止,因此没有必要继续绘制它们。只重绘屏幕上发生变化的一小部分会更有效率。所以我的新想法是,在更新游戏状态的同时,将所有更新的图块添加到列表中,然后只需在每一帧上只重绘更新的图块,如下所示:

/* Drawing loop that only redraws updated tiles */
while(true) {

    /* game state is updated, updated tiles added to a list (omitted) */

    /* loop through only the updated tiles and redraw them */
    for (std::list<Tile>::iterator it=updatedTiles.begin(); it != updatedTiles.end(); ++it) {

        /* call the tile's draw method, which calls SDL_renderCopy()
         * to draw the tile background and sprite (if there is one) */
        it->drawTile(renderer);
    }

    /* output everything to the screen */
    SDL_RenderPresent(renderer);
}

因为我不想在每帧之前清屏,所以在第二种情况下删除了对 SDL_RenderClear() 的调用。但是,从 SDL_RenderPresent 的 SDL2 文档中找到 here,它说:

You are strongly encouraged to call SDL_RenderClear() to initialize the backbuffer before starting each new frame's drawing.

有了这个,我的两个问题是:

  1. 如果我只想更新修改后的图块,我真的需要在每一帧上调用 SDL_RenderClear() 吗?
  2. 如果我必须坚持在每一帧上重绘每个图块,现代显卡/SDL 库 "smart" 是否足以在调用 SDL_RenderPresent() 时仅重绘屏幕已更改,而无需我执行此操作?

文档的相关部分紧接在您提到的部分之前(强调我的)

The backbuffer should be considered invalidated after each present; do not assume that previous contents will exist between frames.

这意味着对 SDL_RenderClear 的调用比 建议的 更多。

你可以做的是绘制背景图像(不管它是什么意思,例如你周围没有任何 PC 或 NPC 的一组图块)在进入场景之前只在目标纹理上执行一次(有关更多详细信息,请参阅 SDL_SetRenderTarget 和周围的其他功能)。然后,在循环中,您可以清除默认渲染器,立即将该图像复制为 背景图像 ,最后只在其上绘制更新的图块,从而更新它们(让我说) 默认或基本表示法
事实上,如果您更新了 N 个图块,您最终会在每次迭代时通过 N+1 次调用 SDL_RenderCopy 或其他任何方式来绘制所有内容。

老实说,只有当您观察到由于每次迭代绘制过多而导致帧明显下降时,我才会这样做。我的两分钱。
无论如何,这是一个肮脏的工作解决方案,至少可以满足您的需求。