Para que los componentes puedan actualizar cada marco (y dejar esta funcionalidad fuera de los componentes que no lo necesitan), tuve la idea de hacer un componente UpdateComponent. Otros componentes como MovableComponent
(que tiene velocidad) heredarían de la IUpdatable
clase abstracta. Esto obliga MovableComponent
a implementar un Update(gametime dt)
método y otro RegisterWithUpdater()
que le da UpdateComponent
un puntero al MovableComponent
. Muchos componentes podrían hacer esto y luego UpdateComponent
llamar a todos sus Update(gametime dt)
métodos sin tener que preocuparse por quiénes o qué son.
Mis preguntas son:
- ¿Parece esto algo normal o usado por alguien? No puedo encontrar nada sobre el tema.
- ¿Cómo podría mantener un orden para los componentes como la física y luego el cambio de posición? ¿Es esto incluso necesario?
- ¿Cuáles son otras formas de asegurar que los componentes que deben procesarse en cada cuadro se procesen realmente?
EDITAR
Creo que voy a considerar cómo darle al administrador de la entidad una lista de tipos que se pueden actualizar. Luego, TODOS los componentes de ese tipo pueden actualizarse en lugar de administrarlo por entidad (que de todos modos son solo índices en mi sistema).
Todavía. Mis preguntas siguen siendo válidas para mí. No sé si esto es razonable / normal, o qué tienden a hacer los demás.
Además, ¡la gente de Insomniac es increíble! /EDITAR
Código resumido para el ejemplo anterior:
class IUpdatable
{
public:
virtual void Update(float dt) = 0;
protected:
virtual void RegisterAsUpdatable() = 0;
};
class Component
{
...
};
class MovableComponent: public Component, public IUpdatable
{
public:
...
virtual void Update(float dt);
private:
...
virtual void RegisterWithUpdater();
};
class UpdateComponent: public Component
{
public:
...
void UpdateAll();
void RegisterUpdatable(Component* component);
void RemoveUpdatable(Component* component);
private:
...
std::set<Component*> updatables_;
};
fuente
Respuestas:
Uno de los principales beneficios de un sistema de componentes es la capacidad de aprovechar los patrones de almacenamiento en caché: buena icache y predicción porque ejecuta el mismo código una y otra vez, buena dcache porque puede asignar los objetos en grupos homogéneos y porque las tablas v cualquiera, mantente caliente.
La forma en que ha estructurado sus componentes, esta ventaja desaparece por completo y, de hecho, puede convertirse en una responsabilidad de rendimiento en comparación con un sistema basado en herencia, ya que está haciendo muchas más llamadas virtuales y más objetos con vtables.
Lo que debe hacer es almacenar grupos por tipo e iterar cada tipo de forma independiente para realizar actualizaciones.
No es tan común en los juegos grandes porque no es ventajoso. Es común en muchos juegos, pero técnicamente no es interesante, por lo que nadie escribe al respecto.
El código en lenguajes como C ++ tiene una forma natural de ordenar la ejecución: escriba las declaraciones en ese orden.
En realidad, eso no tiene sentido porque ningún sistema de física robusto está estructurado de esa manera: no se puede actualizar un solo objeto de física. En cambio, el código se vería más como:
fuente
De lo que estás hablando es razonable y bastante común, creo. Esto podría darle más información.
fuente
Me gustan estos enfoques:
En breve: evite mantener el comportamiento de actualización dentro de los componentes. Los componentes no son comportamiento. El comportamiento (incluidas las actualizaciones) se puede implementar en algunos subsistemas de instancia única. Ese enfoque también podría ayudar a procesar por lotes comportamientos similares para múltiples componentes (tal vez usando instrucciones parallel_for o SIMD en datos de componentes).
El método IUpdatable idea y Update (gametime dt) parece demasiado restrictivo e introduce dependencias adicionales. Puede estar bien si no está utilizando el enfoque de subsistemas, pero si los utiliza, IUpdatable es un nivel de jerarquía redundante. Después de todo, el MovingSystem debe saber que debe actualizar los componentes de Ubicación y / o Velocidad directamente para todas las entidades que tienen estos componentes, por lo que no hay necesidad de algún componente intermedio IUpdatable.
Pero podría usar el componente Actualizable como un mecanismo para omitir la actualización de algunas entidades a pesar de que tengan componentes de Ubicación y / o Velocidad. El componente Actualizable podría tener un indicador de bool que se puede establecer
false
y que indicaría a cada subsistema compatible con Actualizable que esta entidad en particular actualmente no debe actualizarse (aunque en ese contexto, Freezable parece ser el nombre más apropiado para el componente).fuente