2011-03-28 28 views
15

Tengo una pregunta sobre un juego XNA que estoy haciendo, pero también es una pregunta genérica para futuros juegos. Estoy haciendo un juego de Pong y no sé exactamente qué actualizar, así que explicaré mejor a qué me refiero. Tengo un juego de clase, paleta con pelota y, por ejemplo, quiero verificar las colisiones entre la pelota con los límites de la pantalla o las paletas, pero me encuentro con 2 enfoques para hacer esto:Game Architecture

mayores se acerquen a nivel - Hacer que las propiedades de la paleta y la bola sean públicas y en el control de Game.Update para detectar colisiones.

Bajo Nivel Enfoque - Puedo proporcionar todos los datos que necesito (los límites de la pantalla y las paletas INFO) para la clase de bola (con el parámetro, o en una clase estática pública común) y en el Ball.Update puedo comprobar si hay colisiones ?

Creo que mi pregunta de un modo más genérico es:

hace un objeto que necesita saber cómo actualizar y dibujarse a sí mismo, incluso los que tienen dependencias de los niveles más altos que de alguna manera son suministrados a ellos?

o

es mejor para procesarlo en niveles más altos en Game.Update o Game.Draw o el uso de Gestores para simplificar el código?

Creo que esta es una pregunta modelo de lógica de juego que se aplica a todos los juegos. No sé si hice mi pregunta clara, si no, siéntase libre de preguntar.

+1

Creo que cuando dices "lógica" en realidad quieres decir "arquitectura". –

+2

(Sigo haciendo referencia a esta pregunta, así que voy a arreglar el título yo mismo ...) –

+2

Esto es más adecuado para http://gamedev.stackexchange.com/ – AlexFoxGill

Respuesta

45

Lo difícil de responder a su pregunta es que usted está preguntando a ambos: "¿qué debo hacer ahora, para Pong" y "qué debo hacer más adelante, en un juego genérico"?


Para hacer Pong ni siquiera necesitas las clases de Bola y Paleta, porque básicamente son solo posiciones. Sólo se adhieren algo como esto en su clase de juego:

Vector2 ballPosition, ballVelocity; 
float leftPaddlePosition, rightPaddlePosition; 

A continuación, sólo actualizar y dibuja en el orden que más le convenga en Update y Draw funciones de su juego. ¡Fácil!


Pero, supongamos que desea crear varias bolas, y las bolas tienen muchas propiedades (posición, velocidad, rotación, color, etc.): Es posible que desee hacer una clase Ball o estructura que pueda instancia (misma va por las paletas).Incluso podría mover algunas funciones a esa clase donde son independientes (una función Draw es un buen ejemplo).

Pero mantenga el concepto de diseño igual: todo el manejo de la interacción objeto a objeto (es decir, la jugabilidad) ocurre en su clase Game.

Todo está bien si tienes dos o tres elementos de juego (o clases) diferentes.


Sin embargo, vamos a postular un juego más complicado. Tomemos el juego básico de pong, agreguemos algunos elementos de pinball como mutli-ball y aletas controladas por jugadores. Agreguemos algunos elementos de Snake, digamos que tenemos una "serpiente" controlada por AI, así como algunos objetos de recolección que pueden golpear las bolas o la serpiente. Y por si fuera poco, digamos que las paletas también pueden disparar láser como en Space Invaders y los rayos láser hacen cosas diferentes dependiendo de lo que golpeen.

Golly que es un gran lío de interacción! ¿Cómo vamos a lidiar con eso? ¡No podemos ponerlo todo en el juego!

¡Simple! Hacemos una interfaz (o una clase abstracta o una clase virtual) de la que se derivará cada "cosa" (o "actor") en nuestro mundo del juego. He aquí un ejemplo:..

interface IActor 
{ 
    void LoadContent(ContentManager content); 
    void UnloadContent(); 

    void Think(float seconds); 
    void UpdatePhysics(float seconds); 

    void Draw(SpriteBatch spriteBatch); 

    void Touched(IActor by); 

    Vector2 Position { get; } 
    Rectangle BoundingBox { get; } 
} 

