'XML binário' para dados do jogo?

17

Estou trabalhando em uma ferramenta de edição de nível que salva seus dados como XML.

Isso é ideal durante o desenvolvimento, pois é indolor fazer pequenas alterações no formato dos dados e funciona bem com dados do tipo árvore.

A desvantagem, porém, é que os arquivos XML estão bastante inchados, principalmente devido à duplicação de nomes de tags e atributos. Também devido aos dados numéricos ocuparem muito mais espaço do que o uso de tipos de dados nativos. Um pequeno nível pode facilmente terminar em 1Mb +. Quero reduzir esses tamanhos significativamente, principalmente se o sistema for usado para um jogo no iPhone ou em outros dispositivos com memória relativamente limitada.

A solução ideal, para memória e desempenho, seria converter o XML em um formato de nível binário. Mas eu não quero fazer isso. Eu quero manter o formato bastante flexível. O XML facilita muito adicionar novos atributos aos objetos e atribui a eles um valor padrão se uma versão antiga dos dados for carregada. Então, eu quero manter a hierarquia de nós, com atributos como pares nome-valor.

Mas preciso armazenar isso em um formato mais compacto - para remover a duplicação maciça de nomes de tags / atributos. Talvez também para atribuir tipos nativos aos atributos, portanto, por exemplo, dados de ponto flutuante são armazenados como 4 bytes por flutuador, não como uma sequência de texto.

O Google / Wikipedia revela que 'XML binário' dificilmente é um problema novo - já foi resolvido várias vezes. Alguém aqui tem experiência com algum dos sistemas / padrões existentes? - é ideal para o uso de jogos - com uma biblioteca de analisador / carregador gratuita, leve e multiplataforma (C / C ++) disponível?

Ou devo reinventar essa roda eu mesmo?

Ou será melhor esquecer o ideal e apenas compactar meus dados .xml brutos (eles devem se compactar bem com a compactação do tipo zip) e apenas carregar a memória / desempenho em carga?

bluescrn
fonte
1
O XML pode ser compactado usando gzip et al muito bem.
ThiefMaster 26/03/12

Respostas:

18

Usamos muito o XML binário para Superman Returns: The Videogame . Estamos falando de milhares e milhares de arquivos. Funcionou bem, mas honestamente não parecia valer a pena. Ele consumiu uma fração perceptível do nosso tempo de carregamento, e a "flexibilidade" do XML não aumentou. Depois de um tempo, nossos arquivos de dados tinham muitos identificadores estranhos, referências externas que precisavam ser mantidas em sincronia e outros requisitos estranhos para que eles fossem realmente editáveis ​​por humanos.

Além disso, o XML é realmente um formato de marcação, e não um formato de dados. É otimizado para muito texto com tags ocasionais. Não é ótimo para dados totalmente estruturados. Não foi minha decisão, mas se tivesse sido e eu soubesse o que sei agora, provavelmente teria feito JSON ou YAML. Ambos são concisos o suficiente para não exigir compactação e são otimizados para representar dados , não texto .

maravilhoso
fonte
1
Existe uma versão binária do JSON chamada BSON .
Philipp
12

Armazene e edite seus níveis como XML normal, mas faça com que seu mecanismo de jogo prenda-o lentamente em XML binário durante o carregamento e salve o XML binário de volta no disco para que possa carregar na próxima vez (se o XML bruto não tiver sido alterado) .

Algo assim:

data loadXml(xmlFile)
{
    if (xmlFile has changed OR binFile doesn't exist)
    {
        binFile = convertToBinary(xmlFile)
        save(binFile)
    }
    return loadBinaryXml(binFile)
}

Dessa forma, você obtém o melhor dos dois mundos. No lançamento, você só precisa garantir que todos os arquivos binários estejam lá.

Peter Alexander
fonte
5

Os buffers de protocolo do Google parecem ser o caminho a seguir, mas eu não os usei.
http://code.google.com/p/protobuf/

Você define um arquivo .proto que descreve o formato do arquivo:

message Person {
  required int32 id = 1;
  required string name = 2;
  optional string email = 3;
}

Isso é compilado com uma ferramenta de linha de comando que gera classes C / C ++ para gravar e analisar arquivos de dados binários no formato de dados definido anteriormente. Existem também algumas extensões para diferentes linguagens de programação.

A desvantagem do ProtocolBuffer é que eles não são um formato de texto sem formatação. Você precisaria de uma ferramenta para gerá-los, ler e editá-los. Mas isso não deve ser um problema se você os estiver usando apenas para trocar dados entre o editor do jogo e o jogo. Eu não o usaria para definir arquivos de configuração;)

