c++ - 从数组访问 SDL_Rect 成员时出现垃圾值?

标签 c++ arrays object sdl

我一直在关注 LazyFoo 的 SDL tutorials (并且还添加了我自己的组织和编码风格)。当我到达他的animation tutorial我决定做一个单独的类来存储与动画算法相关的变量和方法,而不是全局变量。他使用 SDL_Rects 数组来定义 Sprite 表上不同 Sprite 的边界,所以我使用 SDL_Rect 指针将数组存储在我的自定义类中。当我编译所有东西时我没有看到动画,当我编译原始源代码时我看到了。当我开始调试时,我发现当我渲染 Sprite 时,矩形实际上充满了垃圾,即使当我初始化它们时矩形也很好。我曾多次尝试简化问题,但我在更简单的环境中重新创建错误的每一种方法实际上都按预期工作!因此,考虑到这一点,我为大量代码表示歉意,因为我似乎无法减少问题。

纹理.h

#ifndef TEXTURE_H
#define TEXTURE_H

#include <SDL2/SDL.h>
#include <string>

class Animation {
public:
    Animation(SDL_Renderer* renderer);
    ~Animation();

    void load(std::string path, int frames, SDL_Rect* clips),
         free(),
         render(int x, int y),
         next_frame();
private:
    SDL_Renderer* _renderer=NULL;
    SDL_Rect* _clips=NULL;
    SDL_Texture* _texture=NULL;
    int _frame=0, _frames=0, _width=0, _height=0;
};

#endif

纹理.cpp

#include <stdio.h>
#include <SDL2/SDL_image.h>
#include "texture.h"
#include "error.h"

Animation::Animation(SDL_Renderer* renderer) {
    _renderer = renderer;
}

Animation::~Animation() {
    free();
    _renderer = NULL;
}

void Animation::load(std::string path, int frames, SDL_Rect* clips) {
    free();
    SDL_Texture* texture = NULL;

    SDL_Surface* surface = IMG_Load(path.c_str());
    if (!surface)
        throw ErrorIMG("Could not load image "+path);
    SDL_SetColorKey(surface, SDL_TRUE,
                    SDL_MapRGB(surface->format, 0, 0xFF, 0xFF));

    texture = SDL_CreateTextureFromSurface(_renderer, surface);
    if (!texture)
        throw ErrorSDL("Could not create texture from image "+path);

    _width = surface->w;
    _height = surface->h;
    SDL_FreeSurface(surface);

    _frames = frames;
    _clips = clips;
    printf("clips[%d]: w: %d h: %d\n", 0, _clips[0].w, _clips[0].h);
}

void Animation::free() {
    if (_texture) {
        SDL_DestroyTexture(_texture);
        _texture = NULL;
        _clips = NULL;
        _frames = 0;
        _frame = 0;
        _width = 0;
        _height = 0;
    }
}

void Animation::render(int x, int y) {
    SDL_Rect crect = _clips[_frame/4];
    printf("in render (clips[%d]): w: %d, h: %d\n", _frame/4, crect.w, crect.h);
    SDL_Rect render_space = {x, y, crect.w, crect.h};
    SDL_RenderCopy(_renderer, _texture, &_clips[_frame], &render_space);
}

void Animation::next_frame() {
    SDL_Rect crect = _clips[_frame/4];
    printf("in next frame (clips[%d]): w: %d, h: %d\n", _frame/4, crect.w, crect.h);
    ++_frame;
    if (_frame/4 >= _frames)
        _frame = 0;
}

游戏.h

#ifndef GAME_H
#define GAME_H

#include "texture.h"

class Game {
public:
    Game();
    ~Game();
    void main();
private:
    void load_media();

    SDL_Window* _window=NULL;
    SDL_Renderer* _renderer=NULL;
    Animation* _anim=NULL;

    const int SCREEN_WIDTH=640, SCREEN_HEIGHT=480;
};

#endif

游戏.cpp

#include <SDL2/SDL_image.h>
#include "game.h"
#include "error.h"

