Я создаю игру, в которой используются игровые объекты на основе компонентов, и мне трудно реализовать способ взаимодействия каждого компонента со своим игровым объектом. Вместо того, чтобы объяснять все сразу, я объясню каждую часть соответствующего примера кода:
class GameObjectManager {
public:
//Updates all the game objects
void update(Time dt);
//Sends a message to all game objects
void sendMessage(Message m);
private:
//Vector of all the game objects
std::vector<GameObject> gameObjects;
//vectors of the different types of components
std::vector<InputComponent> input;
std::vector<PhysicsComponent> ai;
...
std::vector<RenderComponent> render;
}
GameObjectManager
Содержит все игровые объекты и их компоненты. Он также отвечает за обновление игровых объектов. Это достигается путем обновления векторов компонентов в определенном порядке. Я использую векторы вместо массивов, чтобы практически не было ограничений на количество игровых объектов, которые могут существовать одновременно.
class GameObject {
public:
//Sends a message to the components in this game object
void sendMessage(Message m);
private:
//id to keep track of components in the manager
const int id;
//Pointers to components in the game object manager
std::vector<Component*> components;
}
GameObject
Класс знает , что его компоненты и отправлять им сообщения.
class Component {
public:
//Receives messages and acts accordingly
virtual void handleMessage(Message m) = 0;
virtual void update(Time dt) = 0;
protected:
//Calls GameObject's sendMessage
void sendMessageToObject(Message m);
//Calls GameObjectManager's sendMessage
void sendMessageToWorld(Message m);
}
Component
Класс чисто виртуальные , так что классы для различных типов компонентов могут осуществлять обработку сообщений и обновление. Он также может отправлять сообщения.
Теперь возникает проблема того, как компоненты могут вызывать sendMessage
функции в GameObject
и GameObjectManager
. Я придумал два возможных решения:
- Дайте
Component
указатель на свойGameObject
.
Однако, поскольку игровые объекты находятся в векторе, указатели могут быстро стать недействительными (то же самое можно сказать и о векторе GameObject
, но, надеюсь, решение этой проблемы также может решить эту проблему). Я мог бы поместить игровые объекты в массив, но тогда мне пришлось бы передавать произвольное число для размера, который мог бы быть слишком большим и тратить память.
- Дайте
Component
указатель наGameObjectManager
.
Однако я не хочу, чтобы компоненты могли вызывать функцию обновления менеджера. Я единственный человек, работающий над этим проектом, но я не хочу иметь привычку писать потенциально опасный код.
Как я могу решить эту проблему, сохраняя мой код безопасным и дружественным к кешу?
Быть «дружественным кешу» - это проблема больших игр . Мне кажется, это преждевременная оптимизация.
Один из способов решить эту проблему, не будучи «дружественным к кешу», - создать объект в куче, а не в стеке: использовать
new
и (умные) указатели для ваших объектов. Таким образом, вы сможете ссылаться на ваши объекты, и их ссылка не будет признана недействительной.Для более дружественного к кешу решения вы могли бы самостоятельно управлять выделением / распределением объектов и использовать дескрипторы этих объектов.
По сути, при инициализации вашей программы объект резервирует кусок памяти в куче (назовем его MemMan), затем, когда вы хотите создать компонент, вы сообщаете MemMan, что вам нужен компонент размера X, он ' Я зарезервирую его для вас, создам дескриптор и сохраню внутренне, где в его расположении находится объект для этого дескриптора. Он вернет дескриптор, и это единственное, что вы сохраните в объекте, никогда не указатель на его местоположение в памяти.
Когда вам нужен компонент, вы попросите MemMan получить доступ к этому объекту, что он с радостью сделает. Но не держите ссылку на это, потому что ....
Одна из задач MemMan - хранить объекты в памяти рядом друг с другом. Раз в несколько игровых кадров вы можете указать MemMan переставлять объекты в памяти (или он может делать это автоматически при создании / удалении объектов). Он обновит свою карту расположения маркера в память. Ваши дескрипторы всегда будут действительны, но если вы сохранили ссылку на пространство памяти ( указатель или ссылку ), вы найдете только отчаяние и запустение.
В учебниках говорится, что такой способ управления памятью имеет как минимум 2 преимущества:
Имейте в виду, что то, как вы используете MemMan и как вы будете организовывать внутреннюю память, действительно зависит от того, как вы будете использовать свои компоненты. Если вы будете перебирать их в зависимости от их типа, вы захотите сохранить компоненты по типу, если вы перебираете их в зависимости от их игрового объекта, вам нужно будет найти способ убедиться, что они близки к одному. другой, основанный на этом, и т.д ...
источник