«XML binaire» pour les données de jeu?

17

Je travaille sur un outil d'édition de niveau qui enregistre ses données au format XML.

C'est idéal pendant le développement, car il est indolore d'apporter de petites modifications au format de données, et cela fonctionne bien avec des données arborescentes.

L'inconvénient, cependant, est que les fichiers XML sont plutôt gonflés, principalement en raison de la duplication des noms de balises et d'attributs. Également dû au fait que les données numériques prennent beaucoup plus d'espace que l'utilisation de types de données natifs. Un petit niveau pourrait facilement devenir 1 Mo +. Je veux réduire ces tailles de manière significative, surtout si le système doit être utilisé pour un jeu sur l'iPhone ou d'autres appareils avec une mémoire relativement limitée.

La solution optimale, pour la mémoire et les performances, serait de convertir le XML en un format de niveau binaire. Mais je ne veux pas faire ça. Je veux garder le format assez flexible. XML facilite l'ajout de nouveaux attributs aux objets et leur donne une valeur par défaut si une ancienne version des données est chargée. Je veux donc garder la hiérarchie des nœuds, avec des attributs comme paires nom-valeur.

Mais j'ai besoin de stocker cela dans un format plus compact - pour supprimer la duplication massive des noms de balises / attributs. Peut-être aussi pour donner des attributs aux types natifs, ainsi, par exemple, les données à virgule flottante sont stockées sous forme de 4 octets par flottant, pas sous forme de chaîne de texte.

Google / Wikipedia révèlent que le «XML binaire» n'est guère un nouveau problème - il a déjà été résolu un certain nombre de fois. Quelqu'un ici a-t-il de l'expérience avec l'un des systèmes / normes existants? - sont-ils idéaux pour les jeux - avec une bibliothèque d'analyseur / chargeur (C / C ++) gratuite, légère et multiplateforme disponible?

Ou devrais-je réinventer cette roue moi-même?

Ou est-ce que je ferais mieux d'oublier l'idéal et de simplement compresser mes données brutes .xml (elles devraient bien s'emballer avec une compression de type zip) et de simplement prendre la mémoire / performance en charge?

bluescrn
la source
1
XML peut être très bien compressé en utilisant gzip et al .
ThiefMaster

Réponses:

18

Nous avons beaucoup utilisé le XML binaire pour Superman Returns: The Videogame . Nous parlons de milliers et de milliers de fichiers. Cela a bien fonctionné, mais honnêtement, cela ne semblait pas valoir la peine. Il a consommé une fraction notable de notre temps de chargement, et la «flexibilité» de XML n'a pas évolué. Après un certain temps, nos fichiers de données avaient trop d'identifiants étranges, de références externes qui devaient être synchronisées et d'autres exigences étranges pour qu'ils puissent vraiment être édités par l'homme.

De plus, XML est vraiment un format de balisage et non un format de données. Il est optimisé pour beaucoup de texte avec des balises occasionnelles. Ce n'est pas idéal pour des données entièrement structurées. Ce n'était pas mon appel, mais s'il l'avait été et que je savais alors ce que je sais maintenant, j'aurais probablement fait JSON ou YAML. Ils sont tous deux suffisamment concis pour ne pas nécessiter de compactage et sont optimisés pour représenter les données , pas le texte .

munificent
la source
1
Il existe une version binaire de JSON appelée BSON .
Philipp
12

Stockez et modifiez vos niveaux en XML normal, mais laissez votre moteur de jeu le faire paresseusement en XML binaire pendant le chargement, et enregistrez le XML binaire sur le disque afin qu'il puisse le charger la prochaine fois (si le XML brut n'a pas changé) .

Quelque chose comme ça:

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

De cette façon, vous obtenez le meilleur des deux mondes. À la sortie, il vous suffit de vous assurer que tous les fichiers binaires sont là.

Peter Alexander
la source
5

Les tampons de protocole Google semblent être la voie à suivre, mais je ne les ai pas utilisés moi-même.
http://code.google.com/p/protobuf/

Vous définissez un fichier .proto qui décrit le format de fichier:

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

Il est ensuite compilé avec un outil en ligne de commande qui génère des classes C / C ++ pour écrire et analyser des fichiers de données binaires dans le format de données défini précédemment. Il existe également quelques extensions pour différents langages de programmation.

