Estoy creando un juego que utiliza objetos de juego basados en componentes, y estoy teniendo dificultades para implementar una forma para que cada componente se comunique con su objeto de juego. En lugar de explicar todo de una vez, explicaré cada parte del código de muestra relevante:
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;
}
El GameObjectManager
tiene todos los objetos del juego y sus componentes. También es responsable de actualizar los objetos del juego. Lo hace actualizando los vectores componentes en un orden específico. Utilizo vectores en lugar de matrices para que prácticamente no haya límite en la cantidad de objetos de juego que pueden existir a la vez.
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;
}
La GameObject
clase sabe cuáles son sus componentes y puede enviarles mensajes.
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);
}
La Component
clase es puramente virtual, de modo que las clases para los diferentes tipos de componentes pueden implementar cómo manejar los mensajes y actualizarlos. También puede enviar mensajes.
Ahora surge el problema de cómo los componentes pueden llamar a las sendMessage
funciones en GameObject
y GameObjectManager
. Se me ocurrieron dos posibles soluciones:
- Dar
Component
un puntero a suGameObject
.
Sin embargo, dado que los objetos del juego están en un vector, los punteros podrían invalidarse rápidamente (lo mismo podría decirse del vector en GameObject
, pero es de esperar que la solución a este problema también pueda resolverlo). Podría poner los objetos del juego en una matriz, pero luego tendría que pasar un número arbitrario para el tamaño, que fácilmente podría ser innecesariamente alto y desperdiciar memoria.
- Dar
Component
un puntero a laGameObjectManager
.
Sin embargo, no quiero que los componentes puedan llamar a la función de actualización del administrador. Soy la única persona que trabaja en este proyecto, pero no quiero acostumbrarme a escribir código potencialmente peligroso.
¿Cómo puedo resolver este problema mientras mantengo mi código seguro y amigable con la caché?
Ser 'amigable con el caché' es una preocupación que tienen los grandes juegos . Esto parece ser una optimización prematura para mí.
Una forma de resolver esto sin ser 'amigable con la caché' sería crear su objeto en el montón en lugar de en la pila: use
new
punteros (inteligentes) para sus objetos. De esta manera, podrá hacer referencia a sus objetos y su referencia no se invalidará.Para una solución más amigable con la memoria caché, puede administrar la desasignación / asignación de objetos usted mismo y usar controladores para estos objetos.
Básicamente, en la inicialización de su programa, un objeto reserva una porción de memoria en el montón (llamémoslo MemMan), luego, cuando desea crear un componente, le dice a MemMan que necesita un componente de tamaño X, ' Lo reservaremos para usted, creará un identificador y mantendremos internamente dónde en su asignación está el objeto para ese identificador. Devolverá el identificador, y eso es lo único que mantendrá sobre el objeto, nunca un puntero a su ubicación en la memoria.
Como necesita el componente, le pedirá a MemMan que acceda a este objeto, lo que con gusto lo hará. Pero no guardes la referencia porque ...
Uno de los trabajos de MemMan es mantener los objetos cerca uno del otro en la memoria. Una vez cada pocos fotogramas del juego, puede decirle a MemMan que reorganice los objetos en la memoria (o podría hacerlo automáticamente cuando cree / elimine objetos). Actualizará su mapa de ubicación de manejador a memoria. Sus identificadores siempre serán válidos, pero si mantuvo una referencia al espacio de memoria (un puntero o una referencia ), encontrará solo desesperación y desolación.
Los libros de texto dicen que esta forma de administrar su memoria tiene al menos 2 ventajas:
Tenga en cuenta que la forma en que usa MemMan y cómo organizará la memoria internamente depende realmente de cómo usará sus componentes. Si itera a través de ellos según su tipo, querrá mantener los componentes por tipo, si itera a través de ellos en función de su objeto de juego, deberá encontrar una manera de asegurarse de que estén cerca de uno otro basado en eso, etc.
fuente