Bien, formulado de esta manera, estoy seguro de que suena oscuro, pero haré todo lo posible para describir mi problema.
Primero, déjame explicarte lo que está mal con mi código fuente real.
Estoy haciendo un juego de plataformas, y aquí están los componentes de la corriente de mi juego: un nivel , que está llevando a cabo su propia rejilla de baldosas y también las hojas de baldosas y una teja , que sólo mantiene el nombre de su hoja de baldosas y el índice dentro de la hoja de azulejos Todos estos están dentro de un proyecto de juego XNA separado, llamado GameLib.
Así es como mis niveles están formateados en un archivo de texto:
x x . . . . .
. . . b g r .
. . . . . . .
X X X X X X X
Donde .
representa un mosaico vacío, b
representa una plataforma azul, X
representa un bloque con un color aleatorio, etc.
En lugar de hacer todo el trabajo de cargar un nivel dentro de la clase Level , decidí aprovechar la extensión de la canalización de contenido y crear una LevelContentPipelineExtension.
Tan pronto como comencé, me di cuenta rápidamente de que no quería codificar más de 20 líneas de este tipo:
if (symbol == 'b')
return new Tile(/*...*/);
Entonces, con la esperanza de al menos centralizar toda la creación de contenido en una clase, hice una clase estática, llamada LevelContentCreation , que contiene la información sobre qué símbolo crea qué y todo este tipo de cosas. También contiene una lista de todas las rutas de activos de las hojas de mosaico, para cargarlas más adelante. Esta clase está dentro de mi proyecto de biblioteca de juegos GameLib.
El problema es que estoy usando esta clase estática LevelContentCreation tanto en mi clase Level (para cargar las hojas de mosaico reales) como en mi extensión de canalización de contenido (para saber qué símbolo representa qué mosaico) ...
Y así es como se ve mi clase LevelContentCreation :
public static class LevelContentCreation
{
private static Dictionary<char, TileCreationInfo> AvailableTiles =
CreateAvailableTiles();
private static List<string> AvailableTileSheets { get; set; }
/* ... rest of the class */
}
Ahora, dado que la clase LevelContentCreation es parte de la biblioteca del juego, la extensión de la tubería de contenido intenta usarla, pero la llamada a CreateAvailableTiles () nunca ocurre y no puedo cargar un objeto Level de la tubería de contenido.
Sé que esto se debe a que la canalización de contenido se ejecuta antes de la compilación real o algo así, pero ¿cómo puedo solucionar el problema?
Realmente no puedo mover la clase LevelContentCreation a la extensión de canalización, porque entonces la clase Level no podría usarla ... Estoy bastante perdido y no sé qué hacer ... Sé estos son decisiones de diseño bastante malas, y me gustaría que alguien pudiera señalarme en la dirección correcta.
Gracias por tu precioso tiempo.
Si algo no está lo suficientemente claro o si debo proporcionar más información / código, deje un comentario a continuación.
Un poco más de información sobre mis proyectos:
- Juego - Proyecto de juego XNA para Windows. Contiene referencias a: Motor, GameLib y GameContent
- GameLib - Proyecto de biblioteca de juegos XNA. Contiene referencias a: Motor
- GameContent - Proyecto de contenido XNA. Contiene referencias a: LevelContentPipelineExtension
- Motor - Proyecto de biblioteca de juegos XNA. Sin referencias
- LevelContentPipelineExtension - Extensión de canalización de contenido XNA. Contiene referencias a: Motor y GameLib
No sé casi nada sobre XNA 4.0 y estoy un poco perdido sobre cómo funciona el contenido ahora. Si necesitas más información, dímelo.
Respuestas:
En los casos en los que usa singletons, clases estáticas o variables globales, el 99% del tiempo lo que realmente debería usar es un servicio de juegos . Los servicios de juego se usan cuando quieres que una instancia de una clase esté disponible para todas las demás clases, pero el acoplamiento estrecho de las otras opciones te da un sabor divertido. Los servicios tampoco tienen los problemas de creación de instancias que has visto, son más reutilizables y se pueden burlar (si te interesan las pruebas unitarias automatizadas) .
Para registrar un servicio, debe darle a su clase su propia interfaz. Entonces solo llama
Game.Services.AddService()
. Para usar más tarde ese servicio en otra clase, llame alGame.Services.GetService()
.En su caso, su clase se vería así:
En algún momento al comienzo del programa, tendría que registrar su servicio
Game.Services
. Prefiero hacer esto al comienzoLoadContent()
, pero algunas personas prefieren registrar cada servicio en el constructor de esa clase.Ahora, cuando quiera acceder a su
LevelContentCreation
clase en cualquier otra clase, simplemente haga esto:Como nota al margen, este paradigma se conoce como Inversión de Control (IoC), y también se usa ampliamente fuera del desarrollo de XNA. El marco más popular para IoC en .Net es Ninject , que tiene una sintaxis más agradable que los métodos Game.Services:
También es compatible con la inyección de dependencia , una forma de IoC que es un poco más compleja, pero mucho más agradable para trabajar.
[Editar] Por supuesto, también puedes obtener esa buena sintaxis con XNA:
fuente
En realidad, encontré una manera de solucionar el problema: eliminé la clase estática y la convertí en una clase normal. De esta manera, estoy seguro de que las declaraciones correctas se ejecutan en el momento correcto.
Desearía no haber perdido su tiempo así, pero en realidad tuve la idea de hacerlo leyendo los comentarios / respuestas.
Gracias de todos modos.
fuente
if
/switch
declaraciones que simplemente devuelven diferentes tipos, probablemente quieras que esos valores formen parte de la clase. En su caso, crearía unaTileType
clase que contiene información (textura, guardar-archivo-letra, etc.) sobre cada tipo de mosaico. Luego, para obtener unaTileType
carta, simplemente busque en su listaTileTypes
o, si le preocupa el rendimiento, almacene una estáticaDictionary<char, TileType>
en laTileType
clase (puede completarla automáticamente en elTileType
constructor).Tile
(clase que representa un único mosaico que se muestra en la pantalla) haría referencia a un soloTileType
. Si suficientes de sus mosaicos tienen un comportamiento único que requiere un código especial, deberá utilizar una jerarquía de clases en su lugar.Eche un vistazo a esto: http://msdn.microsoft.com/en-us/library/bb447745.aspx
Para el diseño de nivel, simplemente pondría una función dentro de mi clase de nivel llamada "LoadFromFile (nombre de cadena)". Porque si deseas agregar soporte para mapas personalizados en tu juego más tarde, tendrás que guardar y cargar los niveles desde tu propio formato de archivo de todos modos.
También puede crear un importador de contenido para cargar desde los activos incluidos y también desde los archivos proporcionados por el usuario. Consulte esta página para obtener más información sobre cómo crear un importador.
http://msdn.microsoft.com/en-us/library/bb447743.aspx
fuente