You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用SDL无法绘制多个精灵对象?技术问题求助

问题根源

你的代码核心问题在于**GameObject类使用了默认的拷贝构造函数和赋值运算符**,导致对象拷贝时执行浅拷贝:多个GameObject实例会共享同一个SDL_Texture*指针。当你把TestObj1、TestObj2推入vector时,vector中存储的是原对象的浅拷贝,这些拷贝对象和原对象共用同一块纹理内存。

虽然在循环执行期间原对象还未销毁,但这种设计存在两个致命问题:

  1. 程序结束时,原对象和vector中的拷贝对象都会调用析构函数销毁同一个纹理,触发双重释放(可能导致崩溃);
  2. 若vector发生扩容(比如后续添加更多对象),旧的拷贝对象会被销毁,连带销毁纹理,导致剩余对象的纹理指针失效。

这就是你只能看到最后一个对象的原因——前面的对象纹理已经被提前销毁,无法正常渲染。

修复方案

推荐使用智能指针存储对象的方案,彻底避免拷贝问题,同时保证资源安全管理:

步骤1:修改GameObject类,禁用拷贝语义

在类中显式禁用拷贝构造和赋值运算符,避免意外的浅拷贝:

#include <iostream>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <memory>  // 新增:包含智能指针头文件
#include <SDL.h>
#include <SDL_image.h>
using namespace std;

class GameObject
{
private:
    SDL_Texture* texture;  // 精灵纹理
    SDL_Rect rect;         // 位置与尺寸
    SDL_Renderer* renderer;// 渲染器指针
public:
    GameObject(SDL_Renderer* renderer, const char* filepath, int x, int y) : texture(nullptr), renderer(renderer) {
        rect.x = x;
        rect.y = y;
        // 新增错误检查
        SDL_Surface* surface = IMG_Load(filepath);
        if (!surface) {
            cout << "加载图片失败: " << IMG_GetError() << endl;
            return;
        }
        texture = SDL_CreateTextureFromSurface(renderer, surface);
        if (!texture) {
            cout << "创建纹理失败: " << SDL_GetError() << endl;
        }
        SDL_FreeSurface(surface);
        if (texture) {
            SDL_QueryTexture(texture, nullptr, nullptr, &rect.w, &rect.h);
        }
    }

    ~GameObject() {
        if (texture) {
            SDL_DestroyTexture(texture);
        }
    }

    // 禁用拷贝构造和赋值运算符
    GameObject(const GameObject&) = delete;
    GameObject& operator=(const GameObject&) = delete;

    // 允许移动构造(可选,不影响当前场景)
    GameObject(GameObject&&) = default;
    GameObject& operator=(GameObject&&) = default;

    void draw() {
        if (texture) {
            SDL_RenderCopy(renderer, texture, nullptr, &rect);
        }
    }
};

步骤2:修改main函数,使用智能指针容器

把vector<GameObject>改为vector<unique_ptr<GameObject>>,直接存储对象的唯一指针,避免拷贝:

int main(int, char**)
{
    const short FPS = 60;
    const int frameDelay = 1000 / FPS;
    Uint32 frameStart;
    int frameTime;

    SDL_Init(SDL_INIT_EVERYTHING);

    SDL_Window* window = SDL_CreateWindow("title", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 960, SDL_WINDOW_SHOWN);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);

    // 修改为智能指针容器
    vector<unique_ptr<GameObject>> gameObjects;
    // 直接在容器中创建对象,避免拷贝
    gameObjects.push_back(make_unique<GameObject>(renderer, "assets//test.png", 10, 20));
    gameObjects.push_back(make_unique<GameObject>(renderer, "assets//test.png", 80, 20));

    bool quit = false;
    while (!quit)
    {
        frameStart = SDL_GetTicks();
        SDL_Event ev;
        while (SDL_PollEvent(&ev))
        {
            switch (ev.type)
            {
            case SDL_QUIT:
                quit = true;
                break;
            }
        }
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);

        // 遍历智能指针容器,调用draw方法
        for (auto& gameObject : gameObjects) {
            gameObject->draw();
        }
        SDL_RenderPresent(renderer);
        frameTime = SDL_GetTicks() - frameStart;
        if (frameDelay > frameTime)
        {
            SDL_Delay(frameDelay - frameTime);
        }
    }
    return 0;
}
额外说明

如果坚持要使用值语义存储对象(不推荐,会重复加载纹理浪费资源),可以实现深拷贝构造函数:在拷贝时重新加载纹理文件,保证每个对象持有独立的纹理。但这种方式会增加内存开销和加载时间,不如智能指针方案高效。

内容的提问来源于stack exchange,提问作者Michael Scott

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 05:24:53