void Game::main() {
    load_media();

    bool has_quit = false;
    SDL_Event event;

    while (!has_quit) {
        while (SDL_PollEvent(&event))
            if (event.type == SDL_QUIT)
                has_quit = true;

        SDL_SetRenderDrawColor(_renderer, 0xff, 0xff, 0xff, 0xff);
        SDL_RenderClear(_renderer);

        _anim->render(100, 100);
        _anim->next_frame();
        SDL_RenderPresent(_renderer);
    }
}

Game::Game() {
    if (SDL_Init(SDL_INIT_VIDEO))
        throw ErrorSDL("SDL could not initialize");

    _window = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_UNDEFINED,
                               SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH,
                               SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
    if (!_window)
        throw ErrorSDL("Window could not be created");

    Uint32 render_flags = SDL_RENDERER_ACCELERATED;
    render_flags |= SDL_RENDERER_PRESENTVSYNC;
    _renderer = SDL_CreateRenderer(_window, -1, render_flags);
    if (!_renderer)
        throw ErrorSDL("Renderer could not be created");
    SDL_SetRenderDrawColor(_renderer, 0xff, 0xff, 0xff, 0xff);

    if (!(IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG))
        throw ErrorIMG("SDL_image could not initialize");
}

Game::~Game() {
    delete _anim;

    SDL_DestroyRenderer(_renderer);
    SDL_DestroyWindow(_window);
    _renderer = NULL;
    _window = NULL;

    IMG_Quit();
    SDL_Quit();
}

void Game::load_media() {
    const int nclips = 4;
    SDL_Rect clips[nclips];

    for (int i=0; i < nclips; i++) {
        clips[i].x = i*64;
        clips[i].y = 0;
        clips[i].w = 64;
        clips[i].h = 164;
    }

    _anim = new Animation(_renderer);
    _anim->load("sheet.png", nclips, &clips[0]);
}

最佳答案

您正在存储一个指向临时对象的指针。 SDL_Rect* clips您传递给 Animation::load 的指针分配给成员 _clips函数返回后使用。为了使其正常工作,指向的数据需要Animation 一样长的时间存在。类正在使用它。问题出现在这里:

void Game::load_media() {
    const int nclips = 4;
    SDL_Rect clips[nclips];
    ...
    _anim->load("sheet.png", nclips, &clips[0]);
}

在这段代码中,clips是一个本地变量。这意味着它在 load_media() 结束时被销毁,该位置的内存内容将变成垃圾。

您可以通过多种方式解决此问题。一个简单的方法是使用 std::vector<SDL_Rect>而不是 SDL_Rect* . std::vector可以安全地复制并为您管理其内部结构。您的新代码可能如下所示:

class Animation {
    ...
    std::vector<SDL_Rect> _clips;
    ...
}


void Animation::load(std::string path, int frames, std::vector<SDL_Rect> clips) {
    ...
    _clips = clips;
    ...
}

void Game::load_media() {
    const int nclips = 4;
    std::vector<SDL_Rect> clips;
    clips.resize(nclips);
    ...
    _anim->load("sheet.png", nclips, clips);
}

别忘了 #include <vector> . std::vector 的文档是here .注意 std::vector有一个 size()可能取代 frames 的方法它无处不在。

关于c++ - 从数组访问 SDL_Rect 成员时出现垃圾值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52103944/

相关文章:

c++ - 将字符串转换为 wstring [没有 locale::global 的俄罗斯符号]

c++ - 我如何在不使用 out 扩展名的情况下给出命令行参数,而不是 ./a.out 3 4 而是像 ./a 3 4

c++ - 如何正确读取子进程的 stdout/stderr 输出?

C MPI数组传递

python - 如何在Python中获取数组中所有NaN元素的索引?

javascript - 比较对象数组中的属性值

c++ - 使用 BOOST ASIO 在异步服务器中发送局部变量

java - 从 txt.file 读入二维数组的数据值被翻转

java - 为什么 int 作为对象传递?

javascript - Vue : How to implement Table search