Estoy haciendo un juego de lanzamiento de Java para teléfonos Android. Tengo 20 enemigos extraños en el juego que tienen comportamientos únicos pero ciertos comportamientos son reutilizados por la mayoría de ellos. Necesito modelar balas, explosiones, asteroides, etc. y otras cosas que también actúen como enemigos. Mi diseño actual favorece composición sobre la herencia y representa objetos juego un poco como esto:¿Cómo modelar la representación y el comportamiento del objeto de juego de forma modular?
// Generic game object
class Entity
{
// Current position
Vector2d position;
// Regular frame updates behaviour
Behaviour updateBehaviour;
// Collision behaviour
Behaviour collideBehaviour;
// What the entity looks like
Image image;
// How to display the entity
Renderer renderer;
// If the entity is dead and should be deleted
int dead;
}
abstract class Renderer { abstract void draw(Canvas c); }
abstract class Behaviour { abstract void update(Entity e); }
Para simplemente dibujar cualquier cosa que se almacena la imagen como entidad, se puede adjuntar un procesador sencillo, por ejemplo,
class SimpleRenderer extends Renderer
{
void draw(Canvas c)
{
// just draw the image
}
}
Para que la entidad volar sobre al azar cada cuadro, simplemente adjuntar un comportamiento así:
class RandomlyMoveBehaviour extends Behaviour
{
void update(Entity e)
{
// Add random direction vector to e.position
}
}
O añadir un comportamiento más complejo como esperando hasta que el jugador está cerca antes de recalada en:
class SleepAndHomeBehaviour extends Behaviour
{
Entity target;
boolean homing;
void init(Entity t) { target = t; }
void update(Entity e)
{
if (/* distance between t and e < 50 pixels */)
{
homing = true;
// move towards t...
}
else
{
homing = false;
}
}
}
Estoy muy contento con este diseño hasta ahora. Es agradable y flexible en cuanto a que puedes, por ejemplo, modularizar la última clase para que pueda suministrar el comportamiento "dormido" y el comportamiento "despierto" para que pueda decir algo como nuevo WaitUntilCloseBehaviour (jugador, 50/píxeles /, nuevo MoveRandomlyBehaviour(), nuevo HomingBehaviour()). Esto hace que sea realmente fácil hacer nuevos enemigos.
La única parte que me molesta es cómo se comunican los procesadores y los procesadores. Por el momento, Entity contiene un objeto Image que un Behavior podría modificar si así lo deseara. Por ejemplo, un comportamiento podría cambiar el objeto entre una imagen de reposo y despierto y el procesador dibujaría la imagen. No estoy seguro de cómo esto va a escalar embargo ej .:
Qué pasa con un enemigo torreta similar al que se enfrenta a una cierta dirección? Creo que podría agregar un campo de rotación a la entidad que Behavior and Renderer pueda modificar/leer.
¿Qué pasa con un tanque donde el cuerpo del tanque y el arma del tanque tienen direcciones separadas? Ahora el renderizador necesita acceso a dos rotaciones desde alguna parte y las dos imágenes a usar. En realidad, no desea hinchar la clase Entity con esto si solo hay un tanque.
¿Qué pasa con un enemigo que brilla mientras su arma se recarga? Realmente desea almacenar el tiempo de recarga en el objeto Comportamiento, pero la clase Renderer no puede verlo.
estoy teniendo problemas para pensar en formas de modelar lo anterior por lo que los procesadores y los comportamientos pueden mantenerse un tanto separada. El mejor enfoque que puedo pensar es hacer que los objetos de comportamiento contengan el estado adicional y el objeto del procesador, luego los objetos de comportamiento llaman al método de dibujar representadores y pasan el estado extra (por ejemplo, rotación) si así lo desean.
Podría, por ejemplo, tener un objeto de Comportamiento similar a un tanque que quiere un Renderer similar a un tanque, donde este último pide las dos imágenes y dos rotaciones para dibujar. Si quisieras que tu tanque fuera simplemente una imagen, simplemente escribirías una subclase Renderer que ignorara las rotaciones.
¿Alguien puede pensar en alguna alternativa?Realmente quiero simplicidad. Como es un juego, la eficiencia también puede ser una preocupación si, por ejemplo, dibujar una sola imagen enemiga de 5x5, cuando tengo 50 enemigos volando a 60 fps, implica muchas capas de llamadas a funciones.
Gracias. No lo he perfilado, pero si quiero que haya muchos objetos volando (por ejemplo, viñetas), probablemente querría evitar el uso de hashmaps para actualizar cada comportamiento en Android. Una de las ventajas que he notado al mantener el almacenamiento variable local para los objetos de comportamiento es que hace que el sistema sea mucho más robusto y fácil de probar, ya que no tiene que preocuparse por los comportamientos combinados que juegan con las mismas variables. – BobbyJim
de acuerdo. Como dije, estamos en PC, y no hay demasiados objetos. Además, es por diversión :) –