Votre jeu contient des données - images-objets, effets sonores, musique, texte - et vous devez les stocker d'une manière ou d'une autre. Parfois, vous pouvez tout encapsuler dans un seul SWF
, .Unity3d
ou EXE
fichier, mais dans certains cas cela ne conviendra pas. Dans ce tutoriel technique, nous allons utiliser des fichiers binaires personnalisés à cette fin..
Remarque: Ce tutoriel suppose que vous maîtrisiez les bits et les octets. Découvrez une introduction aux opérations binaires, hexadécimales et autres et une compréhension des opérateurs au niveau des bits sur Activetuts + si vous avez besoin de la réviser.!
L'utilisation de formats de fichiers binaires personnalisés présente quelques avantages et inconvénients..
Créer quelque chose comme un conteneur de ressources (comme ce didacticiel le fera) réduira les collisions disque / serveur et facilitera généralement le chargement des ressources car il n’est pas nécessaire de charger plusieurs fichiers. Les formats de fichiers personnalisés peuvent également ajouter une couche de sécurité supplémentaire sous forme d’obscurcissement aux ressources du jeu..
D'un autre côté, vous devrez réellement générer les fichiers personnalisés d'une manière ou d'une autre avant de pouvoir les utiliser dans un jeu, mais ce n'est pas aussi difficile que cela puisse paraître - surtout si vous ou une personne de votre connaissance pouvez créer quelque chose comme un fichier. Fichier JAR pouvant être déposé dans un processus de construction avec une relative facilité.
Avant de pouvoir commencer à concevoir vos propres formats de fichier binaire, vous devez comprendre les types de données primitifs (blocs de construction) disponibles. Le nombre de types de données primitifs est en réalité illimité, mais il existe un ensemble commun que la plupart des programmeurs connaissent et utilisent, et ces types de données représentent généralement des multiples de 8 bits..
Comme vous pouvez le constater, ces types de données primitifs fournissent une large gamme de valeurs entières et vous les trouverez au cœur de la plupart des spécifications de fichiers binaires. Il existe quelques types de données plus primitifs, tels que les nombres à virgule flottante, mais les types de données entiers répertoriés ci-dessus sont plus que suffisants pour cette introduction et pour la plupart des formats de fichiers binaires..
Les types de données structurés (ou types complexes) représentent des éléments spécifiques (morceaux) d'un fichier binaire et se composent de types de données primitifs ou d'autres types de données structurés..
Vous pouvez considérer les types de données structurés comme des objets ou des instances de classe dans un langage de programmation, chaque objet déclarant un ensemble de propriétés. Les types de données structurés peuvent être visualisés en utilisant la notation objet simple.
Voici un exemple d'en-tête de fichier fictif:
HEADER signature U24 version U8 longueur U32
Donc, ici, le type de données structuré est nommé ENTÊTE
et il a trois propriétés étiquetées Signature
, version
et longueur
. Chaque propriété de cet exemple est déclarée en tant que type de données primitif, mais les propriétés peuvent également être déclarées en tant que types de données structurés..
Si vous êtes un programmeur, vous commencez probablement à comprendre à quel point il serait facile de représenter un fichier binaire dans un langage de programmation basé sur la POO; Jetons un coup d'oeil à la façon dont cela ENTÊTE
type de données pourrait être représenté en Java:
class Header public int signature; // U24 public int version; // U8 public long length; // U32
A ce stade, vous devez vous familiariser avec les bases des structures de fichiers binaires. Le moment est donc venu d'examiner le processus de conception d'un format de fichier personnalisé fonctionnel. Ce format de fichier sera conçu pour contenir une collection de ressources de jeu comprenant des images et des sons..
La première chose à concevoir est une structure de données pour l’en-tête du fichier afin que celui-ci puisse être identifié avant que le reste du fichier ne soit chargé en mémoire. Idéalement, l'en-tête du fichier devrait au moins contenir un champ de signature et un champ de version:
HEADER signature U24 version U8
La signature de fichier que vous choisissez d'utiliser dépend de vous: il peut s'agir d'un nombre d'octets quelconque, mais la plupart des formats de fichier ont une signature lisible par l'homme contenant trois ou quatre caractères ASCII. Pour mes besoins, le Signature
Ce champ contiendra les codes de caractères de trois caractères ASCII (un octet par caractère) et représentera la chaîne "RES" (abréviation de "RESOURCE"), ainsi les valeurs d'octet seront 0x52
, 0x45
et 0x53
.
le version
domaine sera initialement 0x01
car c'est la version 1 du format de fichier.
Le fichier de ressources lui-même est en réalité un type de données structuré qui contient un en-tête et contiendra plus tard d'autres éléments. Cela ressemble actuellement à ceci:
FILE header HEADER
La prochaine chose que nous allons examiner est la structure de données pour les images.
Le fichier de ressources stockera un tableau de valeurs de couleur ARGB (une par pixel) et permettra de compresser éventuellement ces données à l'aide de l'algorithme ZLIB. Les dimensions de l'image devront également être incluses dans le fichier avec un identifiant pour l'image (pour que vous puissiez accéder à l'image après son chargement en mémoire):
IMAGE id STRING largeur U16 hauteur U16 compressé U8 longueur de données U32 donnée U8 [longueur de données]
Il y a quelques éléments de cette structure qui nécessitent votre attention; le premier est le U8 [longueur de données]
une partie de la structure et la seconde est la CHAÎNE
structure de données utilisée pour la identifiant
, qui n'a pas été défini dans la table des types de données ci-dessus.
Le premier est la notation de tableau de base - cela signifie simplement que dataLength
nombre de U8
les valeurs doivent être lues à partir du fichier. le Les données
Ce champ contient les pixels de l’image et le comprimé
champ indique si le la Les données
le champ est compressé. Si la comprimé
La valeur est 0x01
puis le Les données
le champ est compressé en ZLIB, sinon le décodeur de fichier peut assumer la Les données
le champ n'est pas compressé. L’avantage de l’utilisation de la compression ZLIB est la IMAGE
la structure du fichier finira par avoir une taille similaire à celle d'une version encodée en PNG de l'image.
le CHAÎNE
la structure de données est la suivante:
STRING dataLength U16 data U8 [dataLength]
Pour ce format de fichier, toutes les chaînes seront codées au format UTF-8 et les octets de la chaîne codée seront situés dans le répertoire. Les données
domaine de la CHAÎNE
Structure de données. le dataLength
champ indique le nombre d'octets dans le Les données
champ.
La structure du fichier de ressources ressemble maintenant à ceci:
FILE header HEADER imageCount U16 imageList IMAGE [imageCount]
Comme vous pouvez le voir, le fichier contient maintenant un en-tête, un nouveau imageCount
champ qui indique le nombre d’images dans le fichier et un nouveau imageList
champ pour les images. Cela en soi serait un format de fichier utile pour stocker plusieurs images, mais il serait encore plus utile s'il contenait plusieurs types de ressources. Nous allons maintenant examiner l'ajout de sons au fichier..
Les sons seront stockés dans le fichier de la même manière que les images, mais au lieu de stocker des valeurs de couleur de pixel brutes, le fichier stockera des échantillons de son brut dans différentes résolutions en bits:
SOUND id STRING dataFormat U8 dataLength U32 // échantillons de 8 bits if (dataFormat == 0x00) data U8 [longueur de données] // échantillons de 16 bits if (dataFormat == 0x01) data U16 [longueur de données] // Échantillons 32 bits si (dataFormat == 0x02) data U32 [dataLength]
Oh mon Dieu, déclarations conditionnelles! Parce que le format de données
champ indique le débit binaire du son, le format de la Les données
champ doit être variable, et c’est là que la syntaxe d’instruction conditionnelle simple et conviviale pour les programmeurs entre en jeu.
En regardant la structure de données, vous pouvez facilement voir quel format le Les données
valeurs de champ (échantillons sonores) seront utilisés, étant donné un format de données
valeur. Lors de l'ajout de champs tels que format de données
à une structure de données, les valeurs que ces champs peuvent contenir sont entièrement à vous. Les valeurs 0x01
, 0x02
et 0x03
sont utilisés dans cet exemple simplement parce que ce sont les premières valeurs inutilisées disponibles dans l'octet.
La structure du fichier de ressources ressemble maintenant à ceci:
FILE header HEADER imageCount U16 imageList IMAGE [imageCount] soundCount U16 soundList SOUND [soundCount]
Les données génériques constituent la dernière chose ajoutée à cette structure de fichier de ressources. cela permettra à diverses données relatives au jeu (dans divers formats) d'être stockées dans le fichier.
Comme le IMAGE
structure de données cette nouvelle LES DONNÉES
structure supportera la compression ZLIB facultative car les données textuelles telles que JSON et XML bénéficient généralement de la compression, ce qui obscurcira également les données contenues dans le fichier:
DATA id STRING compressé U8 dataFormat U8 dataLength U32 data U8 [dataLength]
le comprimé
champ indique si le Les données
le champ est compressé: une valeur de 0x01
signifie le Les données
le champ est compressé ZLIB.
le format de données
indique le format des données et que les valeurs que ce champ peut contenir sont à vous. Vous pouvez par exemple utiliser 0x00
pour le texte brut, 0x01
pour XML et 0x02
pour JSON. Un seul octet non signé (U8
) peut tenir 256
différentes valeurs et cela devrait être plus que suffisant pour tous les différents formats de données que vous voudrez peut-être utiliser dans un jeu.
La structure finale du fichier de ressources ressemble à ceci:
FILE header HEADER imageCount U16 imageList IMAGE [imageCount] soundCount U16 soundList SOUND [soundCount] dataCount U16 dataList DATA [dataCount]
En ce qui concerne les formats de fichiers, celui-ci est relativement simple - mais il est fonctionnel et montre comment les formats de fichiers peuvent être représentés et structurés de manière raisonnable et compréhensible..
Il y a encore une chose importante à savoir sur les fichiers binaires: les valeurs multi-octets stockées dans des fichiers binaires peuvent utiliser l'un des deux ordres d'octet (appelé aussi "endian"). L'ordre d'octet peut être LSB (octet le moins significatif en premier, ou "little-endian") ou MSB (octet le plus significatif en premier, ou "big-endian"). La différence entre les ordres de deux octets est simplement l'ordre dans lequel les octets sont stockés.
Par exemple, une valeur de couleur RVB 24 bits comprend trois octets, un octet pour chaque canal de couleur. L'ordre des octets d'un fichier détermine si ces octets sont stockés dans le fichier au format RGB (big-endian) ou BGR (little-endian)..
De nombreux langages de programmation modernes fournissent une API qui vous permet de changer l'ordre des octets lorsque vous lisez un fichier en mémoire. La lecture de valeurs multi-octets à partir d'un fichier binaire n'est donc généralement pas une préoccupation pour les programmeurs. Cependant, si vous lisez un fichier octet par octet, vous devez connaître la séquence d'octets du fichier..
Le code Java suivant montre comment lire une valeur 24 bits (dans ce cas une couleur RVB) à partir d'un fichier en tenant compte de l'ordre des octets du fichier:
bigEndian boolean = true; int readU24 (entrée InputStream) lève IOException int value = 0; if (bigEndian) valeur | = input.read () << 16; // red value |= input.read() << 8; // green value |= input.read() << 0; // blue else // little endian value |= input.read() << 0; // blue value |= input.read() << 8; // green value |= input.read() << 16; // red return value;
La lecture et l'écriture de fichiers binaires sortent du cadre de ce didacticiel, mais dans cet exemple, vous devriez pouvoir voir comment l'ordre des trois octets d'une valeur de 24 bits est inversé en fonction de l'ordre un fichier. Y a-t-il des avantages à utiliser un ordre d'octet au lieu de l'autre? Eh bien, pas vraiment - les ordres d’octets ne concernent que le matériel, pas les logiciels.
C’est à vous de décider où vous allez, mais j’espère que ce tutoriel a rendu les formats de fichiers personnalisés un peu moins effrayants à envisager dans vos propres jeux.!