Preguntas sobre la arquitectura de juegos con XNA

12

Así que finalmente pude jugar con XNA y he estado jugando para hacer un juego en 2D (tengo un montón de recursos artísticos de un amigo que lo desarrolló en iOS)

Parece que muchas cosas son fáciles de hacer y salen de la caja, sin embargo, me quedo perplejo por un montón de cosas, ya que la mayoría de la literatura (los libros que compré, por ejemplo) no prestan demasiada atención al 2D.

Realmente agradecería cualquier aclaración o que me indiquen más información sobre las siguientes búsquedas:

  1. ¿Cuál es el punto de un servicio de juego? Entiendo todo el lenguaje de registrar un objeto como un servicio para que cualquier otro GameComponent pueda agarrarlo, sin embargo, ¿cómo funciona esto simplemente haciéndolo público o estático? Por ejemplo, mi libro recomendó registrar el objeto SpriteBatch en la clase Game.cs. No estoy seguro de cómo es preferible hacerlo simplemente público / estático, ya que de todos modos solo debería haber una instancia de Juego (singleton).

  2. Estoy confundido sobre cuándo debería heredar de GameComponent o RenderableGameComponent. Estoy tratando de seguir un diseño de Administrador / Controlador, de modo que todas las entidades sean creadas / poseídas por un solo administrador y lo mismo para otras cosas. Actualmente tengo cada Administrador / Controlador heredado de GameComponent, sin embargo, ¿cómo supera esto que el objeto Juego posea a todos los Administradores y llame manualmente a la actualización sobre ellos y dibuje?

  3. Noté que Initialize se llama antes de ContentLoad (), encontré esto molesto ya que en mi Initialize es donde me gustaría crear algunas de mis entidades (es decir, Sprite, Player, etc.), sin embargo, no puedo darles su carga SpriteSheets o Textures ya que la llamada para cargarlos aún no se ha producido. ¿Tal vez estoy inicializando incorrectamente o la gente simplemente asigna la textura más abajo en ContentLoad?

Esos parecen ser mi mayor " WTF " de no entender realmente

Setheron
fuente

Respuestas:

11

La respuesta a su pregunta es sencilla: haga lo que funcione. Lo ideal es hacer lo que es simple y funciona. A menudo, usar la arquitectura incorporada no es simple porque, como está descubriendo, tiene que saltar a través de aros para estructurar su juego de la manera que desee.

Para responder la pregunta uno, lo remitiré a mi respuesta a la pregunta "¿Por qué usar los servicios?" . La respuesta breve es que los servicios son una buena forma de evitar problemas de acoplamiento (versiones, dependencia, extensibilidad) que nunca encontrarás en un juego autónomo . La mayoría de las veces es más simple simplemente hacer un miembro public(y tal vez incluso static, si tiene prisa) y preocuparse por problemas más interesantes.

Para responder a su segunda pregunta, lo remitiré a mi respuesta a la pregunta "¿Cuáles son las desventajas de usar DrawableGameComponent para cada instancia de un objeto de juego?" así como mis pensamientos sobre la arquitectura del juego . La respuesta corta es: no hay ningún beneficio a la utilización de los GameComponentmás simple creación de sus propias clases y llamar a métodos como Drawy Updateusted mismo. Si GameComponentcoincide exactamente con la arquitectura deseada, utilícela, pero casi siempre no.

El uso de servicios y componentes solo se vuelve realmente interesante si está creando una biblioteca para el consumo de terceros. XNA hace esto, lo tiene IGraphicsDeviceServicey GamerServicesComponent, por ejemplo. Estos cumplen requisitos específicos de desacoplamiento que normalmente no se encuentran al desarrollar un juego.

Finalmente, la respuesta a su tercera pregunta es bastante simple: simplemente cree sus entidades de juego LoadContent. No hay nada particularmente especial al respecto Initialize.

Vale la pena señalar que todo el Microsoft.Xna.Framework.Gameconjunto es opcional . Proporciona un punto de partida extremadamente útil, pero de ninguna manera debe considerarse como "la única forma correcta" de hacer las cosas.

Andrew Russell
fuente
Muy interesante forma de verlo. Mucho más realista y realista que mi respuesta. Bien merecido +1, señor.
Jesse Emond
¡Tengo una pregunta más! ¿Qué pasa con el origen cuando dibujas para un Texture2D? ¿Es común colocar el origen en la mitad inferior de la textura? Estoy tratando de hacer animaciones, y descubro que tener el origen en (0,0) está causando que la textura cambie ligeramente si el ancho y la altura no son iguales
Setheron
@Setheron: Realmente deberías crear una nueva pregunta, ya que esto no está relacionado con la pregunta original. Revertiré la edición que hiciste a esta pregunta. El origen de SpriteBatch.Drawse especifica en coordenadas de textura-píxel en relación con la esquina superior izquierda de la textura (o rectángulo de origen, si usa uno).
Andrew Russell
1
@Andrew: Recientemente refactoré mi juego para que se basara completamente en servicios en lugar de public/ staticpropiedades. ¿Por qué? Testabilidad Es casi imposible cambiar las cosas con el enfoque de propiedad, mientras que es trivial si todo se inicializa con un IServiceProvidermétodo que se puede rellenar con cualquier cosa que la clase necesite mediante su método de prueba. También evita la necesidad de crear una instancia del Gameobjeto en su prueba, lo que se vuelve desordenado muy rápidamente.
Matthew Scharley
Como comentario aparte, todavía tengo muchas propiedades públicas en el Gameobjeto. Simplemente se accede a ellos a través de interfaces que se insertan Game.Servicesy luego se pasan a todo para obtener de nuevo lo que necesitan.
Matthew Scharley
3

Para su primera pregunta, debo admitir que realmente no sé la respuesta real. Supongo que sería por la misma razón por la cual singleton es generalmente un mal patrón de diseño. Te referiré a una cita de este enlace :

La primera en la que pensamos (y una de las razones detrás de la introducción de servicios) fue hacer que una referencia de GraphicsDevice estuviera disponible en todo el juego. Originalmente teníamos el Game propio del GraphicsDevice y lo expusimos a través de una propiedad como Game.GraphicsDevice. El problema con esto fue que esto limitó estrechamente el GraphicsDevice al Juego, no permitiendo que otra persona "posea" el dispositivo (como un procesador) y evitando que alguien use un GraphicsDevice actualizado en el futuro sin enviar una nueva versión del Juego que no No sea compatible con versiones anteriores.

Para su segunda pregunta, supongo que usar componentes como este sería más útil si siguiera un enfoque basado en componentes , como si tuviera una lista de Spritecomponentes que sabrían cómo dibujar y actualizarse en lugar de un administrador que sabe cómo actualizar y dibujar cada sprite. Estoy de acuerdo, suena igual pero prefiero un diseño basado en componentes porque realmente me gusta el enfoque orientado a objetos que tiene.

Para su tercera pregunta, sí, debe cargar cualquier forma de contenido (activos, fuentes, etc.) en el método LoadContent. En cuanto a la inicialización, normalmente crearía el objeto Player dentro de la función Initialize y luego cargaría su contenido dentro de LoadContent ... O simplemente puede hacerlo todo dentro de LoadContent si lo desea.

Jesse Emond
fuente