Melhor solução para "level string"?

10

Eu tenho um jogo que gera um mapa de nível aleatório no início do nível. Eu quero implementar alguma maneira de salvar e carregar o nível.

Eu estava pensando que talvez o XML fosse uma boa opção para salvar todas as variáveis, então seria fácil criar algo que possa analisar esse XML e gerar exatamente o mesmo nível.

Mas XML provavelmente é um exagero para minhas necessidades. Lembro-me de antigamente com o antigo console da Sega que não tinha a capacidade de salvar seu jogo (acho que o jogo Worms também o fez), que eles dariam a você um monte de personagens que você poderia escrever. Se você digitar essa sequência mais tarde, ela carregará o nível exato.

Uma "string de nível" seria uma boa opção? Seria algum tipo de conversão "base60"? Como eu implementaria isso?

Adam Harte
fonte

Respostas:

20

Presumivelmente, tudo o que você precisa salvar é a semente aleatória, que geralmente é apenas um int. Você pode codificar o int para base64 se quiser torná-lo um pouco mais opaco, mas isso provavelmente não é necessário.

coderanger
fonte
Você também pode gerar a semente a partir de uma palavra real (talvez compre somando os caracteres para obter sua semente) e, dessa forma, retornar algo mais significativo. Você precisaria armazenar as palavras no seu jogo ou recuperá-las.
Jonathan Fischoff
Tudo bem, mas também teria que armazenar dados dinâmicos se o jogo estiver sendo jogado. A string precisaria salvar as posições / pontuação dos caracteres, etc. Qual é a melhor maneira de gerar essa string?
Adam Harte
Eu provavelmente usaria JSON (ou XML, se preferir) para serializar o estado mundial e, em seguida, codificar em base64 essa sequência. Não tenho certeza se isso é útil, por que não criar um sistema de salvar / carregar mais orientado a GUI? Suponho que isso seja baseado na Web e você não queira lidar com esse lado do servidor. Talvez veja shygypsy.com/farm/p.cgi como um exemplo?
Coderanger
23

Qualquer que seja o formato usado para seus jogos salvos, pelo amor de Deus, digite um número de versão. Você poderá ter cargas compatíveis com versões anteriores ao ramificar o número da versão ou poderá reconhecer com segurança os salvamentos antigos demais. carregar.

Você vai se arrepender se não o fizer.

tenpn
fonte
7
O +1 realmente não responde à pergunta, mas é tão importante.
Jonathan Fischoff
10

JSON é bom, mas YAML é melhor. :) http://www.yaml.org/ e http://code.google.com/p/yaml-cpp/ para uma das implementações mais agradáveis ​​de usar.

YAML é um superconjunto de JSON que adiciona suporte a alguns recursos interessantes, principalmente:

  • Nós binários. Isso é ótimo para serializar o tipo de dados com o qual você pode estar lidando para obter descrições de nível. O JSON exige que você traduza para algum formato intermediário de sua escolha, como Base64, antes de escrever / depois da análise. O YAML possui um tipo de nó binário !!, que instrui a biblioteca analisadora a fazer isso por você.
  • Referências intra-documento. Se o mesmo objeto aparecer duas vezes no documento, o JSON o gravará duas vezes e, quando você o voltar a ler, receberá duas cópias. Muitos emissores YAML podem detectar essa situação e, em vez de uma segunda cópia, enviar uma referência à primeira, quando pode ser detectada ao carregar.
  • Tipos de nós customizados. Você pode sinalizar cada mapa em uma lista com, por exemplo, !! Player, !! Inemy, etc., e assim manter suas informações de tipo mais fora de banda.
  • O YAML suporta uma formatação mais legível.
  • Como o JSON é um subconjunto do YAML, a maioria dos leitores do YAML não terá problemas para ler documentos JSON.

fonte
1
O Yaml é muito legal, e se você evitar alguns dos recursos do superconjunto, ele pode ser convertido para json sem dificuldade.
Jethro Larson
3

