我试图制作一个简单的游戏,当我尝试渲染SDL_Texture
时,我得到了一个莫名其妙的错误。我已将所有内容设置正确,我能够使用SDL_RenderClear
成功清除屏幕,并且我的纹理不为空,因此应该已正确创建。但是当我尝试调用render()
函数时出现错误,SDL_GetError()
返回"纹理无效"。
编辑:我现在已经按照要求创建了一个MCVE,我测试了它以验证它是否重现了错误。它应该在路径上显示图像" gfx / grid.bmp"在窗口中,但它给了我错误。这是完整的MCVE:
#include <SDL.h>
#include <string>
#include <iostream>
#undef main
class Texture{
private:
SDL_Texture* texture;
SDL_Renderer* renderer;
int width;
int height;
public:
Texture(){
texture = nullptr;
}
Texture (SDL_Renderer* ren, std::string path){
texture = nullptr;
SDL_Surface* surface = nullptr;
surface = SDL_LoadBMP(path.c_str());
if(surface == nullptr) return;
renderer = ren;
texture = SDL_CreateTextureFromSurface( renderer, surface );
if(texture == 0) return;
width = surface->w;
height = surface->h;
SDL_FreeSurface(surface);
std::cout << "Texture '" << path << "' successfully loaded\n";
}
bool loaded(){
return texture != 0;
}
bool render(int x = 0, int y = 0){
SDL_Rect dest;
dest.x = x;
dest.y = y;
dest.w = width;
dest.h = height;
return SDL_RenderCopy(renderer, texture, nullptr, &dest) == 0;
}
void unload(){
if(loaded()){
SDL_DestroyTexture(texture);
texture = nullptr;
renderer = nullptr;
width = 0;
height = 0;
}
}
~Texture(){
unload();
}
};
class CApp{
private:
bool running = true;
SDL_Window* window = nullptr;
SDL_Renderer* renderer = nullptr;
Texture graphic_grid;
bool render = true;
public:
int OnExecute();
bool OnInit();
void OnEvent(SDL_Event* event);
void OnRender();
void OnCleanup();
};
bool CApp::OnInit(){
if(SDL_Init(SDL_INIT_EVERYTHING) < 0){
return false;
}
if((window = SDL_CreateWindow("Simple Tic-Tac-Toe", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 600, 600, SDL_WINDOW_SHOWN)) == nullptr){
return false;
}
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if(renderer == nullptr){
std::cout << "Renderer failed to load! Error: " << SDL_GetError();
return false;
}
SDL_SetRenderDrawColor( renderer, 0xFF, 0xFF, 0xFF, 0xFF );
graphic_grid = Texture(renderer, "gfx/grid.bmp");
if(!graphic_grid.loaded()){
std::cout << "Image failed to load! Error: " << SDL_GetError();
return false;
}
std::cout << "Initialized fully\n";
return true;
}
void CApp::OnEvent(SDL_Event* event){
switch(event->type == SDL_QUIT){
running = false;
}
}
void CApp::OnRender(){
if(!render) return;
if(SDL_RenderClear(renderer) == 0){
std::cout << "Successfully cleared screen\n";
}
if(!graphic_grid.loaded()){
std::cout << "Texture not loaded!\n";
}else{
std::cout << "Texture not null!\n";
}
//This is the place where I get the "Invalid texture" error, everything else works fine
if(!graphic_grid.render()){
std::cout << "Failed to render image! Error: " << SDL_GetError() << '\n';
}
SDL_RenderPresent(renderer);
render = false;
}
void CApp::OnCleanup(){
graphic_grid.unload();
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
renderer = nullptr;
window = nullptr;
SDL_Quit();
}
int CApp::OnExecute(){
if(OnInit() == false){
return -1;
}
SDL_Event event;
while(running){
while(SDL_PollEvent(&event)){
OnEvent(&event);
}
OnRender();
}
OnCleanup();
return 0;
}
int main(){
CApp theApp;
return theApp.OnExecute();
}
我已经四处寻找,无法找到任何可以解释我的错误的内容。我能找到的最接近的是this question,但是这个错误是由渲染器在纹理之前被摧毁造成的,这不是我的情况。
我真的希望有人可以告诉我可能导致此错误的原因,任何建议都表示赞赏!
答案 0 :(得分:1)
您的问题是您在此行中复制纹理
graphic_grid = Texture(renderer, "gfx/grid.bmp");
由于您没有定义复制赋值运算符或移动赋值,因此编译器会为您提供一个,它只是按原样复制成员。
一旦Texture(renderer, "gfx/grid.bmp");
创建的临时文件被销毁,graphic_grid.texture
就不再有效。您可以通过删除Texture
析构函数来测试这一点,您应该发现所有内容都按预期工作(当然,您实际上 ta fix)。
你需要实现移动赋值或复制赋值构造函数,如果你的Texture
不是要复制的话,最好是前者(因为你需要赋值,你还应该提供移动/复制构造函数)同样,按照五条规则的其他两个移动/复制方法,或者至少明确地delete
它们。)
请阅读0/3/5的规则
Rule-of-Three becomes Rule-of-Five with C++11?
如果您正在使用GCC,-Weffc++
标志会警告您这类狡猾的类设计的某些部分。
我个人尝试尽可能地将句柄和指针包装成RAII类型,只要每个成员都对自己负责,你通常不需要复制和移动构造函数。 std::unique_ptr
对此非常有效,例如:
template<typename T, void(*destroy)(T*), typename Ptr = std::unique_ptr<T, decltype(destroy)>>
struct Handle : public Ptr
{
Handle(T*&& p): Ptr{p, destroy}
{
if (!*this)
throw std::runtime_error{SDL_GetError()};
}
};
using Window_h = Handle<SDL_Window, SDL_DestroyWindow>;
using Surface_h = Handle<SDL_Surface, SDL_FreeSurface>;
// And so on for other handles
可以使用如下
Window_h window{SDL_CreateWindow(
"Hello world",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
800, 600,
0
)};
如果你要如上所述包装SDL_Texture
那么你的Texture
类将有一个隐式移动构造函数和移动赋值运算符,它只是开箱即用(你必须摆脱它但是,renderer
成员,或者以不同的方式处理它,例如使其成为std::shared_ptr
,因此除非其引用计数为0,否则它不会随纹理一起被销毁。
还有一些关于代码的奇怪事情,比如过早地从构造函数返回。如果无法构造对象,则应抛出异常。
答案 1 :(得分:0)
我不知道你要调用哪个构造函数来创建Texture类的对象, 我认为你正在创建的表面存在一些问题。 我已经尝试了一些代码并进行了一些更改,但它运行正常,
void CApp::OnRender(){
SDL_Window* pWindow;
SDL_Renderer* render;
SDL_Surface* surface;
SDL_Init(SDL_INIT_VIDEO);
// Create wndow
pWindow = SDL_CreateWindow(
"Dummy",
100,
100,
640, 480,
SDL_WINDOW_SHOWN);
// Create renderer
render = SDL_CreateRenderer(pWindow, -1, 0);
// Create Surface
surface = SDL_GetWindowSurface(pWindow);
// Creating object of class texture
Texture graphic_grid(render, surface);
if(SDL_RenderClear(render) == 0){
std::cout << "Successfully cleared screen\n";
}
if(!graphic_grid.loaded()){
std::cout << "Texture not loaded!\n";
}else{
std::cout << "Texture not null!\n";
}
if(!graphic_grid.render()){
std::cout << "Failed to render image! Error: " << SDL_GetError() << '\n';
}
for(int i = 0; i < 9; i++){
if(grid[i] == 0) continue;
int x = i%3*200;
int y = i/3*200;
if(grid[i] == 1){
graphic_x.render(x, y);
}else{
graphic_o.render(x, y);
}
}
SDL_RenderPresent(render);
render = false;
}