A compactação dos arquivos xml brutos também deve funcionar. Que tipo de jogo você está fazendo? Se for baseado em nível, você deverá carregar todos os recursos necessários apenas uma vez quando o nível for carregado.

atualização: existem vários projetos para outros idiomas, como o C #, para trabalhar com ProtocolBuffers:
http://code.google.com/p/protobuf/wiki/ThirdPartyAddOns

Stephen
fonte
Um serializador não está adaptado a esse tipo de problema? Acho que não, mas não vejo uma diferença clara. Mas para mim esta resposta parece apropriada. Mas também tar / gzip os arquivos xml reduzirão muito seu tamanho (já que é texto, mas acho que também funcionará para xml), portanto essa pode ser a solução "mais fácil". De qualquer forma, XML é uma linguagem fácil, mas é muito cara em termos de análise / uso de memória: quando você usa XML, deve ler / gravar o menor número de vezes possível.
jokoon
É uma opção interessante, mas parece mais uma alternativa completa ao uso de XML em qualquer lugar do pipeline. Para ser sincero, não ficaria muito entusiasmado com o código gerado - e outra complicação é que estou usando C # para o lado das ferramentas (estou feliz por as ferramentas continuarem trabalhando com os grandes arquivos .XML ) Um conversor XML-> PB pode ser uma opção, embora eu ainda esteja procurando por algo que seja mais 'XML binário de uso geral', em vez de maneiras de criar 'dados de nível binário' específicos (mesmo que isso seja um pouco mais eficientes)
bluescrn
"Estou usando C # para o lado das ferramentas", existem vários projetos para c #. atualizei minha resposta.
Stephen
@ Bluescrn, eu não estaria muito preocupado com o código gerado. O Google oferece suporte de primeira classe a C ++, Java e Python. Eles o usam extensivamente internamente; o código gerado é bastante robusto. Uma grande vantagem do PB é o seu programa de ferramentas contra um .protoarquivo, que quase elimina problemas de falta de comunicação. Os protótipos são muito mais fáceis de ler / manter do que um esquema xml, se você tiver a disciplina (e tempo) para usar esquemas xml.
Deft_code 26/03/12
4

E o formato JSON?

http://www.json.org/xml.html

Sven
fonte
Parece um pouco mais compacto que o XML, mas ainda tem o problema principal de nomes de atributos duplicados. Se o arquivo contivesse uma lista de objetos de jogo com os atributos 'XPosition', 'YPosition' e 'Scale', as seqüências de caracteres 'XPosition' / 'YPosition' / 'Scale' seriam duplicadas para cada objeto de jogo. Esta é a principal coisa que pretendo 'comprimir' no momento
bluescrn
1
@ bluescrn: Não, não tem esse problema. Objetos são uma estrutura; você também pode usar matrizes [que, apenas, parecem assim]. Isso significa que você pode terminar com algo assim para armazenar os nomes e as propriedades dos carros: "cars":{"ford":[8C,FA,BC,2A,384FFFFF],"holden":[00,00,04,FF,04FF54A9]}você pode até omitir o identificador "cars" e ir direto para uma matriz se souber onde será o campo dos carros. Você mesmo pode omitir o "Ford" e nomes "Holden" se você não precisa salvar os dados, deixando-o com: [...,[[8C,FA,BC,2A,384FFFFF],[00,00,04,FF,04FF54A9]]]. Fica mais compacto?
doppelgreener
1
@Axidos: Se você quiser tornar a marcação ilegível e não estruturada, é melhor que seja binária. Além disso, é uma economia falsa, a menos que você esteja analisando dados não compactados durante o tempo de execução (nesse caso, você provavelmente está ferrado de qualquer maneira) ou de alguma forma restrito a algumas centenas de bytes de memória de string durante a análise (a menos que você esteja um microondas, você não é).
@ Joe: bluescrn parece estar procurando um formato legível que não tem nomes duplicados. Eu estava ilustrando a capacidade do JSON de oferecer exatamente isso. Eu concordo totalmente que, em um determinado momento, você também pode se perguntar por que está se incomodando com uma marcação como essa.
doppelgreener
4

