Этот вопрос касается ошибки компилятора Visual Studio 2019 в приложении C ++, работающем в ОС Windows 10. Вероятно, это вопрос среднего или более высокого уровня.
Резюме
У меня есть эта функция:
template<typename T> T* Scene::GetComponent(EntityID entityID) {
CompTypeID typeID = TypeToID<T>();
if (HasComponent<T>(entityID)) {
CompIndex index = componentIndexes[typeID][entityID];
return components[typeID][index];
}
else return nullptr;
}
Эта функция приводит к ошибке компилятора C2440:
'return': невозможно преобразовать из '_Ty' в 'T *' с [_Ty = Component *] и [T = Name]
К этой ошибке также прикреплены эти два сообщения к нему:
сообщение: преобразование из базы в производное требует dynamic_cast или static_cast
сообщение: см. ссылку на создание экземпляра шаблона функции 'T * Scene :: GetComponent (EntityID)' в процессе компиляции with [T = Name]
Эта ошибка появляется 3 раза, каждый с T, равным Name, Transform и Sprite (каждый из компонентов, которые пока что были в моем игровом движке)
Compiler Ошибка C2440 в MSDN: https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/compiler-error-c2440?view=vs-2019
Описание функций
ECS Design
My Entity Component System хранит сущности и компоненты через два массива массивов.
Первый - componentIndexes
, который используется для индексации по объекту i. n ко второму массиву components
, который будет возвращен на основе индекса, компонента. По сути, если вы слышали о сжатых массивах, использующих «разреженный» массив и «плотный» массив, componentIndexes
- это мой разреженный массив, а components
- мой плотный массив для хранения компонентов в моей сцене.
Вкратце:
componentIndexes
сохраняет индекс для компонентов типа CompIndex, при условии: [ComponentType] [entityID] | См. График c 1A
components
хранит данные компонента типа Component *, при условии: [ComponentType] [index] | См. График c 1B
// An array of arrays of indexes into the component arrays, acquired via Entities and Component Types
// Y - std::array of sets with each element position being representative of a Component Type ID
// & each element value being X
// X - std::vector of indexes with each element position being representative of an Entity ID
// & each element value being representative of the position of the component in the component containter(components)
/****EXAMPLE:****
_E0_E1_E2_E3_ E = Entity ID | C = Component ID | i = Index
C0|i0|i1|~~|i2| **A ~~ IS REPRESENTATIVE OF A NULL/NAN VALUE (of actual value equal to -1)**
|--+--+--+--+- Entity 0 has only Component 0 at index 0
C1|~~|~~|i0|i1| Entity 1 has Components 0(indx1) and 2(indx0)
|--+--+--+--+- Entity 2 has only Component 1(indx0)
C2|~~|i0|~~|i1| Entity 3 has Components 0(indx2), 1(indx1), and 2(indx1)
****************/
std::array<std::vector<CompIndex>, TOTAL_COMPONENT_TYPES> componentIndexes;
Graphic 1A | componentIndexes
// An array of vectors of Component*s, holding each components' data
// Y - std::array of vectors with each element position being representative of a Component Type
// & each element value being X
// X - std::vector of Component*s with each element position being indexed into via Entity IDs
// & each element value holding the data concerning a certain component
/****EXAMPLE:****
_i0_i1_i2_i3_ CD = Component Data | C = Component ID | i = Index
C0|CD|CD|CD|~~| Component 0 has 3 components
|--+--+--+--+-
C1|CD|CD|~~|~~| Component 1 has 2 components
|--+--+--+--+-
C2|CD|CD|~~|~~| Component 2 has 2 components
****************/
std::array<std::vector<Component*>, TOTAL_COMPONENT_TYPES> components;
Graphic 1B | components
Построчно
CompTypeID typeID = TypeToID<T>();
Эта функция работает должным образом и возвращает перечисление CompTypeID, которое служит числовым представлением типов компонентов. Это можно использовать для индексации в мои массивы componentIndexes
и components
в зависимости от типа компонента, на который я нацелен.
if (HasComponent<T>(entityID)) {
. . .
}
else return nullptr;
Эта функция работает должным образом и возвращает логическое значение независимо от того, является ли объект имеет определенный c компонент. Он вернет false, если предоставленный тип T не является компонентом или если объект не имеет этого компонента. Else return nullptr - это самоочевидное состояние отказа этой функции.
CompIndex index = componentIndexes[typeID][entityID];
return components[typeID][index];
Эти функции получают и возвращают компонент типа Component*
. Как это происходит, объясняется в ECS Design . каждый элемент в components
должен быть сохраненным компонентом - это структуры, унаследованные от пустой структуры Component
(например, struct Name : Component {};
). Поскольку они унаследованы от родительского класса Component, я должен иметь возможность хранить каждый дочерний «компонент» в components
, потому что он хранит типы Component*
.
В чем, на мой взгляд, проблема
Тип T для GetComponent<T>(EntityID)
будет равен Name, Transform или Sprite. Тип хранения components
- Component*
. Хотя Name * s, Transform*
s, & Sprite*
s могут быть сохранены в Component*
, похоже, что компилятор не позволит мне присвоить данные типа Component*
данным типа Name*
(или другие компоненты) - поэтому, хотя теоретически я должен иметь возможность получить данные Name из моего массива Component*
s, у меня нет возможности сделать это из-за того, что я не могу отправить его из функции GetComponent как Name*
.
Я боюсь, что эта проблема может быть вызвана фундаментальным непониманием того, как работает хранение детей в массиве родителей - может быть, это сохраняет только родительскую часть ребенка?
Только я Можете представить, как это можно исправить одним из двух способов.
- Может быть, у меня есть способ получить информацию о дочернем элементе, учитывая местоположение указателя родителя - возможно, путем смещения указателя в памяти в зависимости от размера указателя родительского и дочернего, если они смежны друг с другом? Я не думаю, что C ++ предоставляет ссылки на память в этом качестве, возможно, такой подход был бы возможен только с использованием Assembly из-за того, что информация указателя скрыта за типами (например,
int*
или Component*
). - Другой способ может заключаться в использовании c умных указателей или (как сказано в сообщении, прикрепленном к ошибке) явного dynamic_cast-ing или static_cast-ing; но я понятия не имею, как работает любая из этих трех вещей, что они делают, как их использовать или когда их следует использовать.
Это мои лучшие предположения - они действительно выстрелы в тьма от меня. Если у вас есть идея получше, поделитесь ею, и я постараюсь разобраться в ней. Это ошибка компилятора, о которой я нашел мало документации или о которой говорил в своих поисках в Google, поэтому я решил, что было бы целесообразно внести свой вклад в это.
Заявление об ограничении ответственности
Иногда я обнаруживаю, что пропустил некоторую c основную информацию, которую большинство людей усваивает рано из-за структурированного учебного плана. Я предполагаю, что именно здесь я буду изучать интеллектуальные указатели и динамические c указатели.
Ресурсы
Scene.h
#ifndef SCENE_H
#define SCENE_H
#pragma once
#include "Components.h"
#include "Systems.h"
#include "Entity.h"
#include <array>
#include <vector>
#include <set>
#include <queue>
class Scene {
private:
// An array of arrays of indexes into the component arrays, acquired via Entities and Component Types
// Y - std::array of sets with each element position being representative of a Component Type ID
// & each element value being X
// X - std::vector of indexes with each element position being representative of an Entity ID
// & each element value being representative of the position of the component in the component container(components)
/****EXAMPLE:****
_E0_E1_E2_E3_ E = Entity ID | C = Component ID | i = Index
C0|i0|i1|~~|i2| **A ~~ IS REPRESENTATIVE OF A NULL/NAN VALUE (of actual value equal to -1)**
|--+--+--+--+- Entity 0 has only Component 0 at index 0
C1|~~|~~|i0|i1| Entity 1 has Components 0(indx1) and 2(indx0)
|--+--+--+--+- Entity 2 has only Component 1(indx0)
C2|~~|i0|~~|i1| Entity 3 has Components 0(indx2), 1(indx1), and 2(indx1)
****************/
std::array<std::vector<CompIndex>, TOTAL_COMPONENT_TYPES> componentIndexes;
// An array of vectors of Component*s, holding each components' data, acquired via Index & Component Type
// Y - std::array of vectors with each element position being representative of a Component Type
// & each element value being X
// X - std::vector of Component*s with each element position being indexed into via Entity IDs
// & each element value holding the data concerning a certain component
/****EXAMPLE:****
_i0_i1_i2_i3_ CD = Component Data | C = Component ID | i = Index
C0|CD|CD|CD|~~| Component 0 has 3 components
|--+--+--+--+-
C1|CD|CD|~~|~~| Component 1 has 2 components
|--+--+--+--+-
C2|CD|CD|~~|~~| Component 2 has 2 components
****************/
std::array<std::vector<Component*>, TOTAL_COMPONENT_TYPES> components;
std::queue<EntityID> oldEntityIDs;
std::array<std::queue<CompIndex>, TOTAL_COMPONENT_TYPES> oldCompIndexes;
EntityID GenerateNewEntityID();
CompIndex GenerateNewCompIndex(CompTypeID compType);
public:
Scene();
void Update();
EntityID NewEntity();
EntityID GetEntityID(std::string entityName);
Signature GetEntityComponentSignature(EntityID entityID);
std::vector<EntityID> GetEntitiesWithComponents(Signature components);
void DeleteEntity(EntityID entityID);
// template<typename T> T* AddComponent(EntityID entityID); // Same issue as GetComponent
template<typename T> T* GetComponent(EntityID entityID);
template<typename T> bool HasComponent(EntityID entityID);
bool HasComponents(Signature signComponents, EntityID entityID);
template<typename T> void DeleteComponent(EntityID entityID);
void DeleteComponents(Signature signComponents, EntityID entityID);
};
#endif // SCENE_H
Сцена. cpp
#include "Scene.h"
Scene::Scene() {
}
void Scene::Update() {
// RENDER SYSTEM
for (EntityID entityID : GetEntitiesWithComponents(RenderSystem::required)) {
RenderSystem::Update(GetComponent<Transform>(entityID), GetComponent<Sprite>(entityID));
}
}
EntityID Scene::GenerateNewEntityID() {
EntityID newID;
if (oldEntityIDs.empty()) {
newID = (EntityID)componentIndexes[0].size();
}
else {
newID = oldEntityIDs.front();
oldEntityIDs.pop();
}
return newID;
}
CompIndex Scene::GenerateNewCompIndex(CompTypeID typeID) {
CompIndex newIndex;
if (oldCompIndexes[typeID].empty()) {
newIndex = components[typeID].size();
}
else {
newIndex = oldCompIndexes[typeID].front();
oldCompIndexes[typeID].pop();
}
return newIndex;
}
EntityID Scene::NewEntity() {
// generate entity ID
EntityID entityID = GenerateNewEntityID();
// populate entity index
for (std::vector<CompIndex> componentSparse : componentIndexes) {
componentSparse[entityID] = NO_COMPONENT;
}
// add default entity components
//AddComponent<Name>(entityID);
//AddComponent<Transform>(entityID);
return entityID;
}
EntityID Scene::GetEntityID(std::string entityName) {
for (EntityID entityID : GetEntitiesWithComponents(IDName)) {
if (GetComponent<Name>(entityID)->name == entityName) {
return entityID;
}
}
return 0; // TODO: resolve "NO_ENTITY" case
}
Signature Scene::GetEntityComponentSignature(EntityID entityID) {
Signature signature;
// for each component type
for (CompTypeID i = (CompTypeID)0; i < TOTAL_COMPONENT_TYPES; i = (CompTypeID)(i + (CompTypeID)1)) {
// if component type is on entity
if (componentIndexes[i][entityID] != NO_COMPONENT) {
// mark that bit of the signature to true
signature.set(i);
}
}
return signature;
}
std::vector<EntityID> Scene::GetEntitiesWithComponents(Signature components) {
std::vector<EntityID> entities;
// TODO: write GetEntittiesWithComponents
return entities;
}
void Scene::DeleteEntity(EntityID entityID) {
// delete entity Components
DeleteComponents(GetEntityComponentSignature(entityID), entityID);
// delete entity indexes
for (std::vector<CompIndex> componentSparse : componentIndexes) {
componentSparse.erase(componentSparse.begin() + entityID);
}
// add ID to list of avaliable IDs
oldEntityIDs.push(entityID);
return;
}
// Same issue as GetComponent()
/*template<typename T> T* Scene::AddComponent(EntityID entityID) {
CompTypeID typeID = TypeToID<T>();
if (typeID == IDNotAComponent || componentsSparse[typeID][entityID] != NO_COMPONENT) {
return nullptr;
}
else {
CompIndex index = GenerateNewCompIndex(typeID);
componentsSparse[typeID][entityID] = index;
components[typeID][index] = new T; // TODO Ensure, does this work?? (T if not pointer could have issue?)
return &components[typeID][index];
}
}*/
template<typename T> T* Scene::GetComponent(EntityID entityID) {
CompTypeID typeID = TypeToID<T>();
if (HasComponent<T>(entityID)) {
CompIndex index = componentIndexes[typeID][entityID];
return components[typeID][index];
}
else return nullptr;
}
template<typename T> bool Scene::HasComponent(EntityID entityID) {
CompTypeID typeID = TypeToID<T>();
if (typeID == IDNotAComponent) {
return false;
}
else if (componentIndexes[typeID][entityID] == NO_COMPONENT) {
return false;
}
else return true;
}
bool Scene::HasComponents(Signature signComponents, EntityID entityID) {
// for each component type
for (CompTypeID i = (CompTypeID)0; i < TOTAL_COMPONENT_TYPES; i = (CompTypeID)(i + (CompTypeID)1)) {
// if component type is in component signature
if (signComponents.test(i)) {
// if component type is not on entity
if (componentIndexes[i][entityID] == NO_COMPONENT) {
return false;
}
}
}
return true;
}
template<typename T> void Scene::DeleteComponent(EntityID entityID) {
CompTypeID typeID = TypeToID(T);
if (typeID == nullptr || componentIndexes[typeID][entityID] != NO_COMPONENT) {
return;
}
else {
CompIndex index = componentIndexes[typeID][entityID];
// delete component
delete components[typeID][index];
components[typeID][index] = nullptr;
// delete component index
componentIndexes[typeID][entityID] = NO_COMPONENT;
oldCompIndexes[typeID].push(index);
return;
}
}
void Scene::DeleteComponents(Signature signComponents, EntityID entityID) {
// for each component type
for (CompTypeID i = (CompTypeID)0; i < TOTAL_COMPONENT_TYPES; i = (CompTypeID)(i + (CompTypeID)1)) {
// if component type is in component signature
if (signComponents.test(i)) {
// if component type is on entity
if (componentIndexes[i][entityID] != NO_COMPONENT) {
CompIndex index = componentIndexes[i][entityID];
// delete component
delete components[i][index];
components[i][index] = nullptr;
// delete component index
componentIndexes[i][entityID] = NO_COMPONENT;
oldCompIndexes[i].push(index);
}
}
}
return;
}
Entity.h
#ifndef ENTITY_H
#define ENTITY_H
#pragma once
#include <limits>
#include <cstdint>
typedef std::uint16_t EntityID;
// Maximum number of entities allowed in a scene
const EntityID MAX_ENTITIES = std::numeric_limits<EntityID>::max(); // 65535
#endif // ENTITY_H
Components.h
#ifndef COMPONENTS_H
#define COMPONENTS_H
#pragma once
#include <bitset>
#include "Name.h"
#include "Transform.h"
#include "Sprite.h"
enum CompTypeID {
IDName,
IDTransform,
IDSprite,
TOTAL_COMPONENT_TYPES,
IDNotAComponent
};
template<typename T> CompTypeID TypeToID() {
if (std::is_same<T, Name>()) {
return IDName;
}
else if (std::is_same<T, Transform>()) {
return IDTransform;
}
else if (std::is_same<T, Sprite>()) {
return IDSprite;
}
else return IDNotAComponent;
}
// Component Signature
typedef std::bitset<TOTAL_COMPONENT_TYPES> Signature;
// Component Index
typedef int CompIndex;
const CompIndex NO_COMPONENT = -1;
#endif // COMPONENTS_H
Имя.h
#ifndef NAME_H
#define NAME_H
#pragma once
#include "Component.h"
#include <string>
struct Name : Component {
public:
std::string name;
};
#endif // NAME_H
Компонент.h
#ifndef COMPONENT_H
#define COMPONENT_H
#pragma once
struct Component {
};
#endif // COMPONENT_H