Как упоминалось выше, первая проблема заключается в том, что вы должны хранить EntityComponent * или умный указатель EntityComponent, чтобы сохранить данные производного объекта от стирания при назначении базовому классу. Таким образом, тип контейнера может быть одним из:
std::unordered_map<int, EntityComponent*>
std::unordered_map<int, std::unique_ptr<EntityComponent>>
std::unordered_map<int, std::shared_ptr<EntityComponent>>
Кроме того, виртуальный деструктор должен быть добавлен в EntityComponent для правильного удаления производных классов:
class EntityComponent
{
virtual ~EntityComponent() = default;
// other stuff ...
};
Когда вы можете использовать dynamic_cast для проверьте, действительно ли EntityComponent является TransformComponent:
EntityComponent* ec = GetComponentById(id); // suppose that function returns TransformComponent or EntityComponent
TransformComponent* tc = dynamic_cast<TransformComponent*>(ec);
if (tc != nullptr)
{
std::cout << "tc is transform component" << std::endl;
tc->GetPosition();
tc->GetScale();
}
Но, вероятно, для игрового движка dynamic_cast может быть медленным в некоторых случаях. Если вы знаете все ваши классы, производные от EntityComponent, вы можете добавить поле Тип, чтобы проверить фактический тип объекта, а затем привести его к нему с помощью static_cast, который работает во время компиляции:
enum class ComponentType
{
BASE,
TRANSFORM,
// other types
};
class EntityComponent
{
ComponentType _type;
public:
inline ComponentType GetType() const { return this->_type; }
inline EntityComponent(ComponentType type = ComponentType::BASE) : _type(type) { }
virtual ~EntityComponent() = default;
// other staff
};
class TransformComponent : public EntityComponent
{
public:
inline TransformComponent() : EntityComponent(ComponentType::TRANSFORM) { }
// other staff
};
int main()
{
EntityComponent* ec = GetComponentById(0);
if (ec->GetType() == ComponentType::TRANSFORM)
{
std::cout << "ec is transform component" << std::endl;
TransformComponent* tc = static_cast<TransformComponent*>(ec);
tc->GetPosition();
tc->GetScale();
}
}
Существует несколько способов добавить TransformComponent для сопоставления как std::unique_ptr<EntityComponent>
:
std::unordered_map<int, std::unique_ptr<EntityComponent>> components;
// emplace raw pointer
int id = 0;
components.emplace(id, new TransformComponent());
// release unique_ptr of derived class and emplace
auto tc = std::make_unique<TransformComponent>();
components.emplace(id, tc.release());
// create unique_ptr of base class and move it to map
std::unique_ptr<EntityComponent> ec(new TransformComponent());
components.insert(id, std::move(ec));