Estoy desarrollando un juego de plataformas en 2D con algunos amigos. Lo hemos basado en el XNA Platformer Starter Kit que utiliza archivos .txt para almacenar el mapa de mosaico. Si bien esto es simple, no nos da suficiente control y flexibilidad con el diseño de nivel. Algunos ejemplos: para múltiples capas de contenido se requieren múltiples archivos, cada objeto se fija en la cuadrícula, no permite la rotación de objetos, un número limitado de caracteres, etc. Por lo tanto, estoy investigando cómo almacenar los datos de nivel y archivo de mapa.
Esto se refiere solo al almacenamiento del sistema de archivos de los mapas en mosaico, no a la estructura de datos que utilizará el juego mientras se está ejecutando. El mapa de mosaico se carga en una matriz 2D, por lo que esta pregunta es acerca de qué fuente llenar la matriz.
Razonamiento para DB: Desde mi perspectiva, veo menos redundancia de datos usando una base de datos para almacenar los datos del mosaico. Las fichas en la misma posición x, y con las mismas características se pueden reutilizar de un nivel a otro. Parece que sería bastante simple escribir un método para recuperar todos los mosaicos que se utilizan en un nivel particular de la base de datos.
Razonamiento para JSON / XML: archivos editables visualmente, los cambios pueden rastrearse a través de SVN mucho más fácilmente. Pero hay contenido repetido.
¿Alguno tiene inconvenientes (tiempos de carga, tiempos de acceso, memoria, etc.) en comparación con el otro? ¿Y qué se usa comúnmente en la industria?
Actualmente el archivo se ve así:
....................
....................
....................
....................
....................
....................
....................
.........GGG........
.........###........
....................
....GGG.......GGG...
....###.......###...
....................
.1................X.
####################
1 - Punto de inicio del jugador, X - Salida de nivel,. - Espacio vacío, # - Plataforma, G - Gema
Respuestas:
Entonces, al leer su pregunta actualizada, parece que está más preocupado por los "datos redundantes" en el disco, con algunas preocupaciones secundarias sobre los tiempos de carga y lo que utiliza la industria.
En primer lugar, ¿por qué le preocupan los datos redundantes? Incluso para un formato hinchado como XML, sería bastante trivial implementar la compresión y reducir el tamaño de sus niveles. Es más probable que ocupes espacio con texturas o sonidos que tus datos de nivel.
En segundo lugar, es más probable que un formato binario se cargue más rápido que uno basado en texto que debe analizar. Doblemente, si puedes dejarlo en la memoria y tener tus estructuras de datos justo allí. Sin embargo, hay algunas desventajas en eso. Por un lado, los archivos binarios son casi imposibles de depurar (especialmente para las personas de creación de contenido). No puedes diferenciarlos o versionarlos. Pero serán más pequeños y se cargarán más rápido.
Lo que hacen algunos motores (y lo que considero una situación ideal) es implementar dos rutas de carga. Para el desarrollo, utiliza un formato basado en texto de algún tipo. Realmente no importa qué formato específico use, siempre que use una biblioteca que sea sólida. Para el lanzamiento, cambia a cargar una versión binaria (una carga más pequeña, más rápida, posiblemente despojada de entidades de depuración). Sus herramientas para editar los niveles escupen ambos. Es mucho más trabajo que un solo formato de archivo, pero puedes obtener lo mejor de ambos mundos.
Dicho todo esto, creo que estás saltando un poco el arma.
El primer paso para estos problemas es siempre describir el problema con el que se está ejecutando a fondo. Si sabe qué datos necesita, entonces sabe qué datos necesita almacenar. A partir de ahí es una pregunta de optimización comprobable.
Si tiene un caso de uso para probar, puede probar algunos métodos diferentes de almacenamiento / carga de datos para medir lo que considere importante (tiempo de carga, uso de memoria, tamaño de disco, etc.). Sin saber nada de eso, es difícil ser más específico.
fuente
Mi recomendación es usar un formato de archivo binario personalizado. Con mi juego, solo tengo un
SaveMap
método que pasa y guarda cada campo usando aBinaryWriter
. Esto también le permite elegir comprimirlo si lo desea y le da más control sobre el tamaño del archivo. es decir, guarde un enshort
lugar de unint
si sabe que no será mayor que 32767. En un bucle grande, guardar algo como un enshort
lugar de unint
puede significar un tamaño de archivo mucho más pequeño.Además, si sigue esta ruta, le recomiendo que su primera variable en el archivo sea un número de versión.
Considere, por ejemplo, una clase de mapa (muy simplificada):
Ahora, supongamos que desea agregar una nueva propiedad a su mapa, digamos a
List
dePlatform
objetos. Elegí esto porque es un poco más complicado.En primer lugar, incrementa
MapVersion
y agregaList
:Luego, actualice el método de guardar:
Luego, y aquí es donde realmente ve el beneficio, actualice el método de carga:
Como puede ver, el
LoadMaps
método no solo puede cargar la última versión del mapa, ¡sino también versiones anteriores! Cuando carga los mapas más antiguos, tiene control sobre los valores predeterminados que usa.fuente
Cuento
Una buena alternativa a este problema es almacenar sus niveles en un mapa de bits, un píxel por mosaico. El uso de RGBA permite fácilmente almacenar cuatro dimensiones diferentes (capas, identificadores, rotación, tinte, etc.) en una sola imagen.
Larga historia
Esta pregunta me recordó cuando Notch transmitió en vivo su entrada de Ludum Dare hace unos meses, y me gustaría compartir en caso de que no sepa lo que hizo. Pensé que era realmente interesante.
Básicamente, utilizó mapas de bits para almacenar sus niveles. Cada píxel en el mapa de bits correspondía a un "mosaico" en el mundo (no es realmente un mosaico en su caso, ya que era un juego emitido por rayos pero lo suficientemente cerca). Un ejemplo de uno de sus niveles:
Entonces, si usa RGBA (8 bits por canal), puede usar cada canal como una capa diferente y hasta 256 tipos de mosaicos para cada uno de ellos. ¿Sería eso suficiente? O, por ejemplo, uno de los canales podría mantener la rotación del mosaico como usted mencionó. Además, crear un editor de niveles para trabajar con este formato también debería ser bastante trivial.
Y lo mejor de todo, ni siquiera necesita ningún procesador de contenido personalizado, ya que puede cargarlo en XNA como cualquier otro Texture2D y leer los píxeles de nuevo en un bucle.
IIRC usó un punto rojo puro en el mapa para señalar a un enemigo, y dependiendo del valor del canal alfa para ese píxel determinaría el tipo de enemigo (por ejemplo, un valor alfa de 255 podría ser un murciélago mientras que 254 sería un zombie u otra cosa).
Otra idea sería subdividir los objetos del juego en aquellos que están fijos en una cuadrícula y aquellos que pueden moverse "finamente" entre las fichas. Mantenga los mosaicos fijos en el mapa de bits y los objetos dinámicos en una lista.
Incluso podría haber una forma de multiplexar aún más información en esos 4 canales. Si alguien tiene alguna idea sobre esto, avíseme. :)
fuente
Probablemente podría salirse con la suya con casi cualquier cosa. Desafortunadamente, las computadoras modernas son tan rápidas que, presumiblemente, esta cantidad relativamente pequeña de datos realmente no haría una diferencia de tiempo notable, incluso si se almacena en un formato de datos hinchado y mal construido que anhela una gran cantidad de procesamiento para ser leído.
Si quieres hacer lo "correcto", haces un formato binario como el que sugirió Drackir, pero si haces otra elección por alguna razón tonta, probablemente no volverá y te morderá (al menos no en cuanto al rendimiento).
fuente
Vea esta pregunta: ¿ 'XML binario' para los datos del juego? También optaría por JSON o YAML o incluso XML, pero eso tiene bastante sobrecarga. En cuanto a SQLite, nunca lo usaría para almacenar niveles. Una base de datos relacional es útil si espera almacenar una gran cantidad de datos relacionados (por ejemplo, tiene enlaces relacionales / claves externas) y si espera hacer una gran cantidad de consultas diferentes, el almacenamiento de mapas de mosaico no parece hacer este tipo de cosas y agregaría una complejidad innecesaria.
fuente
El problema fundamental con esta pregunta es que combina dos conceptos que no tienen nada que ver entre sí:
Puede almacenar sus archivos como texto sin formato en algún formato arbitrario, JSON, script Lua, XML, un formato binario arbitrario, etc. Y nada de eso requerirá que su representación en memoria de esos datos tome una forma particular.
El trabajo de su código de carga de nivel es convertir el paradigma de almacenamiento de archivos en su representación en memoria. Por ejemplo, usted dice: "Veo menos redundancia de datos usando una base de datos para almacenar los datos del mosaico". Si desea menos redundancia, eso es algo que debería tener en cuenta su código de carga de nivel. Eso es algo que su representación en memoria debería poder manejar.
Es no algo que sus necesidades de formato de archivo que se ocupa. Y he aquí por qué: esos archivos tienen que venir de alguna parte.
O va a escribir esto a mano, o va a usar algún tipo de herramienta que cree y edite datos de nivel. Si los escribe a mano, lo más importante que necesita es un formato que sea fácil de leer y modificar. La redundancia de datos no es algo que su formato incluso deba considerar, porque pasará la mayor parte de su tiempo editando los archivos. ¿Desea tener que usar manualmente cualquier mecanismo que se le ocurra para proporcionar redundancia de datos? ¿No sería un mejor uso de tu tiempo que tu cargador de nivel lo maneje?
Si tiene una herramienta que los crea, entonces el formato real solo importa (en el mejor de los casos) por razones de legibilidad. Puedes poner cualquier cosa en estos archivos. Si desea omitir datos redundantes en el archivo, simplemente diseñe un formato que pueda hacerlo y haga que su herramienta use esa capacidad correctamente. Su formato de mapa de mosaico puede incluir compresión RLE (codificación de longitud de ejecución), compresión de duplicación, cualquier técnica de compresión que desee, porque es su formato de mapa de mosaico.
Problema resuelto.
Las bases de datos relacionales (RDB) existen para resolver el problema de realizar búsquedas complejas en grandes conjuntos de datos que contienen muchos campos de datos. El único tipo de búsqueda que realiza en un mapa de mosaicos es "obtener mosaico en la posición X, Y". Cualquier matriz puede manejar eso. El uso de una base de datos relacional para almacenar y mantener datos de mosaico sería una exageración extrema , y realmente no vale nada.
Incluso si construye algo de compresión en su representación en memoria, aún podrá superar los RDB fácilmente en términos de rendimiento y huella de memoria. Sí, tendrá que implementar esa compresión. Pero si el tamaño de los datos en memoria es tan preocupante que consideraría un RDB, entonces probablemente quiera implementar tipos específicos de compresión. Será más rápido que un RDB y menor en memoria al mismo tiempo.
fuente
Para datos realmente simples, probablemente seguiría la ruta XML, si ya está familiarizado con la carga y el análisis XML.
fuente