¿La mejor solución para "cadena de nivel"?


10

Tengo un juego que genera un mapa de nivel aleatorio al comienzo del nivel. Quiero implementar alguna forma de guardar y cargar el nivel.

Estaba pensando que tal vez XML sería una buena opción para guardar todas las variables, entonces sería fácil para mí construir algo que pueda analizar ese XML y generar exactamente el mismo nivel.

Pero XML es probablemente excesivo para mis necesidades. Recuerdo que en el pasado con la vieja consola Sega que no tenía la capacidad de guardar tu juego (creo que el juego de Worms también lo hizo), te daban un montón de personajes que podrías escribir. Si pinchaste esa cadena más tarde, cargaría el nivel exacto.

¿Sería una "cadena de nivel" una buena opción? ¿Sería algún tipo de conversión "base60"? ¿Cómo implementaría esto?

Respuestas:


20

Presumiblemente, todo lo que necesita guardar es la semilla aleatoria, que generalmente es solo una int. Podrías codificar el int a base64 si quisieras hacerlo un poco más opaco, pero eso probablemente no sea necesario.


También podría generar la semilla a partir de una palabra real (tal vez comprar sumando los caracteres para obtener su semilla) y de esa manera devolver algo más significativo. Sin embargo, tendrías que almacenar las palabras en tu juego o recuperarlas.
Jonathan Fischoff el

Está bien, pero también tendría que almacenar datos dinámicos si el juego se está jugando. La cadena necesitaría guardar las posiciones / puntuación de los personajes, etc. ¿Cuál es la mejor manera de generar esta cadena?
Adam Harte

Probablemente usaría JSON (o XML si lo prefiere) para serializar el estado mundial y luego codificar en base64 esa cadena. Sin embargo, no estoy seguro de que eso sea tan útil, ¿por qué no simplemente hacer un sistema de guardar / cargar más orientado a la GUI? Supongo que esto está basado en la web y no desea manejar este lado del servidor. Tal vez mire shygypsy.com/farm/p.cgi para ver un ejemplo.
coderanger

23

Independientemente del formato que utilice para guardar juegos, por amor de Dios, ingrese un número de versión. Podrá tener cargas compatibles con versiones anteriores ramificando en el número de versión, o podrá reconocer de forma segura los archivos guardados que son demasiado antiguos. cargar.

Te arrepentirás si no lo haces.


77
+1 realmente no responde la pregunta, pero es muy importante.
Jonathan Fischoff

10

JSON es bueno, pero YAML es mejor. :) http://www.yaml.org/ y http://code.google.com/p/yaml-cpp/ para una de las implementaciones más agradables de usar.

YAML es un superconjunto de JSON que agrega soporte para algunas características agradables, en particular:

  • Nodos binarios Esto es ideal para serializar el tipo de datos con los que podría estar tratando para descripciones de nivel. JSON requiere que traduzca a algún formato intermedio de su elección, como Base64, antes de escribir / después del análisis. YAML tiene un tipo de nodo binario que le dice a la biblioteca del analizador que lo haga por usted.
  • Referencias dentro del documento. Si el mismo objeto aparece dos veces en el documento, JSON lo escribirá dos veces, y cuando lo vuelva a leer, obtendrá dos copias. Muchos emisores YAML pueden detectar esta situación y, en lugar de una segunda copia, emiten una referencia a la primera, cuando se pueden detectar al cargar.
  • Tipos de nodos personalizados. Puede marcar cada mapa en una lista con, por ejemplo, !! Jugador, !! Enemigo, etc., y así mantener su información de tipo más fuera de banda.
  • YAML admite formatos más legibles.
  • Dado que JSON es un subconjunto de YAML, la mayoría de los lectores de YAML no tendrán problemas para leer documentos JSON.

1
Yaml es muy bueno, y si evita algunas de las características del superconjunto, se puede convertir a json sin dificultad.
Jethro Larson

3

Si desea serializar todos los datos en el juego, recomendaría JSON como su formato de archivo, es por eso que es más fácil usar el XML y el soporte es muy bueno en muchos idiomas.

He usado esta biblioteca para C ++ y funciona muy bien.

http://jsoncpp.sourceforge.net/


3