Use JSON.

(Com base na resposta da Munificent e em grande parte em resposta às suas preocupações expressas em outros lugares)

Você mencionou a preocupação de que o JSON tenha o problema de desperdiçar elementos de nomeação de espaço, como XML. Não faz.

O JSON é construído em duas estruturas: pares nome / valor ( objetos ) e listas ordenadas de valores ( matrizes ). XML é criado apenas em pares nome / valor.

Se você acha que o JSON depende de objetos que você leu, ele foi desenvolvido para ser auto-descritivo e legível por humanos, como este (usando pares de dígitos octais para representar bytes únicos):

{
    "some": ...,
    "data": ...,
    "fields": ...,
    "cars": [
        {"name":"greg","cost":8C,"speed":FA,"age":04,"driverID":384FFFFF},
        {"name":"ole rustbucket","cost":00,"speed":00,"age":2A,"driverID":04FF54A9}
    ]
}

No entanto, você também tem a opção de escrevê-lo dessa maneira, desde que saiba onde tudo estará (e assim possa procurar o índice 4, em vez de objetos "carros", para obter sua lista de carros):

{
    [
        ...,
        ..., 
        ...,
        [["greg",8C,FA,04,384FFFFF],["ole rustbucket",00,00,2A,04FF54A9]],
        ...,
    ]
}

Será que ela obtenha mais conciso do que apenas tendo [, ], ,e seus valores?

Bem, isso acontece se você estiver disposto a se aproximar cada vez mais de um fluxo binário puro.

"cars":{"names":["greg","ole rustbucket"],"stream":8CFA04384FFFFF00002A04FF54A9}
or
[["greg","ole rustbucket"],8CFA04384FFFFF00002A04FF54A9]

Apenas não atire na perna otimizando demais.

doppelgreener
fonte
2

Sei que você aceitou uma resposta, mas o Google "Fast Infoset" (XML binário) e vtd-xml.

Embora o último (VTD) possa não resolver o aspecto da compactação do uso de XML, ele pode acelerar o acesso ao nó em arquivos grandes, consideravelmente (ele usa um 'dicionário' de deslocamentos binários para ir para os nós e não cria objetos para cada nó , trabalhe na sequência XML original). Portanto, sua pesquisa em XML é [mais rápida] e não requer tanta memória em processo para acessar / manipular o documento XML.

Ambos os itens acima têm ligações nos idiomas populares (que incluem C #).

Felicidades

Rico

Big Rich
fonte
1

Você poderia tentar o Karvonite . É para ser ágil. É uma estrutura de persistência que se adaptará às alterações em seus dados razoavelmente bem (o que é bom comparado a lidar com você mesmo). Na verdade, não tenho certeza de como os dados estão estruturados, mas os arquivos são muito menores que os arquivos inchados de xml. (Presumo que ele salve os dados em um formato binário em vez de texto como xml)

A única maneira de pensar com isso é que, se seus dados são corrompidos ou um pouco confusos de uma maneira que a Karvonite não gosta, você fica à mercê de seus criadores, a menos que você descubra como a estrutura do dados funcionam.

A maneira como você especifica como salvar / carregar seus dados é apenas abrir o editor de persistência, importar sua montagem com todos os objetos de dados e marcar algumas caixas de seleção para mostrar quais objetos você deseja suportar e quais campos / propriedades salvar.

Pode valer a pena uma tentativa. Desde que você usa C #, isso se encaixa perfeitamente no seu idioma, pois funciona com o XNA (Windows, Xbox360 e Windows Phone 7, no qual acho que você está interessado desde que mencionou o iPhone?).

Edit: Acabei de notar que você está usando apenas C # para as ferramentas. Isso provavelmente não se encaixaria muito bem no seu fluxo de trabalho. Por alguma razão, eu tinha XNA na minha cabeça.

Michael Coleman
fonte