Tengo el siguiente código, piensan tirador simple en C++:¿Cuál es la forma más elegante y eficiente de modelar una jerarquía de objetos del juego? (Diseño molesta)
// world.hpp
//----------
class Enemy;
class Bullet;
class Player;
struct World
{
// has-a collision map
// has-a list of Enemies
// has-a list of Bullets
// has-a pointer to a player
};
// object.hpp
//-----------
#include "world.hpp"
struct Object
{
virtual ~Object();
virtual void Update() =0;
virtual void Render() const =0;
Float xPos, yPos, xVel, yVel, radius; // etc.
};
struct Enemy: public Object
{
virtual ~Enemy();
virtual void Update();
virtual void Render() const;
};
// Bullet and Player are similar (they render and update differently per se,
/// but the behavior exposed to World is similar)
// world.cpp
//----------
#include "object.hpp"
// object.cpp
//-----------
#include "object.hpp"
dos problemas con este:
1, Los objetos del juego que saben de otros objetos del juego.
Tiene que haber una instalación que conozca todos los objetos. Puede o no tener que exponer TODOS los objetos, tiene que exponer algunos de ellos, dependiendo de los parámetros del investigador (posición, para uno). Se supone que esta instalación es Mundo.
Los objetos deben conocer el mundo en el que se encuentran para buscar información sobre colisiones y otros objetos.
Esto introduce una dependencia donde tanto los Objetos como la implementación Mundial tienen que acceder al encabezado del objeto, por lo que World no incluirá su propio encabezado directamente en lugar de incluir object.hpp (que a su vez incluye world.hpp). Esto me hace sentir incómodo: no creo que world.cpp deba volver a compilarse después de hacer un cambio en object.hpp. El mundo no parece que debería funcionar con Object. Se siente como un mal diseño, ¿cómo puede solucionarse?
2, Polimorfismo: ¿puede y debe usarse para algo más allá de la reutilización de código y la agrupación lógica de entidades de juego (y cómo)?
Enemigos, balas y reproductor se actualizarán y representarán de manera diferente, seguramente, de ahí la funcionalidad virtual Update() y Render() - una interfaz idéntica. Todavía se mantienen en listas separadas y la razón de esto es que su interacción depende de las listas de dos objetos que interactúan: dos enemigos rebotando entre sí, una bala y un enemigo se destruyen entre sí, etc.
Esto es funcionalidad eso está más allá de la implementación de Enemy and Bullet; esa no es mi preocupación aquí. Siento que más allá de sus interfaces idénticas hay un factor que separa Enemigos, balas y jugadores, y esto podría y debería expresarse de otra manera, permitiéndome crear una lista única para todos ellos, ¡ya que su interfaz es idéntica!
El problema es cómo distinguir una categoría de objeto de otra si está dentro de la misma lista. La identificación tidida u otra forma de identificación del tipo está fuera de discusión, pero ¿qué otra manera de hacerlo? O tal vez no hay nada de malo con el enfoque?
Gracias por los primeros dos párrafos; tiene mucho sentido y me ayudó a aclarar mi mente. He reescrito mi pregunta para aclarar qué quería hacer con el polimorfismo. – zyndor
El patrón Visitor es impresionante, gracias por señalarlo. Supongo que los métodos OnHit() son análogos al método Visit() del patrón y los Objects serían sus propios visitantes, ¿no? – zyndor
Ha sido difícil elegir una respuesta para aceptar como la mejor, ya que todos ellos obtuvieron buenos puntos, pero ninguno de ellos fue realmente completo. Sentí que el esquema de diseño sobre la interacción Objeto-Mundo fue la información más útil y la más relevante para mi pregunta. - – zyndor