【问题标题】:Rendering single thing without having to clear entire screen in sdl渲染单个事物而无需在 sdl 中清除整个屏幕
【发布时间】:2021-07-18 13:37:55
【问题描述】:

sdl 有什么层吗?

我的意思是像在 Photoshop 中一样,我们有多个图层,可以在一个图层上绘制而不影响另一个图层,

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

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

【问题讨论】:

  • 有趣。我的赌注是“不”。但我会关注这个问题并希望被证明是错误的。
  • 您是否接受必须清除并重新创建每一层的解决方案?对于每一帧?对于该层的每次更改?您是否接受需要每帧清除屏幕但未更改的图层仍然存在并且可以重复使用的解决方案?您会接受与您描述/要求完全一致但比通常的清除和重新渲染所有内容的方法慢 100 倍的解决方案吗?
  • @Yunnosch 我不确定我是否听懂了,您能否提供一些参考或示例来说明您所说的内容?
  • 好的,我试试。场景 A:您将背景渲染到一个“层”中。您将播放器渲染到第二层。您将敌人渲染到第三层。您将所有三层都放在屏幕上。看起来不错。玩家移动,但既不是背景也不是敌人。您只需将播放器重新渲染到第二层(清除后),清除屏幕,将所有三层都放到屏幕上并获得一个播放器不同的帧,其他一切都相同。这可以接受吗?
  • 使用SDL_SetRenderTarget(),您可以轻松创建自己的图层系统。我将起草一个示例作为答案。

标签: c++ c sdl sdl-2


【解决方案1】:

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

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

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

例子:

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

【讨论】:

  • 仅提及“老派视差”就会赢得我的支持(除其他外,因为它暗示了可能的后续问题......)。可悲的是,你只能从我这里得到一个。干得好。
猜你喜欢
  • 1970-01-01
  • 2011-06-05
  • 2014-05-23
  • 2017-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-10
  • 1970-01-01
相关资源
最近更新 更多