XML es una buena opción si no está limitado por el tamaño y es compatible de forma nativa (por ejemplo, en .NET y Flash), pero si desea un formato delgado, puede crear su propio formato y analizador de manera bastante sencilla. Normalmente uso 1 carácter, por ejemplo. coma para separar cada objeto. Para decodificar la cadena, haga una división en coma. Ahora cada objeto necesita propiedades diferentes, así que sepárelos con un carácter diferente, por ejemplo, punto y coma, y ​​use otro carácter para separar los nombres de propiedad de los valores de propiedad, por ejemplo. Colon. Todo esto se puede decodificar fácilmente sin expresiones regulares simplemente usando string.split. Aquí hay un ejemplo:

id:1;x:5;y:45.2;angle:45,id:28;x:56;y:89;angle:12;health:78

puede ahorrar aún más espacio manteniendo los nombres de propiedad en 1 carácter, por ejemplo, h para la salud. P.ej.

i:1;x:5;y:45.2;a:45,i:28;x:56;y:89;a:12;h:78

Compare con la alternativa JSON:

{"o":[{"i":1, "x":5, "y":45.2, "a":45}, {"i":28, "x":56, "y":89, "a":12, "h":78}]}

Además, si desea reducir el tamaño de sus números, puede codificarlos utilizando el conjunto completo de caracteres UTF16 imprimibles. Este hilo me inspiró a hacer una pregunta en Stack Overflow sobre la cantidad de datos que podría empaquetar en un personaje en pantalla . La respuesta parece estar en algún lugar de más de 40,000 valores para un número entero, si no te importa tener brail, kanji y piezas de ajedrez: ♔♕♖♗♘♙♚♛♜♝♞♟

Para obtener una mayor reducción de tamaño, puede usar el orden de lectura / escritura para determinar qué valor es cuál, por lo que los dos primeros caracteres representan la identificación, los dos siguientes son la posición x, los dos siguientes la y, luego el ángulo, luego la salud , etc. Entonces:

F5DGP@%&002DFTK#OP1F

podría almacenar toda la misma información que los otros ejemplos.

Las cuadrículas de mosaico se pueden almacenar como una cadena con cada carácter que representa un tipo diferente de mosaico, por ejemplo:

i789pog5h3kl

donde podría decir lava, 9 hierba mala, etc.


Esto está más en la línea de lo que estoy preguntando. ¡Menciono XML en mi pregunta, pero aún así la gente lo sugiere!
Adam Harte

1
Agregue {} alrededor de eso y básicamente tiene JSON ;-)
coderanger

Necesitaría agregar una carga de comillas también. Probablemente duplicaría el número de caracteres, pero obtendría el anidamiento de objetos.
Iain

1

Si está codificando en .Net, entonces XML es muy fácil de usar, ya que puede serializar / deserializar su clase de nivel dentro / fuera de XML con solo un par de líneas, y luego todo está en una clase bien administrada.

TheMap sería una variable de tipo Map en la que tiene todos sus datos cargados.

Dim TheMap As New Map

Suponiendo que ya tiene una clase Map construida, esto guardaría su mapa en XML:

Dim Serializer As New System.Xml.Serialization.XmlSerializer(GetType(TheMap))
Dim Strm As New FileStream("c:\Map.xml", FileMode.Create, FileAccess.Write, FileShare.None)
Serializer.Serialize(Strm, TheMap)
Strm.Close()

Esto cargaría ese XML nuevamente en su clase de mapa, para ser usado nuevamente en el código.

Dim Reader As New StreamReader("map.xml")
Dim Serializer As New System.Xml.Serialization.XmlSerializer(GetType(TheMap))

TheMap = Serializer.Deserialize(Reader)
Reader.Close()

A partir de este momento, su archivo XML ahora se carga en su clase para facilitar su uso.

En cuanto a su problema de "Cadena de nivel", lo que se dijo anteriormente funcionaría muy bien, podría usar el número de semilla como la "cadena de nivel".

De lo contrario, podría generar previamente la cantidad de mapas diferentes que desee, y guardarlos todos con una "Cadena de nivel" y luego usarlos para abrir el mapa adecuado.