Se você deseja serializar todos os dados no jogo, eu recomendaria o JSON como seu formato de arquivo; é por isso que é mais fácil usar o XML e o suporte é muito bom em vários idiomas.

Eu usei essa biblioteca para C ++ e funciona muito bem.

http://jsoncpp.sourceforge.net/

Jonathan Fischoff
fonte
3

O XML é uma boa opção se você não estiver limitado pelo tamanho e for suportado nativamente (por exemplo, no .NET e Flash), mas se desejar um formato mais fino, poderá criar seu próprio formato e analisador com bastante facilidade. Eu normalmente uso 1 caractere, por exemplo. vírgula para separar cada objeto. Para decodificar a sequência, faça uma divisão por vírgula. Agora, cada objeto precisa de propriedades diferentes, portanto, separe-as com um caractere diferente, por exemplo, ponto-e-vírgula, e use outro caractere para separar os nomes das propriedades dos valores das propriedades, por exemplo. Cólon. Tudo isso pode ser decodificado facilmente sem regex apenas usando string.split. Aqui está um exemplo:

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

você pode economizar ainda mais espaço mantendo os nomes das propriedades com até 1 caractere, por exemplo, h para saúde. Por exemplo.

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

Compare com a alternativa JSON:

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

Além disso, se você quiser diminuir o tamanho dos seus números, poderá codificá-los usando o conjunto completo de caracteres UTF16 imprimíveis. Esse tópico me inspirou a perguntar uma pergunta no Stack Overflow sobre a quantidade de dados que você poderia compactar em um caractere na tela . A resposta parece estar acima de 40.000 valores para um número inteiro, se você não se importa de ter brail, Kanji e peças de xadrez: ♔♕♖♗♘♙♚♛♜♝♞♟

Para obter uma redução adicional de tamanho, você pode usar a ordem de leitura / gravação para determinar qual valor é qual; portanto, os dois primeiros caracteres representam o id, os próximos dois são a posição x, os próximos dois são y, o ângulo e a saúde etc. Então:

F5DGP@%&002DFTK#OP1F

poderia armazenar todas as mesmas informações que os outros exemplos.

As grades de mosaico podem ser armazenadas como apenas uma string, com cada caractere representando um tipo diferente de mosaico, por exemplo:

i789pog5h3kl

onde eu poderia significar lava, 9 significa grama etc.

Iain
fonte
Isso é mais parecido com o que estou perguntando. Menciono XML na minha pergunta, mas ainda assim as pessoas sugerem!
Adam Harte
1
Adicionar {} em torno disso e você tem basicamente JSON ;-)
coderanger
Você também precisará adicionar várias aspas. Provavelmente dobraria o número de caracteres, mas você aninharia objetos.
Iain
1

Se você está codificando .Net, o XML é super fácil de usar, pois você pode serializar / desserializar sua classe de nível para dentro / fora do XML com apenas algumas linhas e, em seguida, tudo em uma classe bem gerenciada.

O TheMap seria uma variável do tipo Mapa na qual você carrega todos os seus dados.

Dim TheMap As New Map

Supondo que você já tenha uma classe Map criada, isso salvaria seu mapa em 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()

Isso carregaria esse XML de volta na sua classe de mapa, para ser usado novamente no 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 deste ponto, seu arquivo XML agora é carregado em sua classe para facilitar o uso.

Quanto ao seu problema de "String de nível", o que foi declarado antes funcionaria muito bem, você poderia usar o número de Seed como "String de nível".

Caso contrário, você pode pré-gerar quantos mapas diferentes quiser e salvá-los todos com uma "String de nível" e depois usá-lo para abrir o mapa apropriado.

jblaske
fonte
+1 mesmo que eu odeie XML - Se a linguagem fornecer um formato de serialização padrão, é melhor considerar isso primeiro, especialmente se for um formato que outras ferramentas possam analisar teoricamente (como XML / JSON / YAML).
0

