Eso generalmente se hace mediante mensajes. Puede encontrar muchos detalles en otras preguntas en este sitio, como aquí o allá .
Para responder a su ejemplo específico, un camino a seguir es definir una pequeña Messageclase que sus objetos puedan procesar, por ejemplo:
struct Message
{
    Message(const Objt& sender, const std::string& msg)
        : m_sender(&sender)
        , m_msg(msg) {}
    const Obj* m_sender;
    std::string m_msg;
};
void Obj::Process(const Message& msg)
{
    for (int i=0; i<m_components.size(); ++i)
    {
        // let components do some stuff with msg
        m_components[i].Process(msg);
    }
}
De esta manera no estás "contaminando" tu Objinterfaz de clase con métodos relacionados con componentes. Algunos componentes pueden elegir procesar el mensaje, algunos simplemente pueden ignorarlo.
Puede comenzar llamando a este método directamente desde otro objeto:
Message msg(obj1, "EmitForce(5.0,0.0,0.0)");
obj2.ProcessMessage(msg);
En este caso, obj2's Physicselegirá el mensaje y realizará el procesamiento que sea necesario. Cuando termine, ya sea:
- Envíe un mensaje "SetPosition" a sí mismo, que el Positioncomponente elegirá;
- O acceda directamente al Positioncomponente para modificaciones (bastante incorrecto para un diseño basado en componentes puros, ya que no puede suponer que cada objeto tiene unPositioncomponente, pero elPositioncomponente podría ser un requisitoPhysics).
En general, es una buena idea retrasar el procesamiento real del mensaje a la actualización del siguiente componente. Procesarlo de inmediato podría significar enviar mensajes a otros componentes de otros objetos, por lo que enviar solo un mensaje podría significar rápidamente una pila de espagueti inextricable.
Probablemente tendrá que ir a un sistema más avanzado más adelante: colas de mensajes asíncronos, envío de mensajes a un grupo de objetos, registro / anulación de registro por componente, etc.
La Messageclase puede ser un contenedor genérico para una cadena simple como se muestra arriba, pero el procesamiento de cadenas en tiempo de ejecución no es realmente eficiente. Puede buscar un contenedor de valores genéricos: cadenas, enteros, flotantes ... Con un nombre o mejor aún, un ID, para distinguir diferentes tipos de mensajes. O también puede derivar una clase base para satisfacer necesidades específicas. En su caso, podría imaginar un EmitForceMessagederivado del Messagevector de fuerza deseado y agregarlo, pero tenga cuidado con el costo de tiempo de ejecución de RTTI si lo hace.
                 
                
dynamic_castpuede convertirse en un cuello de botella, pero no me preocuparé por ahora. Todavía puede optimizar esto más adelante si se convierte en un problema. Los identificadores de clase basados en CRC funcionan como un encanto.Lo que hice para resolver un problema similar al que muestra es agregar algunos controladores de componentes específicos y agregar algún tipo de sistema de resolución de eventos.
Entonces, en el caso de su objeto "Física", cuando se inicializa, se agregaría a un administrador central de objetos de Física. En el ciclo del juego, este tipo de gerentes tienen su propio paso de actualización, por lo que cuando este PhysicsManager se actualiza, calcula todas las interacciones físicas y las agrega a una cola de eventos.
Después de producir todos sus eventos, puede resolver su cola de eventos simplemente verificando lo que sucedió y tomando acciones según corresponda, en su caso, debería haber un evento que diga que los objetos A y B interactuaron de alguna manera, por lo que llama a su método emitForceOn.
Ventajas de este método:
Contras:
Espero que esto ayude.
PD: Si alguien tiene una forma más limpia / mejor de resolver esto, realmente me gustaría escucharlo.
fuente
Algunas cosas a tener en cuenta en este diseño:
fuente