estado de jogo salvando / carregando?

12

qual é o método ou algoritmo mais usado para salvar o estado do jogo (perfis), bancos de dados em arquivos de texto, como ocorre a criptografia e coisas relacionadas.

Eu vi César IV usar o mySQL.

alguma sugestão.

NativeCoder
fonte

Respostas:

20

Tenho certeza de que é feito com mais frequência em um formato proprietário (binário), basta escrever cada variável de qualquer estrutura de estado do jogo usada em um arquivo ou ler esse arquivo novamente em uma instância da estrutura. No C ++, por exemplo, você pode tratar um objeto como uma matriz de bytes e apenas fwrite () em um arquivo ou fread () do arquivo e convertê-lo novamente em sua forma de objeto.

Se você estiver usando Java ou outro idioma, poderá optar por usar a serialização para tornar a tarefa realmente fácil. A explicação do Java está na parte inferior desta resposta; outras linguagens (de nível superior ao C ++) certamente também possuem suas próprias bibliotecas de serialização ou funções internas, que você deve optar por usar. Não importa quão ineficiente possa ser a rotina de serialização, hoje em dia o espaço no disco rígido é barato, especialmente quando o estado do jogo não deve ser muito grande e impede que você escreva e mantenha suas próprias rotinas de serialização.

Você absolutamente não deve usar o MySQL (basta ler a primeira frase desta revisão ). Se você realmente precisa de um banco de dados relacional por algum motivo, use SQLite ; é um sistema de banco de dados leve e existe em um único arquivo de banco de dados. Mas para a maioria dos jogos, os bancos de dados relacionais não são o caminho a percorrer, e as empresas que tentam usá-los geralmente acabam usando-os como uma tabela de pesquisa de valor-chave em vez de um verdadeiro banco de dados relacional.

Qualquer tipo de criptografia de arquivos de disco local é apenas ofuscação ; qualquer hacker apenas farejará os dados logo após o seu programa os descriptografar. Pessoalmente, sou contra esse tipo de coisa, ESPECIALMENTE com jogos para um jogador. Minha opinião é que os proprietários do jogo (clientes pagantes, lembre-se) devem ter permissão para invadir o jogo, se quiserem. Em alguns casos, isso pode criar um maior senso de comunidade, e "mods" podem ser desenvolvidos para o seu jogo que atraem mais clientes para você. O exemplo mais recente que vem à mente é o mod Portal no Minecraft, lançado recentemente. Ele é publicado em sites de notícias de jogadores em toda a Internet, e você pode apostar que aumentou as vendas do Minecraft.


Se por algum motivo você é realmente louco o suficiente para usar o Java para desenvolvimento de jogos como eu, aqui está uma rápida explicação da serialização em Java:

Mantenha todos os dados do estado do jogo em uma classe, torne a classe serializável implementando Serializable, use a transientpalavra - chave se você misturar variáveis ​​instanciadas especiais na classe (objetos transitórios não são serializados) e simplesmente use ObjectOutputStreampara gravar em um arquivo e ObjectInputStreamler um arquivo . É isso aí.

Ricket
fonte
2
Hehe, The Witcher não criptografou seus dados. Muito fácil abrir o arquivo do jogo salvo em um editor hexadecimal, mexer um pouco com ele e obter todas as cartas de nuas ... oh Deus, me desculpe!
Anthony
Eu usei a serialização binária do .NET para salvar jogos, e o C # talvez seja uma plataforma um pouco mais razoável do que o Java na qual os jogos podem ser desenvolvidos. Eu gosto de como ele salva automaticamente todo um gráfico de objetos automaticamente. Isso costumava ser muito mais difícil. É claro que agora existe o desafio de excluir peças desnecessárias como as texturas, mas isso não é grande coisa a se realizar.
BlueMonkMN
Não discordo que o C # seja um pouco mais popular que o Java para desenvolver jogos. Mais razoável implica alguma subjetividade e, como desenvolvedor de XNA e Java, prefiro Java. Sinta-se à vontade para listar instruções sobre serialização em C # também, se desejar, nunca o fiz, mas posso editá-lo em minha resposta. A serialização de Java salva o gráfico inteiro do objeto e a palavra-chave 'transitória' nas variáveis ​​exclui partes desnecessárias como texturas; qualquer membro não transitório deve ser serializável ou você pode escrever um método writeObject personalizado para contornar isso.
Ricket 12/08/10
O pickle do Python salvará um gráfico inteiro de objetos para você e também é fácil de usar. Quando chega a hora para desserializar lhe são entregues objetos de volta totalmente hidratados, fácil como torta: docs.python.org/library/pickle.html
drhayes
Ao usar o formatador binário interno, excluir determinados campos da serialização em C # / .NET é tão simples quanto adorná-los com o atributo [Não serializado]. O que é fácil ignorar, no entanto, é que os eventos gerados pelo compilador (ou seja, qualquer evento sem operadores de adição / remoção personalizados) criam um campo de apoio para armazenar seus assinantes. Portanto, é fácil incluir acidentalmente (e sem saber) manipuladores de eventos em um gráfico de objetos serializados. Para contornar isso, você precisa adicionar o atributo NonSerialized à declaração do evento, mas você precisa adicionar o qualificador "field:":[field: NonSerialized]
Mike Strobel
3

