Estou desenvolvendo um jogo de plataformas 2D com alguns amigos da uni. Nós o baseamos no XNA Platformer Starter Kit, que usa arquivos .txt para armazenar o mapa de blocos. Embora isso seja simples, não nos dá controle e flexibilidade suficientes com o design de níveis. Alguns exemplos: para várias camadas de conteúdo, são necessários vários arquivos, cada objeto é fixado na grade, não permite a rotação de objetos, número limitado de caracteres etc. Por isso, estou pesquisando como armazenar os dados de nível e arquivo de mapa.
Isso diz respeito apenas ao armazenamento do sistema de arquivos dos mapas de blocos, não à estrutura de dados a ser usada pelo jogo enquanto estiver em execução. O mapa de blocos é carregado em uma matriz 2D, portanto, esta pergunta é sobre qual fonte a qual preencher a matriz.
Raciocínio para DB: Na minha perspectiva, vejo menos redundância de dados usando um banco de dados para armazenar os dados do bloco. Os ladrilhos na mesma posição x, y com as mesmas características podem ser reutilizados de nível para nível. Parece que seria simples o suficiente escrever um método para recuperar todos os blocos que são usados em um nível específico do banco de dados.
Raciocínio para JSON / XML: arquivos editáveis visualmente, as alterações podem ser rastreadas via SVN muito mais facilmente. Mas há conteúdo repetido.
Você tem alguma desvantagem (tempos de carregamento, tempos de acesso, memória etc.) em comparação com os outros? E o que é comumente usado na indústria?
Atualmente, o arquivo se parece com isso:
....................
....................
....................
....................
....................
....................
....................
.........GGG........
.........###........
....................
....GGG.......GGG...
....###.......###...
....................
.1................X.
####################
1 - Ponto inicial do jogador, Saída do nível X,. - Espaço vazio, # - Plataforma, G - Gem
Respostas:
Portanto, lendo sua pergunta atualizada, parece que você está mais preocupado com "dados redundantes" no disco, com algumas preocupações secundárias sobre o tempo de carregamento e o que o setor usa.
Primeiro, por que você está preocupado com dados redundantes? Mesmo para um formato inchado como XML, seria bastante trivial implementar a compactação e diminuir o tamanho dos seus níveis. É mais provável que você ocupe espaço com texturas ou sons do que seus dados de nível.
Segundo, é mais provável que um formato binário seja carregado mais rápido que um formato baseado em texto que você precisa analisar. Duplamente, se você pode simplesmente deixá-lo na memória e ter suas estruturas de dados ali mesmo. No entanto, existem algumas desvantagens nisso. Por um lado, os arquivos binários são quase impossíveis de depurar (especialmente para pessoas de criação de conteúdo). Você não pode diferenciá-los ou versioná-los. Mas eles serão menores e carregarão mais rapidamente.
O que alguns mecanismos fazem (e o que considero uma situação ideal) é implementar dois caminhos de carregamento. Para o desenvolvimento, você usa algum tipo de formato baseado em texto. Realmente não importa qual formato específico você usar, desde que você use uma biblioteca sólida. Para a liberação, você alterna para carregar uma versão binária (carregamento menor e mais rápido, possivelmente sem entidades de depuração). Suas ferramentas para editar os níveis cospem os dois. É muito mais trabalhoso do que apenas um formato de arquivo, mas você pode obter o melhor dos dois mundos.
Tudo isso dito, acho que você está pulando um pouco a arma.
O primeiro passo para esses problemas é sempre descrever o problema que você está enfrentando. Se você sabe quais dados você precisa, você sabe quais dados você precisa armazenar. A partir daí, é uma questão de otimização testável.
Se você tiver um caso de uso para testar, poderá testar alguns métodos diferentes de armazenamento / carregamento de dados para medir o que considerar importante (tempo de carregamento, uso de memória, tamanho do disco etc.). Sem saber nada disso, é difícil ser mais específico.
fonte
Minha recomendação é usar um formato de arquivo binário personalizado. No meu jogo, só tenho um
SaveMap
método que percorre e salva cada campo usando aBinaryWriter
. Isso também permite que você escolha compactá-lo, se desejar, e oferece mais controle sobre o tamanho do arquivo. ou seja, salve um emshort
vez de umint
se você souber que não será maior que 32767. Em um loop grande, salvar algo como um emshort
vez de umint
pode significar um tamanho de arquivo muito menor.Além disso, se você seguir esse caminho, recomendo que sua primeira variável no arquivo seja um número de versão.
Considere, por exemplo, uma classe de mapa (muito simplificada):
Agora, digamos que você queira adicionar uma nova propriedade ao seu mapa, digamos, um
List
dosPlatform
objetos. Eu escolhi isso porque é um pouco mais envolvido.Primeiro de tudo, você incrementa
MapVersion
e adiciona oList
:Atualize o método save:
Então, e é aqui que você realmente vê o benefício, atualize o método de carregamento:
Como você pode ver, o
LoadMaps
método pode não apenas carregar a versão mais recente do mapa, mas também versões mais antigas! Quando carrega os mapas mais antigos, você tem controle sobre os valores padrão que ele usa.fonte
História curta
Uma alternativa interessante para esse problema é armazenar seus níveis em um bitmap, um pixel por bloco. O uso do RGBA permite facilmente que quatro dimensões diferentes (camadas, IDs, rotação, tonalidade etc.) sejam armazenadas em uma única imagem.
Longa história
Essa pergunta me lembrou quando Notch transmitiu sua entrada no Ludum Dare há alguns meses e gostaria de compartilhar caso você não saiba o que ele fez. Eu pensei que era realmente interessante.
Basicamente, ele usou bitmaps para armazenar seus níveis. Cada pixel no bitmap correspondia a um "bloco" no mundo (não um bloco no seu caso, pois era um jogo transmitido por raios, mas perto o suficiente). Um exemplo de um de seus níveis:
Portanto, se você usar RGBA (8 bits por canal), poderá usar cada canal como uma camada diferente e até 256 tipos de blocos para cada um deles. Isso seria suficiente? Ou, por exemplo, um dos canais pode manter a rotação do bloco como você mencionou. Além disso, a criação de um editor de níveis para trabalhar com este formato também deve ser bastante trivial.
E o melhor de tudo: você nem precisa de um processador de conteúdo personalizado, pois você pode carregá-lo no XNA como qualquer outro Texture2D e ler os pixels em loop.
No IIRC, ele usou um ponto vermelho puro no mapa para sinalizar para um inimigo e, dependendo do valor do canal alfa desse pixel, ele determinaria o tipo de inimigo (por exemplo, um valor alfa de 255 pode ser um bastão, enquanto 254 seria um zumbi ou algo mais).
Outra idéia seria subdividir os objetos do jogo naqueles que são fixos em uma grade e aqueles que podem se mover "finamente" entre as peças. Mantenha os blocos fixos no bitmap e os objetos dinâmicos em uma lista.
Pode até haver uma maneira de multiplexar ainda mais informações nesses 4 canais. Se alguém tiver alguma idéia sobre isso, me avise. :)
fonte
Você provavelmente poderia se safar de quase tudo. Infelizmente, os computadores modernos são tão rápidos que essa quantidade presumivelmente relativamente pequena de dados não faria uma diferença perceptível no tempo, mesmo que armazenados em um formato de dados inchado e mal construído, que exige uma enorme quantidade de processamento para ser lido.
Se você deseja fazer a coisa "certa", cria um formato binário como sugerido pelo Drackir, mas se você fizer outra escolha por algum motivo bobo, provavelmente não voltará e o morderá (pelo menos não em termos de desempenho).
fonte
Veja esta pergunta: 'XML binário' para dados do jogo? Eu também optaria por JSON ou YAML ou mesmo XML, mas isso tem muita sobrecarga. Quanto ao SQLite, eu nunca usaria isso para armazenar níveis. Um banco de dados relacional é útil se você espera armazenar muitos dados relacionados (por exemplo, possui links relacionais / chaves estrangeiras) e se você solicita uma grande quantidade de consultas diferentes, o armazenamento de tilemaps não parece fazer esses tipos de coisas e acrescentaria complexidade desnecessária.
fonte
O problema fundamental dessa pergunta é que ela conflita dois conceitos que nada têm a ver um com o outro:
Você pode armazenar seus arquivos como texto sem formatação em algum formato arbitrário, JSON, script Lua, XML, um formato binário arbitrário, etc. E nada disso exigirá que sua representação na memória desses dados assuma qualquer forma específica.
O trabalho do seu código de carregamento de nível é converter o paradigma de armazenamento de arquivos em sua representação na memória. Por exemplo, você diz: "Vejo menos redundância de dados usando um banco de dados para armazenar os dados do bloco". Se você deseja menos redundância, é algo que seu código de carregamento de nível deve procurar. Isso é algo que sua representação na memória deve ser capaz de lidar.
É não algo suas necessidades de formato de arquivo para se preocupar com. E aqui está o porquê: esses arquivos precisam vir de algum lugar.
Você os escreverá manualmente ou usará algum tipo de ferramenta que cria e edita dados de nível. Se você estiver escrevendo à mão, o mais importante é um formato fácil de ler e modificar. Redundância de dados não é algo que o seu formato ainda precisa considerar, porque você estará gastando mais de seu tempo editando os arquivos. Deseja realmente usar manualmente quaisquer mecanismos que você criar para fornecer redundância de dados? Não seria melhor usar seu tempo apenas para o seu carregador de nível lidar com isso?
Se você possui uma ferramenta que os cria, o formato real só importa (na melhor das hipóteses) por uma questão de legibilidade. Você pode colocar qualquer coisa nesses arquivos. Se você deseja omitir dados redundantes no arquivo, crie um formato que possa fazê-lo e faça com que sua ferramenta use essa capacidade corretamente. Seu formato de mapa de mosaico pode incluir compactação RLE (codificação de comprimento de execução), compactação de duplicação, quaisquer técnicas de compactação que você desejar, porque é o seu formato de mapa de mosaico.
Problema resolvido.
Existem bancos de dados relacionais (RDB) para resolver o problema de executar pesquisas complexas em grandes conjuntos de dados que contêm muitos campos de dados. O único tipo de pesquisa que você faz em um mapa de blocos é "obter blocos na posição X, Y". Qualquer matriz pode lidar com isso. Usar um banco de dados relacional para armazenar e manter os dados do mapa de blocos seria um exagero extremo e não valeria nada.
Mesmo que você construa alguma compactação em sua representação na memória, ainda poderá vencer os RDBs facilmente em termos de desempenho e presença de memória. Sim, você terá que realmente implementar essa compactação. Mas se o tamanho dos dados na memória é uma preocupação que você consideraria um RDB, provavelmente você realmente deseja implementar tipos específicos de compactação. Você será mais rápido que um RDB e terá menos memória ao mesmo tempo.
fonte
Para dados realmente simples, eu provavelmente seguiria a rota XML, se você já estiver familiarizado com o carregamento e a análise de XML.
fonte