(Esto es sólo un ejemplo no hay "una verdadera interfaz de agente" que funcione para todos los juegos, tendrá que diseñar su propio Esto es por lo que Don 'me gusta DrawableGameComponent.)

Tener una interfaz común permite que Game solo hable de actores, en lugar de tener que conocer todos los tipos de tu juego. Simplemente se deja de hacer las cosas comunes a todos los tipos - detección de colisiones, dibujo, actualización, carga, descarga, etc.

Una vez que estés en el actor, puede empezar a preocuparse por tipos específicos de actor. Por ejemplo, esto podría ser un método en el Paddle:

void Touched(IActor by) 
{ 
    if(by is Ball) 
     ((Ball)by).BounceOff(this.BoundingBox); 
    if(by is Snake) 
     ((Snake)by).Kill(); 
} 

Ahora, me gusta hacer que la pelota rebotó por el Paddle, pero en realidad es una cuestión de gusto. Podrías hacerlo al revés.

Al final deberías ser capaz de incluir a todos tus actores en una gran lista que simplemente puedes repetir en Game.

En la práctica, puede terminar teniendo múltiples listas de actores de diferentes tipos por motivos de rendimiento o simplicidad de código. Esto está bien, pero en general, intenta mantenerte en el principio de Juego solo sabiendo acerca de los actores genéricos.

Los actores también pueden querer consultar qué otros actores existen por varias razones. Así que dale a cada actor una referencia al juego y haz que la lista de actores sea pública en el juego (no es necesario ser súper estricto sobre público/privado cuando estás escribiendo código de juego y es tu propio código interno)


Ahora, incluso podría ir un paso más allá y tener múltiples interfaces. Por ejemplo: uno para renderizado, uno para scripting y AI, uno para física, etc. Luego, tiene múltiples implementaciones que se pueden componer en objetos.

Esto se describe en detalle en this article. Y tengo un ejemplo simple en this answer. Este es un próximo paso apropiado si comienzas a encontrar que tu interfaz de actor único está empezando a convertirse en más un "árbol" de clases abstractas.

+0

Respuesta impresionante - "Sin embargo, postulamos un juego más complicado " esto suena un juego genial ... están desarrollando algo como esto :) – harag

1

Puede ver su bola y paleta como un componente de su juego, y XNA le da la clase base GameComponent que tiene un método Update(GameTime gameTime) que puede anular para hacer la lógica. Además, también está la clase DrawableGameComponent, que viene con su propio método Draw para anular. Cada clase GameComponent también tiene una propiedad Game que contiene el objeto del juego que las creó. Allí puede agregar algunos Servicios que su componente puede usar para obtener información por sí mismo.

El enfoque que desee realizar, ya sea tener un objeto "maestro" que maneje cada interacción, o proporcionar la información a los componentes y hacer que reaccionen ellos, es decisión exclusiva de usted. El último método es preferido en un proyecto más grande. Además, esa sería la forma orientada a objetos para manejar las cosas, para darle a cada entidad sus propios métodos de actualización y dibujo.

0

Estoy de acuerdo con lo que dijo Andrew. Estoy aprendiendo XNA también y en mis clases, por ejemplo, tu clase de pelota. Tendría un método de actualización (tiempo de juego) y un método Draw() al menos. Suele ser Initialize(), Load() también. Luego, de la clase principal del juego, llamaré a esos métodos de sus respectivos primos. Esto fue antes de saber sobre GameComponent. Aquí hay un buen artículo sobre si deberías usar eso. http://www.nuclex.org/blog/gamedev/100-to-gamecomponent-or-not-to-gamecomponent

2

También podría optar por comenzar a pensar en cómo los diferentes componentes del juego necesitan hablar entre ellos.

Bola y paleta, ambos son objetos en el juego y en este caso, objetos portátiles, movibles. La paleta tiene los siguientes criterios

Sólo puede moverse arriba y abajo

  1. La paleta está fijado a un lado de la pantalla, o a la parte inferior
  2. la paleta podría ser controlado por el usuario (1 frente a la computadora o 1 vs 1)
  3. la paleta se puede representar
  4. la paleta sólo se puede mover a la parte inferior o la parte superior de la pantalla, no puede pasar de los límites

la pelota tiene los siguientes criterios

No puede salir de los límites de la pantalla

  1. Se puede volverse
  2. Dependiendo de donde es golpeado en la paleta, se puede controlar de manera indirecta (Algunas físicas simples)
  3. Si va detrás de la paleta, la ronda está terminada
  4. Cuando se inicia el juego, la pelota se une generalmente a la paleta de la persona que perdió.

La identificación de los criterios comunes que se pueden extraer de una interfaz

public interface IRenderableGameObject 
{ 
    Vector3 Position { get; set; } 
    Color Color { get; set; } 
    float Speed { get; set; } 
    float Angle { get; set; } 
} 

También tiene algunas GamePhysics

public interface IPhysics 
{ 
    bool HasHitBoundaries(Window window, Ball ball); 
    bool HasHit(Paddle paddle, Ball ball); 
    float CalculateNewAngle(Paddle paddleThatWasHit, Ball ball); 
} 

Entonces hay cierta lógica juego

public interface IGameLogic 
{ 
    bool HasLostRound(...); 
    bool HasLostGame(...); 
} 

Esto no es toda la lógica, pero debería darte una idea de qué buscar, porque está creando un conjunto de bibliotecas y funciones que puede usar para determinar qué va a suceder y qué puede suceder y cómo debe actuar cuando suceden esas cosas.

Además, al ver esto puede refinar y refactorizar esto para que sea un mejor diseño.

Conozca su dominio y anote sus ideas. Fallar en planear está planeando fallar