Cómo modelar la representación y el comportamiento del objeto de juego de manera modular?

Estoy haciendo un Java shoot em up juego para teléfonos Android. Tengo 20 enemigos raros en el juego que cada uno tiene algunos comportamientos únicos, pero ciertos comportamientos son reutilizados por la mayoría de ellos. Necesito modelar balas, explosiones, asteroides, etc. y otras cosas que actúan un poco como enemigos también. Mi diseño actual favorece la composición sobre la herencia y representa objetos de juego un poco como esto:

// 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 dibujar lo que se almacena como la imagen de la entidad, puede adjuntar un renderizador simple, por ejemplo

 class SimpleRenderer extends Renderer { void draw(Canvas c) { // just draw the image } } 

Para hacer que la entidad vuele de forma aleatoria cada fotograma, simplemente adjunte un comportamiento como este:

 class RandomlyMoveBehaviour extends Behaviour { void update(Entity e) { // Add random direction vector to e.position } } 

O agregue un comportamiento más complejo como esperar hasta que el jugador esté cerca antes de regresar a casa:

 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 que puede, por ejemplo, modularizar la última clase para que pueda proporcionar el comportamiento de "sueño" y el comportamiento "despierto" para que pueda decir algo como nuevo WaitUntilCloseBehaviour (jugador, 50 / pixels /, nuevo MoveRandomlyBehaviour (), nuevo HomingBehaviour ()). Esto hace que sea realmente fácil de hacer nuevos enemigos.

La única parte que me molesta es cómo se comunican los comportamientos y los procesadores. En este momento, Entity contiene un objeto de imagen que un comportamiento podría modificar si decide hacerlo. Por ejemplo, un comportamiento podría cambiar el objeto entre una imagen de sueño y despierto y el renderizador sólo dibujaría la imagen. No estoy seguro de cómo esto va a escalar, por ejemplo:

  • ¿Qué hay de un enemigo parecido a una torreta que se enfrenta a una dirección determinada? Supongo que podría agregar un campo de rotación a la Entidad que Comportamiento y Renderer pueden modificar / leer.

    • ¿Qué pasa con un tanque donde el cuerpo del tanque y la pistola del tanque tienen direcciones separadas? Ahora el renderizador necesita acceso a dos rotaciones desde algún lugar y las dos imágenes a usar. En realidad, no querrás hinchar la clase de Entidad con esto si solo hay un tanque.

    • ¿Qué pasa con un enemigo que brilla como su arma se recarga? Realmente desea almacenar el tiempo de recarga en el objeto Behavior, pero la clase Renderer no puede verlo.

Tengo problemas para pensar en formas de modelar lo anterior para que los renderizadores y los comportamientos puedan mantenerse algo separados. El mejor enfoque que puedo pensar es que los objetos de comportamiento contengan el estado extra y el objeto de representación, entonces los objetos de comportamiento llaman al método de dibujo de los renderizadores y pasan el estado extra (por ejemplo, la rotación) si así lo desean.

Entonces podría tener un objeto de comportamiento similar a un tanque que quiera un Renderer similar a un tanque en el que el último pida las dos imágenes y dos rotaciones para dibujar. Si usted quisiera que su tanque apenas fuera una imagen llana, usted apenas escribiría una subclase Renderer que ignoró las rotaciones.

¿Puede alguien 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 5×5, cuando tengo 50 enemigos volando a 60fps, implica muchas capas de llamadas de función.

El diseño de la composición es válido, ya que permite mezclar y combinar el (los) comportamiento (s) y render (es).

En el juego con el que estamos jugando, hemos añadido una "base de datos" que contiene información básica (en su caso la posición y el estado de muertos / vivos) y datos de variables que se establecen o no por el subsistema de comportamiento y colisión. El procesador puede utilizar estos datos (o no, si no es necesario). Esto funciona bien y permite un efecto nítido, como establecer un "objetivo" para un efecto gráfico dado.

Algunos problemas:

  • Si el Renderer solicita datos que el comportamiento no estableció. En nuestro caso, el evento se registra y se usan valores por defecto (definidos en renderizador).
  • Es un poco más difícil comprobar las informaciones necesarias de antemano (es decir, ¿qué datos deben estar en la base de datos para el Renderer A? ¿Qué datos se establecen mediante el comportamiento B?). Tratamos de mantener el documento actualizado, pero estamos pensando en grabar el set / get por las clases, y generar una página doc …

Actualmente estamos usando un HashMap para la base de datos, pero esto está en una PC, no en un IPhone. No sé si perfomance será suficiente, en cuyo caso otra estructura podría ser mejor.

También en nuestro caso, hemos decidido por un conjunto de renderizado especializado. Por ejemplo, si la entidad posee un dato de blindaje no vacío, el ShieldRenderer mostrará la representación … En su caso, el tanque podría poseer dos renderizadores enlazados a dos datas (definidos por la inicialización):

 Renderer renderer1 = new RotatedImage("Tank.png", "TankRotation"); Renderer enderer2 = new RotatedImage("Turret.png", "TurretRotation"); 

Con "TankRotation" y "TurretRotation" establecido por el comportamiento. Y el procesador simplemente rotando la imagen antes de mostrarla en la posición.

  image.rotate (entity.databag.getData(variable)); 

Espero que esto ayude

Saludos
Guillaume

El diseño con el que estás me ve bien. Este capítulo sobre los componentes puede ayudarle.

  • Cómo consultar el SpeechRecognizer predeterminado
  • Patrón ViewHolder correctamente implementado en CursorAdapter personalizado?
  • Cómo hacer un fragmento desplazable basado en el predeterminado android PageView
  • Error en la conexión http java.lang.IllegalArgumentException: El carácter no permitido en la consulta en el índice 76
  • ¿Hay un equivalente TweenMax en Java
  • ¿Por qué Android LocationManager tiene un gran retraso antes de que las actualizaciones de ubicación se inicien si la precisión del ajuste
  • ¿Cómo permitir que los usuarios busquen la última versión de la aplicación desde la aplicación?
  • Robotium: ¿Cómo espero que termine la carga antes de continuar?
  • ¿Cómo puedo convertir una parte del archivo de código fuente de Java a Kotlin?
  • Cómo activar la ofuscación de ProGuard en Android Studio?
  • Recupere el botón por findViewWithTag ¿no trabajando?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.