Se você estiver escrevendo seu próprio código, digamos em C ++, poderá usar arquivos binários. Os arquivos binários oferecem uma forma de criptografia através da ofuscação. Mas, como documentado em todas as internets, essa é uma forma muito fraca de segurança.

OU, você pode usar algo como RapidXML se quiser uma solução legível por humanos.

Se você estiver usando algum tipo de estrutura, verifique suas funções de suporte a arquivos. HTH

JustBoo
fonte
0

A maioria dos jogos que eu vi usa código escrito à mão para ler / gravar em arquivos binários. Frequentemente, o formato em disco será uma réplica exata do layout de memória de um objeto, portanto, o carregamento é apenas:

  1. Leia o arquivo na memória.
  2. Converter o ponteiro no buffer para o tipo de objeto.

É rápido, mas é uma tarefa difícil de manter, específica da plataforma e inflexível.

Ultimamente, tenho visto alguns jogos usando SQLite. As pessoas com quem conversei parecem gostar.

maravilhoso
fonte
0

A serialização é mencionada em algumas das outras respostas, e eu concordo que é uma solução razoável. Porém, uma das formas de falha é no controle de versão.

Digamos que você libere seu jogo e as pessoas o joguem e criem alguns jogos salvos. Em um patch posterior, você deseja corrigir um erro ou adicionar alguns recursos. Se você usar um método simples de serialização binária e adicionar um membro às suas classes, sua serialização poderá não ser compatível com os antigos jogos salvos quando os clientes instalarem o patch.

Portanto, qualquer que seja a abordagem usada, pense nesse problema antes de lançar o jogo pela primeira vez! Os clientes não ficarão felizes se tiverem que recomeçar após a aplicação de um patch.

Uma maneira de evitar isso é fazer com que cada tipo de dados básico implemente os métodos Save (version) e Load (version) que sejam inteligentes o suficiente para saber quais dados salvar e carregar para cada versão do jogo. Dessa forma, você pode oferecer suporte à compatibilidade com versões anteriores de seus jogos salvos e falhar normalmente se um usuário tentar carregar um jogo salvo de uma versão mais recente do jogo em execução.

kevin42
fonte
1
Como uma observação rápida: implementar as funções "salvar" e "carregar" para cada versão do jogo rapidamente se transforma em um pesadelo de manutenção. Eu encontrei uma solução melhor é incorporar um número de versão, escrever funções de "salvar / carregar" para a versão atual do jogo e escrever uma função de "conversão" da versão não atual mais recente para a versão atual. Depois, mantenha todas as suas funções de conversão por perto. Tentar carregar um jogo com dez versões anteriores executaria dez funções de conversão em série, e a função nativa "carregar a versão atual do jogo".
precisa saber é o seguinte
Muito mais fácil de manter a longo prazo - manter uma série de funções "carregar todos os arquivos de jogos anteriores" rapidamente se transforma em uma operação em tempo polinomial.
precisa saber é o seguinte
Nos jogos em que trabalhei, não é tão complexo - em vez de manter várias cópias dos métodos de carregamento / gravação, o único método usa o número da versão para determinar quais bytes ler e gravar. Isso fica ainda mais fácil se você não apenas escrever estruturas, mas implementar o carregamento e o salvamento como operações no nível do tipo de dados primitivo (por exemplo, ReadByte / ReadFload / etc). Então, se você adicionar um novo membro, poderá fazer algo como se (versão> 10) {someNewByte = ReadByte (); } A implementação dessa maneira também facilita o suporte a diferentes plataformas endian.
precisa saber é o seguinte
Outra maneira de evitar o aprimoramento do suporte à versão é evitar que os métodos de serialização / desserialização de seus objetos gravem / leiam valores individuais diretamente de um fluxo. Em vez disso, os objetos podem gravar seus próprios dados em um pacote de propriedades usando pares de chave / valor e, em seguida, gravar o pacote no fluxo. Durante a desserialização, os objetos podem ler valores da bolsa pelo nome. Mas, em vez de sugerir verificações de número de versão específicas, como o kevin42, você pode ter regras para lidar com valores ausentes (por exemplo, usar um valor padrão padrão quando o valor exigido não estiver na bolsa).
Mike Strobel
0

Se você pode usar o mesmo formato usado para armazenar seus níveis, é um bônus.

Por exemplo, um jogo como o Peggle pode ter um layout inicial do tabuleiro, número de bolas, pontuação atual (0 para o tabuleiro inicial) etc. Então, um jogo salvo pode usar exatamente o mesmo formato.

Se você usar o mesmo formato, o jogo salvo e a carga nivelada poderão compartilhar código, facilitando seu trabalho!

Para um jogo com arquivos de mapa realmente grandes, o jogo salvo pode incluir o arquivo de mapa por referência. Os arquivos de nível inicial podem fazer a mesma coisa, até, o que torna mais fácil voltar a esse mapa se, por algum motivo, a história retornar o personagem ao mesmo lugar, exceto que alguns dos NPCs estão mortos e há cadáveres sobre.

Zan Lynx
fonte