L'inconvénient de ProtocolBuffer est qu'ils ne sont pas au format texte brut. Vous auriez besoin d'un outil pour les générer, les lire et les modifier. Mais cela ne devrait pas être un problème si vous ne les utilisez que pour échanger des données entre votre éditeur de jeu et votre jeu. Je ne l'utiliserais pas pour définir des fichiers de configuration;)

La compression des fichiers xml bruts devrait également fonctionner. Quel type de jeu faites-vous? S'il est basé sur le niveau, vous ne devez charger toutes les ressources nécessaires qu'une seule fois lorsque le niveau est chargé.

mise à jour: il existe plusieurs projets pour d'autres langages tels que C # pour travailler avec ProtocolBuffers:
http://code.google.com/p/protobuf/wiki/ThirdPartyAddOns

Stephen
la source
Un sérialiseur n'est-il pas adapté à ce genre de problème? Je suppose que non, mais je ne vois pas de différence claire. Mais cette réponse me semble appropriée. Mais aussi tar / gzip les fichiers xml réduiront considérablement leur taille (car c'est du texte, mais je suppose que cela fonctionnera également pour xml), ce qui pourrait être la solution "la plus facile". Quoi qu'il en soit, XML est un langage simple, mais il est très coûteux en termes d'analyse / d'utilisation de la mémoire: lorsque vous utilisez XML, vous devez lire / écrire le moins de fois possible.
jokoon
C'est une option intéressante, mais ressemble plus à une alternative complète à l'utilisation de XML n'importe où dans le pipeline. Pour être honnête, je ne serais pas très enthousiaste à propos du code généré, cependant - et une autre complication est que j'utilise C # pour les outils (je suis heureux que les outils continuent à travailler avec les gros fichiers .XML ). Un convertisseur XML-> PB peut être une option, bien que je pense que je suis toujours à la recherche de quelque chose qui soit plus «XML binaire à usage général», plutôt que des moyens de cuire des «données de niveau binaire» spécifiques (même si ce serait un peu plus efficace)
bluescrn
"J'utilise C # pour les outils", il y a plusieurs projets pour c #. mis à jour ma réponse.
Stephen
@bluescrn, je ne serais pas trop inquiet pour le code généré. Google offre un support de première classe pour C ++, Java et Python. Ils l'utilisent largement en interne; le code généré est assez robuste. Un gros avantage avec PB, c'est votre programme d'outils par rapport à un .protofichier, ce qui élimine presque les problèmes de mauvaise communication. Les protos sont beaucoup plus faciles à lire / maintenir qu'un schéma xml, si vous avez même la discipline (et le temps) pour utiliser les schémas xml.
deft_code
4

Et le format JSON?

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

Sven
la source
Il semble légèrement plus compact que XML, mais présente toujours le principal problème des noms d'attributs dupliqués. Si le fichier contenait une liste d'objets de jeu avec les attributs 'XPosition', 'YPosition' et 'Scale', les chaînes 'XPosition' / 'YPosition' / 'Scale' seraient dupliquées pour chaque objet de jeu. C'est la chose principale que je vise à 'comprimer' pour le moment
bluescrn
1
@bluescrn: Non, il n'a pas ce problème. Les objets sont une structure; vous pouvez également utiliser des tableaux [qui, simplement, ressemblent à ceci]. Cela signifie que vous pouvez vous retrouver avec quelque chose comme ça pour stocker les noms et les propriétés des voitures: "cars":{"ford":[8C,FA,BC,2A,384FFFFF],"holden":[00,00,04,FF,04FF54A9]}vous pouvez même omettre l'identifiant "cars" et aller directement dans un tableau si vous savez où se trouvera le champ cars. Vous pouvez même omettre les noms « gué » et « holden » si vous n'avez pas besoin d'enregistrer ces données, vous laissant avec: [...,[[8C,FA,BC,2A,384FFFFF],[00,00,04,FF,04FF54A9]]]. Est-ce que ça devient plus compact?
doppelgreener
1
@Axidos: Si vous voulez rendre le balisage illisible et non structuré, vous pourriez tout aussi bien le rendre binaire. En plus de cela, c'est une fausse économie, à moins que vous n'analysiez des données non compressées pendant l'exécution (dans ce cas, vous êtes probablement vissé de toute façon), ou en quelque sorte contraint pour quelques centaines d'octets de mémoire de chaîne pendant l'analyse (sauf si vous êtes sur un micro-ondes, vous n'êtes pas).
@Joe: bluescrn semble rechercher un format lisible qui n'a pas de noms en double. J'illustrais la capacité de JSON à offrir exactement cela. Je suis tout à fait d'accord cependant qu'à un certain point, vous pourriez tout aussi bien vous demander pourquoi vous vous embêtez même avec un balisage comme celui-ci.
doppelgreener
4

