Как избежать виртуальных функций в 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(), который на самом деле будет указывать на первого члена юнита или дерева.

Другие вопросы по тегам