Как избежать виртуальных функций в C при разработке Entity System
Я хочу разработать систему сущностей для моей игры, используя только C и небольшое подмножество C++. Многие люди делают это, используя наследование, но я наткнулся на помеченные союзы и узнал, что вы можете добиться подобного результата таким образом, потому что виртуальные функции медленны для игр (так что я слышал от Кейси Муратори и Джона Блоу, двух разработчиков игр, из которых я рисую много вдохновения).
struct Unit {
int type;
int hp;
union {
struct {
int kingID;
} soldier_data;
struct {
string name;
} king_data;
};
}
Джон Блоу избегает виртуальных функций, используя некоторые функции из своего нового языка Jai, который еще не выпущен, поэтому приведенный выше пример - моя единственная идея.
Например, я мог написать только одну функцию Update и использовать тип для различения сущностей. Это была бы очень большая функция, но мы избегаем виртуальных функций.
Вещи в моей игре, скажем, солдаты и деревья. Деревья статичны и в основном ничего не делают, поэтому я бы написал другую более легкую структуру для сущностей деревьев, чтобы сэкономить память, и использовал объединения для хранения разных типов деревьев:
struct TreeEntity {
Texture* texture;
union {
struct {
int height;
} pineTree_data;
struct {
string name;
} coconutTree_data;
}
}
Проблема, с которой я сталкиваюсь, состоит в том, что, если и солдаты и деревья кликабельны? Если бы я использовал наследование, я бы просто использовал функцию Entity* selectEntity() и проверил бы тип экземпляра после этого, но с моим подходом я немного потерян.
Мой подход плох? Должен ли я придерживаться виртуальных функций или есть способ справиться с этим?
1 ответ
Вы спрашиваете, как создать общий Entity* selectEntity()
функция, которая может вернуть либо Unit*, либо Tree* (или, возможно, другие вещи).
Вы можете сделать это, возвращая указатель базового класса. Unit и Tree наследуются от Entity, и Entity может содержать небольшое перечисление, например:
enum class EntityType : uint8_t {
Unit, Tree, // ...
};
Если вы не хотите использовать наследование, вместо этого вы можете просто иметь общую начальную подпоследовательность переменных-членов в каждом типе сущности, например:
struct Unit {
EntityType type;
// ...
};
Это эквивалентно версии наследования с точки зрения разметки памяти, и вы можете вернуть EntityType * из selectEntity()
, который на самом деле будет указывать на первого члена юнита или дерева.