+1 aunque odio XML: si el lenguaje proporciona un formato de serialización predeterminado, es mejor considerarlo primero, especialmente si es un formato que otras herramientas podrían analizar teóricamente (como XML / JSON / YAML).

0

Usaría un structdispositivo simple o similar (dependiendo de su idioma) para almacenar todo el estado del juego en un lugar central. Si desea la protección de setters / getters, puede ajustar la estructura en a class.

Si lo desea, utilice los campos de bits o simplemente manipule los bits usted mismo utilizando operadores bit a bit.

Tenga en cuenta que en algunos idiomas, las reglas para el relleno y el embalaje de la estructura pueden ser un poco complicadas, pero también puede no ser muy importante para su caso si tiene uno o dos bytes de relleno.

También puede usar un #pragma(como #pragma pack(1)) o un __attribute__para empacar la estructura, eliminando el relleno. Esto puede o no funcionar dependiendo de su compilador y arquitectura de destino.

Tenga en cuenta que el uso de campos de bits y pragmas o atributos de paquete puede reducir la portabilidad. En arquitecturas de hardware, la capacidad de campo de estructura (orden de bytes) también puede cambiar. Por lo tanto, es posible que desee evitar esto si está intentando la portabilidad.

(Por ejemplo, Pac-Man, esta estructura podría contener ingenuamente una identificación de mapa o una semilla de mapa, una posición x-y de Pac-Man, cuatro posiciones x e y fantasmas, y un gran campo de bits para la presencia o ausencia de 32-64 gránulos, cualquiera que sea el máximo).

Una vez que tenga su estructura, páselo a algo así como una función xxencode :

encode_save( char * outStringBuf, size_t outStringBufSize,
             const SaveStruct * inSaveData, size_t inSaveDataSize )

Escribir esta función es un poco propenso a errores; necesita cambiar y combinar bytes según sea necesario para obtener, por ejemplo, 6 bits a la vez, luego traducir a un carácter apropiado. Personalmente, trataría de buscar el código de otra persona, a menos que lo hiciera por "diversión" (y probablemente querría un conjunto de pruebas para ello).

Nunca subestimes el poder de la vieja escuela structen los lugares correctos. Lo hemos usado mucho para juegos de GBA y DS aquí.


La serialización de estructura bruta es un código nuevo no portátil y extremadamente frágil. A menos que sea el paso final de hornear datos para una plataforma con recursos muy limitados, es una optimización muy prematura. ¿Hay alguna razón por la que prefiera 6 bits a 8? De todos modos, el formato no será legible para el ser humano, también podría aprovechar la velocidad y la depuración de un diseño de estructura real.

@ Joe: Mencioné que no es portátil y algunas de las posibles preocupaciones. La pregunta pedía específicamente una "cadena de nivel" legible para humanos como los viejos juegos de Sega y mencionó la conversión de base60; pasar una estructura a través de algo como xxencode hará esto. Esto no es necesariamente una optimización prematura: una estructura simple, no empaquetada en bits, es una forma muy "central" de almacenar datos guardados, y puede simplificar gran parte del código que interactúa con los datos. Véanse, por ejemplo, los artículos recientes de Noel Llopis sobre estructuras no amigas no miembros y programación "de adentro hacia afuera". Esto es solo BESO.
leander

1
Estoy totalmente de acuerdo con esto. Sí, no es portátil en todas las versiones, ni en los sistemas, pero, de nuevo, los juegos guardados no son recursos que deben reutilizarse durante el desarrollo o copiarse en todas las plataformas. La depuración no es un problema con una lectura / escritura en el lugar, impleméntela una vez y funcionará para siempre. SI la extensibilidad es realmente un problema: agregue un número de versión como el primer ybyte / palabra y puede activarlo (aunque eso introduciría una vulnerabilidad de datos). No es que xml vaya a resolver problemas de versiones, tienden a estar más involucrados que solo establecer un valor. Sí, soy pragmático.
Kaj

0

XML es bueno para documentos estructurados arbitrariamente (los elementos pueden aparecer en diferentes niveles del árbol) o la incrustación de formato externo (como poner svg en una página xhtml). Si no tiene esos requisitos, es un formato realmente ineficiente, y es preferible algo más simple como csv o json.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.