渲染单个事物而不必在 sdl 中清除整个屏幕
Rendering single thing without having to clear entire screen in sdl
是否有 sdl 之类的层?
按图层我的意思是像在 photoshop 中我们有多个图层并且可以在一个图层上绘图而不影响另一个图层,
例如,如果我有一个 main_layer
、一个 background_layer
和一个 enemy_layer
,其中主要玩家重新定位(如用户移动角色)、静态背景渲染和敌人渲染可以分别进行吗?
而不是必须清除整个屏幕然后一遍又一遍地把所有内容放回去?即改变一件事而不影响另一件事?有人能指出我正确的方向吗?
您可以使用渲染目标实现自己的图层系统。
- 为每一层创建纹理渲染目标。
- 绘制到层的渲染目标以更新它。
- 每一帧,将每一层绘制到屏幕上。你还需要事先清除最后一帧。
值得注意的是这里有一个递减点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;
};
是否有 sdl 之类的层?
按图层我的意思是像在 photoshop 中我们有多个图层并且可以在一个图层上绘图而不影响另一个图层,
例如,如果我有一个 main_layer
、一个 background_layer
和一个 enemy_layer
,其中主要玩家重新定位(如用户移动角色)、静态背景渲染和敌人渲染可以分别进行吗?
而不是必须清除整个屏幕然后一遍又一遍地把所有内容放回去?即改变一件事而不影响另一件事?有人能指出我正确的方向吗?
您可以使用渲染目标实现自己的图层系统。
- 为每一层创建纹理渲染目标。
- 绘制到层的渲染目标以更新它。
- 每一帧,将每一层绘制到屏幕上。你还需要事先清除最后一帧。
值得注意的是这里有一个递减点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;
};