¡Buena pregunta! Antes de llegar a las preguntas específicas que hizo, diré: no subestime el poder de la simplicidad. Tenpn tiene razón. Tenga en cuenta que todo lo que intenta hacer con estos enfoques es encontrar una manera elegante de diferir una llamada a la función o desacoplar a la persona que llama de la persona que llama. Puedo recomendar corutinas como una forma sorprendentemente intuitiva de aliviar algunos de esos problemas, pero eso está un poco fuera de tema. A veces, es mejor simplemente llamar a la función y vivir con el hecho de que la entidad A está acoplada directamente a la entidad B. Ver YAGNI.
Dicho esto, he usado y estoy contento con el modelo de señal / ranura combinado con el simple paso de mensajes. Lo usé en C ++ y Lua para un título de iPhone bastante exitoso que tenía un calendario muy apretado.
Para el caso de señal / ranura, si quiero que la entidad A haga algo en respuesta a algo que hizo la entidad B (por ejemplo, abrir una puerta cuando algo muere), podría hacer que la entidad A se suscriba directamente al evento de muerte de la entidad B. O posiblemente la entidad A se suscribirá a cada uno de un grupo de entidades, incrementará un contador en cada evento disparado y abrirá la puerta después de que N de ellos hayan muerto. Además, "grupo de entidades" y "N de ellas" normalmente se definirían por diseñador en los datos de nivel. (Por otro lado, esta es un área donde las corutinas realmente pueden brillar, por ejemplo, WaitForMultiple ("Dying", entA, entB, entC); door.Unlock ();)
Pero eso puede volverse engorroso cuando se trata de reacciones estrechamente acopladas al código C ++, o eventos inherentemente efímeros del juego: infligir daño, recargar armas, depurar, retroalimentación de IA basada en la ubicación impulsada por el jugador. Aquí es donde el mensaje que pasa puede llenar los vacíos. Básicamente se reduce a algo así como "diga a todas las entidades en esta área que sufran daños en 3 segundos" o "cada vez que complete la física para descubrir a quién disparé, dígales que ejecuten esta función de script". Es difícil descubrir cómo hacerlo de manera agradable usando publicación / suscripción o señal / ranura.
Esto puede ser fácilmente exagerado (en comparación con el ejemplo de tenpn). También puede ser una hinchazón ineficiente si tienes mucha acción. Pero a pesar de sus inconvenientes, este enfoque de "mensajes y eventos" encaja muy bien con el código de juego con guión (por ejemplo, en Lua). El código del script puede definir y reaccionar a sus propios mensajes y eventos sin que el código C ++ se preocupe en absoluto. Y el código de script puede enviar fácilmente mensajes que activan el código C ++, como cambiar niveles, reproducir sonidos o incluso dejar que un arma establezca cuánto daño produce el mensaje TakeDamage. Me ahorró un montón de tiempo porque no tenía que perder el tiempo constantemente con luabind. Y me permitió mantener todo mi código luabind en un solo lugar, porque no había mucho. Cuando está correctamente acoplado,
Además, mi experiencia con el caso de uso # 2 es que es mejor manejarlo como un evento en la otra dirección. En lugar de preguntar cuál es la salud de la entidad, dispare un evento / envíe un mensaje cada vez que la salud haga un cambio significativo.
En términos de interfaces, por cierto, terminé con tres clases para implementar todo esto: EventHost, EventClient y MessageClient. EventHosts crea ranuras, EventClients se suscribe / conecta a ellos y MessageClients asocia un delegado con un mensaje. Tenga en cuenta que el objetivo delegado de un MessageClient no necesariamente tiene que ser el mismo objeto que posee la asociación. En otras palabras, MessageClients puede existir únicamente para reenviar mensajes a otros objetos. FWIW, la metáfora de host / cliente es algo inapropiada. Fuente / sumidero podrían ser mejores conceptos.
Lo siento, divagué un poco allí. Es mi primera respuesta :) Espero que tenga sentido.