Eu usaria um structdispositivo simples ou semelhante (dependendo do seu idioma) para armazenar todo o estado do jogo em um local central. Se você deseja a proteção de setters / getters, pode envolver a estrutura em a class.

Se você preferir , use campos de bits ou simplesmente faça você mesmo a manipulação de bits usando operadores bit a bit.

Esteja ciente de que, em alguns idiomas, as regras para preenchimento e empacotamento de estruturas podem ser um pouco complicadas - mas também pode não ser muito importante para o seu caso, se você tiver um ou dois bytes de preenchimento.

Você também pode usar um #pragma(como #pragma pack(1)) ou um __attribute__para compactar de perto a estrutura, eliminando o preenchimento. Isso pode ou não funcionar, dependendo do seu compilador e arquitetura de destino.

Observe que o uso de campos de bits e pragmas ou atributos de pacotes pode reduzir a portabilidade. Nas arquiteturas de hardware, a resistência do campo struct (ordem dos bytes) também pode ser alterada. Portanto, você pode evitar isso se estiver tentando portabilidade.

(Por exemplo, Pac-Man, essa estrutura pode conter ingenuamente um ID de mapa ou semente de mapa, uma posição x e y de Pac-Man, quatro posições x e y fantasmas e um campo de bits grande para a presença ou ausência de 32 a 64 pellets, qualquer que seja o máximo.)

Depois de ter sua estrutura, passe-a para algo como uma função xxencode :

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

Escrever esta função é um pouco propenso a erros; você precisa mudar e combinar bytes conforme necessário para obter, por exemplo, 6 bits de cada vez, depois converter em um caractere apropriado. Eu pessoalmente tentaria caçar o código de outra pessoa, a menos que estivesse fazendo isso por "diversão" (e provavelmente desejaria uma suíte de testes).

Nunca subestime o poder da velha escola structnos lugares certos. Nós usamos muito isso para jogos GBA e DS aqui.

leander
fonte
A serialização de estrutura bruta é um novo código não-portátil e extremamente frágil. A menos que seja a etapa final de criação de dados para uma plataforma com recursos muito limitados, é uma otimização muito prematura. Existe uma razão para você preferir 6 bits a 8? De qualquer forma, o formato não pode ser lido por humanos, você também pode tirar proveito da velocidade e depuração de um layout de estrutura real.
@ Joe: Eu mencionei que não é portátil e algumas das preocupações em potencial. A pergunta pedia especificamente uma "string de nível" legível por humanos, como os jogos antigos da Sega e mencionava a conversão de base60; passar uma estrutura por algo como xxencode fará isso. Isso não é necessariamente uma otimização prematura: uma estrutura simples, não compactada em bits, é uma maneira bem "central" de armazenar dados salvos e pode simplificar grande parte do código que interage com os dados. Veja, por exemplo, os artigos recentes de Noel Llopis sobre estruturas de não-membros e não-amigos e programação "de dentro para fora". Este é apenas o KISS.
Leander
1
Eu concordo totalmente com esse caminho. Sim, não é portátil entre versões nem sistemas, mas, novamente, os save-games não são recursos que precisam ser reutilizados durante o desenvolvimento ou copiados em plataformas. A depuração dificilmente é um problema com uma leitura / gravação no local, implemente-a uma vez e funcionará para sempre. Se a extensibilidade for realmente um problema - adicione um número de versão como o primeiro ybyte / palavra e você poderá ativá-lo (embora isso introduza uma vulnerabilidade de dados). Não é como se o xml resolvesse problemas de versão - eles tendem a estar mais envolvidos do que apenas definir um valor. Sim, sou pragmática.
Kaj
0

XML é bom para documentos estruturados arbitrariamente (os elementos podem aparecer em diferentes níveis da árvore) ou para incorporação em formato externo (como colocar svg em uma página xhtml). Se você não possui esses requisitos, é um formato realmente ineficiente e é preferível algo mais simples como csv ou json.

Jethro Larson
fonte