我一直在关注克里斯托弗·赫伯茨洞穴故事的重建 (他的 GitHub 页面:https://github.com/chebert/cavestory-screencast, 和播放列表 https://www.youtube.com/watch?v=IufkC1IRY2Q&list=PL006xsVEsbKjSKBmLu1clo85yLrwjY67X&index=1&pp=iAQB), 为了在更实际的环境中学习 C++,以及如何为每个函数使用更多的类/标头方法编写简单的游戏,而不是将所有内容写在一个庞大的“main.cpp”文件中。
一切都很顺利,但是这一切都是在 SDL 1.2 中完成的,而不是 2.0 中。现在,在最新版本中从头开始一个新项目后,我尝试在屏幕上显示单个位图文件,可以通过按 Esc 键关闭该文件。它构建得很好,代码如下:
游戏.cpp:
...
void Game::eventLoop(){
Graphics graphics;
SDL_Event event;
sprite_.reset(new Sprite("sprites/tilesets/dirt_tile.bmp", 0, 0, 128, 128));
bool running = true;
while(running){
const int start_time_ms = SDL_GetTicks();
while (SDL_PollEvent(&event) ){
switch (event.type){
case SDL_KEYDOWN:
if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)
running = false;
break;
default:
break;
}
}
update();
draw(graphics);
const int ms_per_frame = 1000 / kFPS;
const int elapsed_time_ms = SDL_GetTicks() - start_time_ms;
if(elapsed_time_ms < ms_per_frame){
SDL_Delay(ms_per_frame - elapsed_time_ms);
}
}
}
...
void Game::draw(Graphics& graphics){
sprite_->draw(graphics, 400, 300);
graphics.flip();
}
图形.h:
struct SDL_Surface;
struct SDL_Rect;
struct Graphics{
typedef SDL_Texture* SurfaceID;
Graphics();
~Graphics();
void blitSurface(SurfaceID source, SDL_Rect* source_rectangle, SDL_Rect* destination_rectangle);
void flip();
private:
SDL_Window* window_;
SDL_Renderer* renderer_;
};
#endif // GRAPHICS_H
图形.cpp:
...
Graphics::Graphics(){
window_ = SDL_CreateWindow("Swordguy",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
kScreenWidth, kScreenHeight, 0);
renderer_ = SDL_CreateRenderer(window_, -1, 0);
}
Graphics::~Graphics(){
SDL_DestroyWindow(window_);
SDL_DestroyRenderer(renderer_);
}
void Graphics::blitSurface(SurfaceID source, SDL_Rect* source_rectangle, SDL_Rect* destination_rectangle){
SDL_RenderCopy(renderer_, source, source_rectangle, destination_rectangle);
}
void Graphics::flip(){
SDL_RenderPresent(renderer_);
}
sprite.h:
struct Graphics;
struct Sprite{
Sprite(const std::string& file_path,
int source_x, int source_y,
int width, int height);
// ~Sprite();
void draw(Graphics& graphics, int x, int y);
private:
SDL_Rect source_rect_;
SDL_Texture* sprite_sheet_;
};
#endif // SPRITE_H
精灵.cpp:
Sprite::Sprite(const std::string& file_path,
int source_x, int source_y,
int width, int height){
SDL_Surface* sprite_sheet_ = SDL_LoadBMP(file_path.c_str() );
source_rect_.x = source_x;
source_rect_.y = source_y;
source_rect_.w = width;
source_rect_.h = height;
}
/*
Sprite::~Sprite(){
SDL_FreeSurface(sprite_sheet_);
}
*/
void Sprite::draw(Graphics& graphics, int x, int y){
SDL_Rect destination_rectangle;
destination_rectangle.x = x;
destination_rectangle.y = y;
graphics.blitSurface(sprite_sheet_, &source_rect_, &destination_rectangle);
}
之后,程序以某种方式构建并运行,但屏幕完全空白。
在此处输入问题之前我所做的事情:
从https://wiki.libsdl.org/SDL2/MigrationGuide检查了SDL2/迁移指南,并在graphics.cpp中添加了单独的窗口和渲染器函数,以及析构函数中的橡皮擦,
在第 9 行的 sprite.cpp 中添加了 SDL_Surface*,因为没有它,SDL_Surface.h 不会将其转换为 SDL_Texture*,
在第 18 行关闭了 sprite.cpp 中释放表面的析构函数,只是为了检查程序,因为它无法将 SDL_Texture* 转换为 SDL_Surface*,
检查 *.bmp 文件是否正常,它是从 Lazy Foo 的代码中正确加载的:https://lazyfoo.net/tutorials/SDL/02_getting_an_image_on_the_screen/index.php。文件本身也是一个适当的 24 位 *.bmp 文件,
感觉解决方案真的很简单,但我只是不知道该怎么办了。有什么帮助或提示吗?
问题似乎在于从 SDL 1.2 迁移到 SDL 2.0,特别是在处理 SDL 2.0 中的渲染和纹理系统时。让我们分解一下问题,看看如何解决:
SDL_Texture 与 SDL_Surface: 在 SDL2 中,渲染到屏幕涉及使用 SDL 渲染器的
SDL_Texture
,而不是 SDL 1.2 中使用的 SDL_Surface。您的代码尝试将 SDL_Surface
用作 SDL_Texture
,这是行不通的。
加载BMP作为纹理: 在 Sprite 类中,您将 BMP 加载到 SDL_Surface 中,但没有将此表面转换为 SDL2 用于渲染的 SDL_Texture。
让我们尝试解决这些问题:
将 SDL_Surface 转换为 SDL_Texture:
修改
Sprite
中的sprite.cpp
构造函数,将加载的BMP表面转换为SDL_Texture:
Sprite::Sprite(const std::string& file_path,
int source_x, int source_y,
int width, int height) {
SDL_Surface* loadedSurface = SDL_LoadBMP(file_path.c_str());
if (loadedSurface == nullptr) {
// Handle error
return;
}
// Convert the surface to a texture
sprite_sheet_ = SDL_CreateTextureFromSurface(graphics.renderer_, loadedSurface);
// Free the loaded surface now that we have a texture
SDL_FreeSurface(loadedSurface);
source_rect_.x = source_x;
source_rect_.y = source_y;
source_rect_.w = width;
source_rect_.h = height;
}
请注意,Graphics 类应该提供一个方法或直接访问渲染器,例如,
graphics.renderer_
,用于此转换。
清除渲染器:
在使用渲染器绘制到屏幕之前,最好先清除渲染器:
void Graphics::flip(){
SDL_RenderClear(renderer_); // Clear the renderer
SDL_RenderPresent(renderer_);
}
检查BMP路径和错误处理:
确保 BMP 文件的路径正确并且相对于可执行文件或工作目录存在。此外,您可以使用 SDL_GetError() 打印和检查与 SDL 函数相关的任何错误:
SDL_Surface* loadedSurface = SDL_LoadBMP(file_path.c_str());
if (loadedSurface == nullptr) {
std::cerr << "Unable to load BMP: " << SDL_GetError() << std::endl;
return;
}
进行这些更改后,重新编译并运行您的代码。 BMP 应正确显示在屏幕上。请记住,在 SDL 2.0 中,使用
SDL_Texture
和渲染 API 提供硬件加速,与 SDL 1.2 中基于表面的方法相比,这将更加高效。