Ditt spill har data - sprites, lydeffekter, musikk, tekst - og du må lagre den på en eller annen måte. Noen ganger kan du inkapslere alt til en enkelt SWF
, .unity3d
eller EXE
fil, men i noen tilfeller vil det ikke være egnet. I denne tekniske opplæringen ser vi på bruk av tilpassede binære filer til dette formålet.
Merk: Denne opplæringen antar at du har en grunnleggende forståelse av biter og byte. Sjekk ut en introduksjon til binær, hexadecimal og mer og forstå bitwise operatører på Activetuts + hvis du trenger å revidere!
Det er noen fordeler og ulemper ved å bruke egendefinerte binære filformater.
Å skape noe som en ressursbeholder (som denne opplæringen vil gjøre) vil redusere disk / server thrashing og vil vanligvis gjøre ressursbelastning mye lettere fordi flere filer ikke trenger å lastes. Tilpassede filformater kan også legge til et ekstra lag med sikkerhet i form av obfuscation til spillressurser.
På forsiden må du faktisk generere de egendefinerte filene på en eller annen måte før du kan bruke dem i et spill, men det er ikke så vanskelig som det kan høres - spesielt hvis du eller noen du kjenner, kan skape noe som en JAR-fil som kan slippes inn i en byggprosess med relativ letthet.
Før du kan begynne å designe dine egne binære filformater, trenger du en forståelse av de primitive datatyper (byggeklosser) som er tilgjengelige for deg. Antall primitive datatyper er faktisk ubegrensede, men det er et vanlig sett som de fleste programmerere er kjent med og bruker, og disse datatyper representerer vanligvis flere ganger 8 biter.
Som du kan se, gir disse primitive datatyper et bredt spekter av heltallverdier, og du finner dem som kjernen i de fleste binære filspesifikasjoner. Det er noen få primitive datatyper, for eksempel flytende poengstall, men de totale datatyper som er oppført ovenfor, er mer enn tilstrekkelige for denne introduksjonen og for de fleste binære filformater.
Strukturerte datatyper (eller komplekse datatyper) representerer bestemte elementer (biter) av en binær fil, og de består av primitive datatyper eller andre strukturerte datatyper.
Du kan tenke på strukturerte datatyper som objekter eller klasseksempler i et programmeringsspråk, hvor hvert objekt erklærer et sett med egenskaper. Strukturerte datatyper kan visualiseres ved hjelp av enkel objektnotasjon.
Her er et eksempel på en fiktiv filoverskrift:
HEADER signatur U24 versjon U8 lengde U32
Så her heter den strukturerte datatypen OVERSKRIFT
og den har tre egenskaper merket signatur
, versjon
og lengde
. Hver egenskap i dette eksemplet er erklært som en primitiv datatype, men egenskaper kan også deklareres som strukturerte datatyper.
Hvis du er en programmerer, begynner du sannsynligvis hvor lett det ville være å representere en binær fil i et OOP-basert programmeringsspråk; la oss ta en rask titt på hvordan dette OVERSKRIFT
datatype kan være representert i Java:
Klassehode offentlig int signatur; // U24 public int versjon; // U8 offentlig lang lengde; // U32
På dette punktet bør du være kjent med grunnleggende om binære filstrukturer, så nå er det på tide å se på prosessen med å designe et fungerende tilpasset filformat. Dette filformatet vil bli designet for å holde en samling av spillressurser, inkludert bilder og lyder.
Det første som skal utformes, er en datastruktur for filoverskriften, slik at filen kan identifiseres før resten av filen er lastet inn i minnet. Ideelt sett skal filhodet minst inneholde et signaturfelt og et versjonsfelt:
HEADER signatur U24 versjon U8
Filnavnet du velger å bruke, er opp til deg: det kan være et hvilket som helst antall byter, men de fleste filformater har en menneskelig lesbar signatur som inneholder tre eller fire ASCII-tegn. For mitt formål, signatur
feltet vil inneholde tegnkoder på tre ASCII-tegn (en byte per tegn) og vil representere strengen "RES" (kort for "RESOURCE"), slik at byteverdiene vil bli 0x52
, 0x45
og 0x53
.
De versjon
feltet vil i utgangspunktet være 0x01
fordi dette er Versjon 1 av filformatet.
Resursfilen er faktisk en strukturert datatype som inneholder en header og vil senere inneholde andre elementer. Det ser for øyeblikket ut som dette:
FIL header HEADER
Den neste tingen vil vi se på er datastrukturen for bilder.
Resursfilen lagrer en rekke ARGB-fargeværdier (en per piksel) og tillater at dataene eventuelt komprimeres ved hjelp av ZLIB-algoritmen. Bildedimensjonene må også inkluderes i filen sammen med en identifikator for bildet (slik at bildet kan nås etter at det er lastet inn i minnet):
BILDE id STRING bredde U16 høyde U16 komprimert U8 dataLength U32 data U8 [dataLength]
Det er et par ting i den strukturen som trenger din oppmerksomhet; den første er U8 [dataLength]
En del av strukturen og den andre er STRING
datastruktur brukt til id
, som ikke var definert i tabellen over datatyper ovenfor.
Den tidligere er grunnleggende array notasjon - det betyr bare det dataLength
antall U8
verdier må leses fra filen. De data
feltet inneholder bildepiksler, og komprimert
feltet angir om den data
feltet er komprimert. Hvis komprimert
verdien er 0x01
og så data
feltet er ZLIB komprimert, ellers kan dekoderen påta seg data
feltet er ikke komprimert. Fordelen med å bruke ZLIB komprimering her er BILDE
filstrukturen vil ende opp med å være en tilsvarende størrelse som en PNG-kodet versjon av bildet.
De STRING
datastruktur er som følger:
STRING dataLength U16 data U8 [dataLength]
For dette filformatet vil alle strenger bli kodet som UTF-8 og bytes av den kodede strengen vil bli plassert i data
feltet av STRING
data struktur. De dataLength
feltet angir antall byte i data
felt.
Resursfilstrukturen ser nå slik ut:
FIL header HEADER imageCount U16 bildeListe bilde [imageCount]
Som du kan se filen inneholder nå en header, en ny imageCount
felt som angir antall bilder i filen, og en ny imageList
feltet for bildene. Dette i seg selv ville være et nyttig filformat for å lagre flere bilder, men det ville være enda mer nyttig hvis det hadde flere ressurstyper, så vil det nå se på å legge til lyder på filen.
Lydene lagres i filen på samme måte som bilder, men i stedet for å lagre råpikselfargeværdier, lagrer filen rå lydprøver i varierende bitoppløsninger:
SOUND id STRING dataFormat U8 dataLength U32 // 8-bit samples if (dataFormat == 0x00) data U8 [dataLength] // 16-bit samples if (dataFormat == 0x01) data U16 [dataLength] // 32-biters prøver hvis (dataFormat == 0x02) data U32 [dataLength]
Oh godhet, betingede utsagn! Fordi det dataformat
feltet indikerer bithastigheten til lyden, formatet til data
feltet må være variabelt, og det er der den enkle og programmeringsvenlige, betingede setningssyntaxen kommer inn i spill.
Når du ser på datastrukturen, kan du enkelt se hvilket format den data
feltverdier (lydprøver) vil bli brukt, gitt en bestemt dataformat
verdi. Når du legger til felt som dataformat
til en datastruktur er verdiene de feltene kan inneholde helt opp til deg. Verdiene 0x01
, 0x02
og 0x03
brukes i dette eksemplet, bare fordi de er de første ubrukte verdiene som er tilgjengelige i byten.
Resursfilstrukturen ser nå slik ut:
FIL header HEADER imageCount U16 bildeListe bilde [imageCount] soundCount U16 lydliste SOUND [soundCount]
Den siste tingen som legges til denne ressursfilstrukturen, er generisk data; Dette vil tillate at diverse spillrelaterte data (i forskjellige formater) skal pakkes inn i filen.
Som BILDE
Datastruktur denne nye DATA
strukturen vil støtte valgfri ZLIB-komprimering fordi tekstbaserte data som JSON og XML generelt har nytte av komprimering, og dette vil også forvirre dataene i filen:
DATA id STRING komprimert U8 dataFormat U8 dataLength U32 data U8 [dataLength]
De komprimert
feltet angir om data
feltet er komprimert: en verdi på 0x01
betyr data
feltet er ZLIB komprimert.
De dataformat
Indikerer formatet på dataene, og verdiene dette feltet kan inneholde, er opp til deg. Du kan for eksempel bruke 0x00
for rå tekst, 0x01
for XML og 0x02
for JSON. En enkelt usignert byte (U8
) kan holde 256
forskjellige verdier, og det bør være mer enn nok for alle de forskjellige dataformatene du kanskje vil bruke i et spill.
Den endelige ressursfilstrukturen ser slik ut:
FIL header HEADER imageCount U16 bildeListe bilde [imageCount] soundCount U16 lydliste SOUND [soundCount] dataCount U16 dataList DATA [dataCount]
Så langt som filformater går, er dette relativt enkelt - men det er funksjonelt og det demonstrerer hvordan filformatene kan representeres og struktureres på en fornuftig og forståelig måte.
Det er en viktigere ting som du kanskje trenger å vite om binære filer: multibyteverdier lagret i binære filer kan bruke en av to byteordrer (dette kalles også "endian"). Byteordren kan enten være LSB (minste signifikant byte eller "small endian") eller MSB (mest signifikant byte først eller "big endian"). Forskjellen mellom de to byteordrene er ganske enkelt den rekkefølgen der byte lagres.
For eksempel består en 24-biters RGB-fargeverdi av tre byte, en byte for hver fargekanal. Byte rekkefølge av en fil bestemmer om de byte lagres i filen som RGB (big-endian) eller BGR (liten endian).
Mange moderne programmeringsspråk gir en API som lar deg bytte rekkefølgen mens du leser en fil i minnet, slik at lesing av multibyteverdier fra en binær fil ikke er noe programmerere vanligvis trenger å være bekymret for. Men hvis du leser en byte-by-byte, må du være oppmerksom på filens byteordre.
Følgende Java-kode demonstrerer hvordan du leser en 24-biters verdi (i dette tilfellet en RGB-farge) fra en fil mens du vurderer filens bytebestilling:
bigEndian boolean = true; int readU24 (InputStream input) kaster IOException int verdi = 0; hvis (bigEndian) value | = 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;
Den faktiske lesing og skriving av binære filer er utenfor omfanget av denne opplæringen, men i det eksemplet bør du kunne se hvordan rekkefølgen av de tre byte med en 24-biters verdi vendes avhengig av byte rekkefølgen (endian) av en fil. Er det noen fordeler med å bruke en byteordre i stedet for den andre? Vel, ikke egentlig - byteordrer er bare bekymret for maskinvare ikke programvare.
Hvor du går herfra, er opp til deg, men jeg håper denne opplæringen har gjort tilpassede filformater litt mindre skummelt å vurdere å bruke i dine egne spill!