(SFML) 继承问题 (C++)

(SFML) Issues with inheritance (C++)

所以,我正在使用 SFML,我正在尝试设置一个实体 class 和一个从它继承的玩家子 class,但这是我第一次使用继承我有问题: 首先,我有一个 AssetManager class,它是我从不同来源拼凑而成的,因为我还不太了解它们是如何工作的:

AssetManager.h:

class AssetManager {
public:
    AssetManager();
    
    static sf::Texture& LoadTexture(std::string const& path);
    static sf::SoundBuffer& LoadSoundBuffer(std::string const& path);
    static sf::Font& LoadFont(std::string const& path);

private:
    std::map<std::string, sf::Texture> m_Textures;
    std::map<std::string, sf::SoundBuffer> m_SoundBuffers;
    std::map<std::string, sf::Font> m_Fonts;

    static AssetManager* sInstance;
};

不过你可以只看纹理相关的部分,这里是AssetManager.cpp的部分:

AssetManager* AssetManager::sInstance = nullptr;

AssetManager::AssetManager() {
    assert(sInstance == nullptr);
    sInstance = this;
}

sf::Texture& AssetManager::LoadTexture(std::string const& path) {

    auto& texMap = sInstance->m_Textures;
    auto pairFound = texMap.find(path);

    if (pairFound != texMap.end()) {
        return pairFound->second;
    }
    else {
        auto& texture = texMap[path];
        texture.loadFromFile(path);
        return texture;
    }

}

然后 class 的对象包含在 Sprite class 中,这有助于为我声明精灵。

Sprite.h:

class Sprite {
public:
    AssetManager manager;
    sf::Texture m_Texture;
    sf::Sprite m_Sprite;

    sf::Vector2f sprite_scale;
    sf::Vector2u original_size;
    sf::Vector2f texture_size;

    Sprite(std::string path,sf::IntRect rect,sf::Vector2f size);
    
};

Sprite.cpp:

Sprite::Sprite(std::string path, sf::IntRect rect, sf::Vector2f size) {
    m_Texture = sf::Texture(AssetManager::LoadTexture(path));
    m_Sprite.setTextureRect(rect);
    m_Sprite.setTexture(m_Texture);

    original_size = m_Texture.getSize();

    texture_size.x = static_cast<float>(original_size.x);
    texture_size.y = static_cast<float>(original_size.y);

    sprite_scale.x = size.x / texture_size.x;
    sprite_scale.y = size.y / texture_size.y;

    m_Sprite.setScale(sf::Vector2f(sprite_scale.x, sprite_scale.y));
    m_Sprite.setOrigin(sf::Vector2f(original_size.x / 2, original_size.y / 2));

}

然后,Sprite class 的对象本身包含在 Entity class.

Entity.h:

class Entity {
public:
    Sprite entity_sprite;
    int health;
    float speed;
    bool collision = false;

    bool entity_collision(sf::Sprite entity2_sprite);// Entity.cpp only contains the declaration of this function so far so no need to post it.
    

};

现在,由于我不明白的原因,我无法在声明 entity_sprite 对象时直接将任何参数分配给它,我只能在没有参数的情况下声明它,尽管 class 没有默认构造函数。 但是我可以使用赋值来解决它: Entity entity_sprite = Entity("res/wildcard.png", { 0,0,36,63 }, { 36,63 });

但这不是主要问题,直接使用 Entity class 不是我想要做的,我正在尝试编写 Player sub-class 并改用它:

Player.h:

class Player:public Entity {
    Player() {
        entity_sprite = Sprite("res/wildcard.png", { 0,0,36,63 }, { 36,63 });
    }
};

现在我再次无法直接将参数直接分配给对象,因为 call of an object of a class type without appropriate operator() or conversion function to pointer-to-function type(有趣的是,如果我回到 Entity 对象并在那里分配参数并且假装错误不存在,由 Player class 产生的错误更改为 Too many arguments' and 'Too many initializers

这太令人困惑了。

尽管如此,我又一次能够使用赋值来绕过它,和以前完全一样,除了这次我得到一个错误 the default constructor "Entity" cannot be referenced -- its a deleted function.,所以我回到 Entity class 并添加一个像这样的空构造函数:Entity() { } 但是此构造函数又给我另一个错误提示 no default constructor exists for class "Sprite",即使实体 class 并不完全继承自 Sprite class,所以我进一步回到 Sprite class 并给它一个空的构造函数:Sprite(){},错误似乎消失了,直到我声明一个 Player main.cpp 文件中的对象并尝试编译并获取指向 AssetManager.cpp 中以下行的调试错误:assert(sInstance == nullptr);

看似简单的任务却有这么多问题,我该如何摆脱困境?

好的,在查阅了SFML论坛后,我将代码重构为如下:

Sprite.h:

#include "AssetManager.h"

class Sprite{
public:
    sf::Sprite m_sprite;

    sf::Vector2f sprite_scale;
    sf::Vector2u original_size;
    sf::Vector2f texture_size;

    Sprite(){}
    sf::Sprite set_sprite(sf::Texture& tx, sf::IntRect rect, sf::Vector2f size);
};

Sprite.cpp:

#include "Sprite.h"

sf::Sprite Sprite::set_sprite(sf::Texture& tx, sf::IntRect rect, sf::Vector2f size) {

    sf::Sprite spr(tx);

    spr.setTextureRect(rect);

    original_size =tx.getSize();

    texture_size.x = static_cast<float>(original_size.x);
    texture_size.y = static_cast<float>(original_size.y);

    sprite_scale.x = size.x / texture_size.x;
    sprite_scale.y = size.y / texture_size.y;

    spr.setScale(sf::Vector2f(sprite_scale.x, sprite_scale.y));
    spr.setOrigin(sf::Vector2f(original_size.x / 2, original_size.y / 2));

    return spr;
}

Entity.h:

#pragma once
#include "Sprite.h"
#include "collision.h"
#include "Timer.h"

class Entity {
public:
    Sprite spr;
    sf::Sprite entity_sprite;
    int health;
    float max_speed;
    sf::Vector2f speed;
    sf::Vector2f direction;
    float acceleration;
    bool collision = false;
    timer t;
    float acc_time;
};

Player.h:

#pragma once
#include "Entity.h"

class Player:public Entity {
public:
    Player();
    float acc_time = t.accumulate_time();
    void keyboard_controls();
    void mouse_controls(sf::Vector2f cursor);
};

Player.cpp:

#include "Player.h"
#include <math.h>


Player::Player() {
    
    speed = { 0,0 };
    acceleration = 2;
    max_speed = 500 + acceleration;
    entity_sprite = spr.set_sprite(AssetManager::LoadTexture("res/wildcard.png"), { 0,0,60,63 }, { 60,63 });
}

简而言之,Sprite class' 构造函数被替换为具有完全相同作用的方法,这样我就可以简单地声明一个没有参数的 Sprite 对象Entity class,派生的 Player class 不会有任何问题,因为我不会被要求为 Sprite 和 Entity 创建默认构造函数classes.