使用抽象基类指向SDL_Rect类成员的指针的问题

Issue with a pointer to a SDL_Rect class member using an abstract base class

本文关键字:Rect 成员 指针 问题 SDL 抽象 基类      更新时间:2023-10-16

我有一个抽象基类,它有几个继承者,并且我创建了一个抽象基类的指针数组,这些指针将指向继承者:

抽象基类:

class Tile              
{
    public:
        bool isPassable;
        void setTileSet( SDL_Texture *Texture );
        SDL_Rect* clip;
};

继承人:

class Floor: public Tile
{
    public:
        bool isPassable = true;
        SDL_Rect clip = { 20, 0, 20, 20 };
};

指针数组:

Tile * dungeon[ z ][ x ][ y ] = {{{ nullptr }}};

当我试图通过指针数组访问成员SDL_Rect clip来blit tile时,什么也没有发生。程序编译并运行,但屏幕仍然默认为黑色:

dungeon[ 0 ][ 0 ][ 0 ] = &floor;
Draw::renderTexture( TILESET, REN, 0, 0, dungeon[ 0 ][ 0 ][ 0 ]->clip );

然而,当我只是使用类Floor来访问clip时,图像会很好地blit:

Draw::renderTexture( TILESET, REN, 0, 0, floor.clip );

我认为指针抽象基类可能没有加载,但当我试图编写代码以清除它时:

SDL_Rect error = { 0, 0, 20, 20 };
if( dungeon[ 0 ][ 0 ][ 0 ] != nullptr )
{
    Draw::renderTexture( TILESET, REN, 0, 0, dungeon[ 0 ][ 0 ][ 0 ]->clip );   
}
else
{
    Draw::renderTexture( TILESET, REN, 0, 0, &error );                          
}

屏幕保持黑色

这是Draw::renderTexture函数,虽然我怀疑这是问题:

void Draw::renderTexture(SDL_Texture *texture, SDL_Renderer *render, int x, int y, SDL_Rect *clipping )     
{
SDL_Rect destination;
destination.x = x;
destination.y = y;
destination.w = clipping->w;
destination.h = clipping->h;
SDL_RenderCopy( render, texture, clipping, &destination ); 

每层都有两个版本的isPassable: Floor::isPassableTile::isPassable。一个被初始化,另一个没有。你也有两个版本的剪辑。Floor::clip未初始化,Tile::clip未初始化。所以当你将地下城元素称为Tile时,它自然会访问未初始化的元素,然后灾难就来了。

你的代码说if( dungeon[ 0 ][ 0 ][ 0 ] != nullptr )...没有帮助,因为Tile::clip不是null;未初始化。

解决方案是消除isPassable和clip从Tile或Floor,所以你将只有一个副本,并不能访问错误的一个。

如果你想让它保持平铺,这些事情需要修复:

  • 初始化它们,可能默认为false和NULL,在Tile的actor中——所有类的所有成员都需要初始化。这就是为什么你的if语句没有帮助
  • 让Floor的actor调用Tile的actor,传递适当的值,如果Floors需要不同的初始值:Floor () : Tile (true, 20, 0, 20, 20) {},也许。

您需要首先强制转换您的数组成员,以便它知道它正在查看哪个成员。目前你有一个Tile实例的数组,所以当你把dungeon[ 0 ][ 0 ][ 0 ]->clip传递给渲染函数时,系统会认为你指的是基类。

如果你把它改为(Floor*)(dungeon[ 0 ][ 0 ][ 0 ])->clip,它应该工作。

然而,我要指出,这看起来像一个非常糟糕的设计,可能是因为您还没有完全理解继承和多态性,尽管您可能有一些理由这样做。

我个人会这样修改类,首先是基类:

class Tile              
{
    public:
        Tile() :
            m_passable(true) {}
        Tile(bool passable, SDL_Rect& clip) :
            m_passable(passable),
            m_clip(clip) {}
        virtual ~Tile() {}
        inline void SetTileSet(SDL_Texture *texture) { m_texture = texture; }
        inline SDL_Texture* GetTileSet() { return m_texture; }
        inline void SetClip(SDL_Rect& clip) { m_clip = clip; }
        inline SDL_Rect& GetClip() { return m_clip; }
        inline void SetPassable(bool passable) { m_passable = passable; } 
        inline bool IsPassable() { return m_passable; }
    protected:  
        bool m_passable;
        SDL_Rect m_clip;
        SDL_Texture* m_texture;
    private:
        Tile(Tile const&) = delete;
        Tile& operator=(Tile const&) = delete;
};

,然后是继承类:

class Floor : public Tile
{
    public:
        Floor(bool passable, SDL_Rect& clip) :
            Tile(passable, clip);
    protected:
    private:
        Floor(Floor const&) = delete;
        Floor& operator=(Floor const&) = delete;
};

当你想要一个新的Floor实例时,只需执行:

SDL_Rect clip = { 0, 0, 20, 20 };
Floor* myFloor = new Floor(true, clip);

然后将其放入数组中,只需执行以下命令:

dungeon[ 0 ][ 0 ][ 0 ] = (Tile*)myFloor;

最后,如果你做了这些更改,你应该通过引用传递给你的渲染函数:

void Draw::renderTexture(SDL_Texture* texture, SDL_Renderer* render, int x, int y, SDL_Rect& clipping )     
{
    SDL_Rect destination = { x, y, clipping.w, clipping.h };
    SDL_RenderCopy( render, texture, &clipping, &destination );
}