渲染单个事物而不必在 sdl 中清除整个屏幕

Rendering single thing without having to clear entire screen in sdl

是否有 sdl 之类的层?

按图层我的意思是像在 photoshop 中我们有多个图层并且可以在一个图层上绘图而不影响另一个图层,

例如,如果我有一个 main_layer 、一个 background_layer 和一个 enemy_layer ,其中主要玩家重新定位(如用户移动角色)、静态背景渲染和敌人渲染可以分别进行吗?

而不是必须清除整个屏幕然后一遍又一遍地把所有内容放回去?即改变一件事而不影响另一件事?有人能指出我正确的方向吗?

您可以使用渲染目标实现自己的图层系统。

  1. 为每一层创建纹理渲染目标。
  2. 绘制到层的渲染目标以更新它。
  3. 每一帧,将每一层绘制到屏幕上。你还需要事先清除最后一帧。

值得注意的是这里有一个递减点return。如果一个层只包含几个精灵,即使它们不移动,每帧直接将每个精灵绘制到屏幕上可能更便宜。

示例:

// Given a renderer
SDL_Renderer *renderer = ...;


// *** Creating the layer ***
SDL_Texture *my_layer = SDL_CreateTexture(
    renderer, 
    SDL_PIXELFORMAT_RGBA8888,
    SDL_TEXTUREACCESS_TARGET, 
    screen_width, screen_height);

// To make transparency work (for non-base layers):
SDL_SetTextureBlendMode(my_layer, SDL_BLENDMODE_BLEND);


// *** Drawing TO the layer ***
SDL_SetRenderTarget(renderer, my_layer);

// For non-base layers, you want to make sure you clear to *transparent* pixels.
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);

// ... Draw to the layer ...


// *** Drawing the layer to the screen / window ***
SDL_SetRenderTarget(renderer, NULL);
SDL_RenderCopy(renderer, my_layer, NULL, NULL);

您可以通过创建大于 screen_width x screen_height 的层并使用 SDL_RenderCopy()srcrect 参数滚动层来更进一步。有了几个背景层,就可以用来获得高效、整洁的老式视差效果。

您可能还想将层的概念封装到 C++ 中的某些 Layer class 中。这是一个粗略的起点:

class Layer {
  public:
    Layer(SDL_Renderer *renderer, int w, int h)
      : texture_(SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h),
      , display_{0, 0, 0, 0}
    {
        SDL_GetRendererOutputSize(renderer, &display_.w, &display_.h);
        w = std::min(w, display_.w);
        h = std::min(h, display_.h);

        max_scroll_x = w - display_.w;
        max_scroll_y = h - display_.h;

        SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_BLEND);
    }

    Layer(Layer&& rhs) 
      : texture_(rhs.texture_)
      , display_(rhs.display)
      , max_scroll_x(rhs.max_scroll_x)
      , max_scroll_y(rhs.max_scroll_y) {
        rhs.texture_ = nullptr;
    }

    Layer& operator=(const Layer& rhs) {
      if(texture_) {SDL_DestroyTexture(texture_);}

      texture_ = rhs.texture_;
      display_ = rhs.display_;
      max_scroll_x = rhs.max_scroll_x;
      max_scroll_y = rhs.max_scroll_y;

      rhs.texture_ = nullptr;
    )

    Layer(const Layer& rhs) = delete;
    Layer& operator=(const Layer& rhs) = delete;

    ~Layer() {
      if(texture_) {SDL_DestroyTexture(texture_);}
    }

    // Subsequent draw calls will target this layer
    void makeCurrent(SDL_Renderer* renderer) {
        SDL_SetRenderTarget(renderer, texture_);
    }

    // Draws the layer to the currently active render target
    void commit(SDL_Renderer* renderer, const SDL_Rect * dstrect=nullptr) {
      SDL_RenderCopy(renderer, texture_, &display_, dstrect);
    }

    // Changes the offset of the layer
    void scrollTo(int x, int y) {
      display_.x = std::clamp(x, 0, max_scroll_x);
      display_.y = std::clamp(y, 0, max_scroll_y);
    }
private:
    SDL_Texture* texture_;
    SDL_Rect display_;
    int max_scroll_x;
    int max_scroll_y;
};