Utilisez JSON.

(S'appuyant sur la réponse de Munificent, et en grande partie en réponse à vos préoccupations exprimées ailleurs)

Vous avez mentionné que JSON a le problème de gaspiller des éléments de nommage d'espace, comme XML. Ce n'est pas le cas.

JSON est construit sur deux structures: les paires nom / valeur ( objets ) et les listes ordonnées de valeurs ( tableaux ). XML est construit uniquement sur des paires nom / valeur.

Si vous pensez que JSON repose sur des objets que vous avez lus, JSON est conçu pour être auto-descriptif et lisible par l'homme, comme ceci (en utilisant des paires de chiffres octaux pour représenter des octets uniques):

{
    "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}
    ]
}

Cependant, vous avez également la possibilité de l'écrire comme ceci, tant que vous savez où tout sera (et que vous pouvez donc rechercher l'index 4, plutôt que l'objet "voitures", pour obtenir votre liste de voitures):

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

Est - il obtenir plus concis que d' avoir simplement [, ], ,et vos valeurs?

Eh bien, c'est le cas si vous êtes prêt à vous rapprocher de plus en plus d'un flux binaire pur.

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

Ne vous tirez pas une balle dans la jambe en optimisant trop.

doppelgreener
la source
2

Je sais que vous avez accepté une réponse, mais Google à la fois "Fast Infoset" (XML binaire) et vtd-xml.

Bien que ce dernier (VTD) ne puisse pas résoudre l'aspect de compression de votre utilisation XML, il peut accélérer considérablement l'accès aux nœuds sur de gros fichiers (il utilise un dictionnaire de décalages binaires pour accéder aux nœuds et ne crée pas d' objets pour chaque nœud. , travaillez plutôt sur la chaîne XML d'origine). Par conséquent, sa recherche XML est [dit être] à la fois plus rapide et ne nécessite pas autant de mémoire en cours pour accéder / manipuler le document XML.

Les deux ci-dessus ont des liaisons dans les langages populaires (qui incluent C #).

À votre santé

Riches

Big Rich
la source
1

Vous pouvez essayer la Karvonite . C'est censé être agile. C'est un framework de persistance qui s'adaptera assez bien aux changements de vos données (ce qui est bien par rapport à la gestion binaire de vous-même). Je ne sais pas vraiment comment les données sont structurées, mais les fichiers sont beaucoup plus petits que les fichiers gonflés xml. (Je suppose qu'il enregistre les données dans un format binaire au lieu de texte comme xml)

Le seul inconvénient auquel je peux penser est que si vos données sont corrompues ou si elles sont gâchées d'une manière que Karvonite n'aime pas, votre type est à la merci de ses créateurs à moins que vous ne compreniez comment la structure du les données fonctionnent.

La façon dont vous spécifiez comment enregistrer / charger vos données est d'ouvrir simplement leur éditeur de persistance, d'importer votre assemblage avec tous les objets de données et de cocher certaines cases pour afficher les objets que vous souhaitez prendre en charge et les champs / propriétés à enregistrer.

Cela pourrait valoir le coup d'essayer. Depuis que vous utilisez C #, cela correspond parfaitement à votre langage car il fonctionne avec XNA (Windows, Xbox360 et Windows Phone 7 qui, je pense, vous intéressent depuis que vous avez mentionné l'iPhone?).

Edit: Je viens de remarquer que vous n'utilisez que C # pour les outils. Cela ne s'intégrerait probablement pas très bien dans votre flux de travail. Pour une raison quelconque, j'avais XNA dans ma tête.

Michael Coleman
la source