2010-06-08 9 views
16

¿Cuáles son las razones a favor y en contra de que un objeto del juego dibuje y actualice por sí mismo? Por ejemplo, si tiene un juego donde el jugador tiene una posición en la pantalla, por qué no tener una clase que lo abarca todo:¿Qué tiene de malo la arquitectura de un objeto de juego al dibujarse y actualizarse?

public class Player { 
    private int x, y, xVelocity, yVelocity; 
    private Sprite s; 
    //... 

    public Player() { 
     // load the sprite here, somehow? 
    } 

    public void draw(CustomGraphicsClass g) { 
     g.draw(s, x, y); 
    } 

    public void update(long timeElapsed) { 
     x += (xVelocity * timeElapsed); 
     y += (yVelocity * timeElapsed); 
    } 
} 

Lo que está mal con este diseño? ¿Cuáles son las caídas o las preocupaciones? ¿Cómo sería mejor escribir algo como esto, o mejor diseñar este tipo de cosas en un juego?

Además, algo conectado, ¿cómo implementarías la carga de esa imagen de Sprite?

Y, además, ¿cómo implementaría la colisión entre dos Player s?

(probablemente debería separar estos extra de dos preguntas en nuevas preguntas, ¿eh?)

Respuesta

18

Combina todos sus códigos lógicos y de representación cuando tienen poco en común más allá de estar conceptualmente vinculados a la misma "entidad". A medida que su clase crece, puede encontrarse con un enorme monolítico Player que es una pesadilla para mantener. Partir a lo largo de los límites del dominio (renderizado, AI) lo hace más manejable sin tener que renunciar a mucho ya que esos dominios no tienen mucha superposición.

Más allá de mantenimiento, hay algunas otras consideraciones:

  1. Si desea procesar la representación y la IA en diferentes hilos, mezclando su estado en la misma clase se acaba pidiendo problemas multi-threading desagradables.

  2. Si está utilizando un lenguaje como C++, las clases altamente acopladas como esta pueden matar sus tiempos de compilación.

  3. Dependiendo de cómo se coloquen sus códigos y objetos en la memoria, dividir los objetos en componentes separados para cada dominio puede proporcionarle una mejor coherencia de caché y un rendimiento mucho mejor.

Aquí está lots more info si tiene curiosidad.

+1

¡Me encanta la página a la que vinculó con "mucha más información"! Todavía estoy pensando en todo esto, pero es ** definitivamente ** útil no solo que se me diga qué hacer, sino que se me da un ejemplo decente y una discusión sobre las trampas y otras consideraciones. – Ricket

+0

¡Gracias! Es un libro en el que estoy trabajando, pero está más o menos en pausa en este momento. Avíseme si algo no está claro en el capítulo. – munificent

+1

+1 Solo para el punto 3, que es la razón principal, especialmente en las consolas. La encapsulación puede conducir a un pobre rendimiento del caché, especialmente cuando se almacenan indicadores como IsVisible por objeto, luego se carga toda la línea de caché para ese obj solo para comprobar si se debe dibujar, etc. – zebrabox

2

Está bien, pero si usted tiene muchos objetos diferentes, todas con sprites, posiciones y velociites que necesitan actualización, entonces no será mucha replicacion Puedes empujar la ubicación, la velocidad a una clase base, pero esto dificulta el tipo de movimiento. Mejor es separar el movimiento del objeto mismo.

Por ejemplo, considere una pelota que rebota hacia arriba y hacia abajo. No es fácil modelar solo con la velocidad. En su lugar, cree una clase Motion, con ConstantMotion (para velocidad) y BounceMotion para rebote. La clase de movimiento se encarga entonces de actualizar el estado de posición del objeto de cuadro a cuadro.

+0

Esto es interesante. ¿Hay un nombre para este patrón que describiste o un enlace donde podría obtener más información? –

+0

Es la separación de las preocupaciones. El patrón más cercano es probablemente el patrón de Estrategia. – mdma

3

Una posible desventaja podría ser una violación de la separación de preocupaciones. Pensaría que, idealmente, los objetos no deberían saber cómo se renderizan, de modo que el motor de representación pueda ser ajustado por separado de los objetos del juego ... aunque, en la práctica, puede ser que estas cosas a menudo sean demasiado interdependientes ser separado de esta manera.

+1

+1 Esto se llama AOP - Programación Orientada a Aspectos. –

3

Supongamos que cada vez que el objeto (es decir Player) se actualiza debe

1) redibujar el propio

2) notificar a otras unidades que se pone al día

3) "update-acción" a la historia/registro/lo que sea (tal vez querrás tener la posibilidad de reproducir todo el juego una vez que haya terminado, como una película).

....

n) cualquier otra interacción entre el jugador a objetos y su entorno.

En todos estos casos que tendrá que cambiar su método de actualización, como:

public void update(long timeElapsed) { 
    dosmth(); 

    redraw(); 
    notifyUnits(); 
    updateHistory(); 
} 

Es muy molesto. Es por eso que en tales casos se debe usar un patrón de observador. Su objeto debe notificar a todos los oyentes que fue actualizado. Los oyentes (GraphicsContext, History, units) reaccionarán de forma adecuada y su programa será fácil de mantener porque todas sus partes serán responsables solo de una tarea concreta.

+1

tenga en cuenta que redraw() no debe volver a pintar directamente, sino que es simplemente una indicación de que el objeto debe dibujarse. Un procesador externo se encarga de iterar a través de objetos que necesitan redibujar y dibujarlos en la secuencia correcta, aplicando el recorte apropiado, etc. – mdma

1

Es útil observar la posibilidad de que la mayoría de los objetos en un sistema solo modifiquen los descriptores específicos del motor (velocidad, animación activa) y permitan que el motor se encargue del efecto real. Hay algunas excepciones a esto (generalmente viñetas y otros efectos de un solo tilde), pero en general estos sistemas se basan en un único tic universal que actualiza el juego.La razón de esto es principalmente, como mdma señaló en su comentario sobre la respuesta de Roman, que el motor real hace mucho más que simples operaciones individuales de renderizado. Del mismo modo, un motor de física actualizará todo el mundo, calculando los volúmenes de colisión teniendo en cuenta el movimiento de sub-tick.

Cargar un sprite suele ser una cuestión de cargar los recursos de un mapa completo y luego abordar ese sprite por su nombre dentro del repositorio de recursos del mapa.

La colisión suele ser manejada por un motor de física independiente, ver arriba.

1

Después de haber caído para este patrón antes de que aquí es mi entrada:

Para un juego, el rendimiento será el mayor inconveniente. Es probable que desee mantener todos los contextos de dibujos, objetos, etc. en un solo lugar y optimizar un bloque de código más grande.

Si por alguna razón que quería hacer en diferentes plataformas, a continuación, este enfoque falla debido a falta de separación, de nuevo un procesador de plug-in es un mejor enfoque

Cuestiones relacionadas