Kodeoptimaliseringer for spillutvikling Grunnleggende strukturer og Mindsets

For mange begynner indie devs, blir koden optimalisering nesten en sekund trodde. Det blir overført til fordel for motorer eller rammer, eller kan betraktes som en mer avansert teknikk utenfor rekkevidde. Imidlertid er det optimaliseringsmetoder som kan brukes på mer grunnleggende måter, slik at koden din kan operere mer effektivt og på flere systemer. La oss ta en titt på noen grunnleggende kodeoptimalisering for å komme i gang.

Optimalisering for spillere og sanitet

Det er ikke uvanlig for indieutviklere å etterligne optimaliseringsmetodene til større selskaper. Det er ikke nødvendigvis en dårlig ting, men å streve for å optimalisere spillet ditt forbi poenget med nyttig avkastning er en god måte å drive deg gal. En smart taktikk for å holde oversikt over hvor effektive optimaliseringene dine er, er å segmentere måldemografikken din og se på hvilke spesifikasjoner maskinene dine har. Benchmarking ditt spill mot datamaskiner og konsoller som dine potensielle spillere bruker, vil bidra til å holde balansen mellom optimalisering og sunnhet.

Grunnleggende kodeoptimaliseringer

Alt som er til side, det er ganske mange optimeringer som nesten alltid kan brukes til å hjelpe spillet ditt til å fungere bedre. De fleste av disse er system-agnostiske (og noen motorer og rammer tar allerede hensyn til dem), så jeg vil inkludere noen pseudokodeeksempler for å få deg på høyre fot. La oss ta en titt.

Minimer objekter på skjermen

Ofte håndteres av motorer, og noen ganger selv GPUer selv, minimerer mengden prosessorkraft som går inn i objektsobjektene, ekstremt viktig. For egne konstruksjoner er det en god ide å begynne å skille objekter i to "lag" i det vesentlige. Den første er dens grafiske representasjon, og den andre er dens data og funksjoner (for eksempel dens plassering). Når et objekt er på skjermen, trenger vi ikke lenger å bruke ressursene som gjengir det, og i stedet bør vi velge sporing. Sporing av ting, som sted og stat, med variabler, reduserer nødvendige ressurser til bare en brøkdel av den opprinnelige kostnaden.

For spill med et stort antall objekter eller objekter som er datatunge, kan det til og med være nyttig å ta det et skritt videre ved å opprette separate oppdateringsrutiner, sette en til å oppdatere mens objektet er på skjermen og det andre for off-screen . Ved å sette opp en mer avansert separasjon på denne måten kan du lagre systemet fra å måtte utføre et antall animasjoner, algoritmer og andre oppdateringer som kan være unødvendige når objektet er skjult.

Her er et pseudokodeeksempel på en objektklasse ved hjelp av flagg og plasseringsbegrensninger:

Objekt NPC Int locationX, locationY; // nåværende posisjon for objektet på et 2d-plan Funksjon drawObject () // en funksjon for å tegne objektet ditt, som skal kalles i skjermoppdateringsløkken // funksjon som kontrollerer om objektet er innenfor den nåværende visningsporten Funksjon pollObjectDraw (array currentViewport [minX, minY, maxX, maxY]) // hvis det er innenfor visningsporten, returner at det kan tegnes Hvis (this.within (currentViewport)) Return true;  Else Return false;  

Selv om dette eksemplet er forenklet, tillater det oss å avgjøre om objektet vil oppstå før du tegner det, slik at vi får en ganske forenklet funksjon i stedet for et fulltrekkssamtale. For å skille funksjoner utover grafiske samtaler, kan det være nødvendig å bruke en ekstra buffer, for eksempel en funksjon som vil inkludere alt en spiller kan se kort, i stedet for hva de nettopp kan se.

Å skille fra rammeoppdateringer

Avhengig av hvilken motor eller ramme som du bruker, kan du ofte ha objekter som oppdateres på hver ramme, eller "tick". Å gjøre det kan beskrive en prosessor ganske raskt, så for å lette den byrden vil vi redusere samtaler på hver ramme når det er mulig.

Det første vi vil skille er funksjonalisering. Disse anropene er vanligvis ressursintensive, så integrering av et anrop som kan fortelle oss når et objekts visuelle egenskaper har endret, kan redusere gjengivelsen drastisk.

For å ta det et skritt videre, kan vi bruke en midlertidig skjerm for våre objekter. Ved å ha gjenstandene direkte tegnet til denne midlertidige beholderen, kan vi sikre at de bare trekkes når det er nødvendig.

I likhet med den første optimaliseringen nevnt ovenfor, introduserer start-iterasjonen av koden vår enkle meningsmåling:

Objekt NPC boolean hasChanged; // sette dette flagget til sant når en endring er gjort til objektet // funksjonen som returnerer om Function pollObjectChanged (retur harChanged (); 

Under hver ramme nå, i stedet for å utføre en rekke funksjoner, kan vi se om det er nødvendig. Selv om denne implementeringen også er enkel, kan den allerede begynne å vise store gevinster i spillets effektivitet, spesielt når det gjelder statiske elementer og sakte oppdateringsobjekter som en HUD.

For å ta dette videre i ditt eget spill, kan det være nyttig å splitte flagget ned i flere, mindre komponenter for segmenteringsfunksjonalitet. For eksempel kan du ha flagg for en dataendring og en grafisk endring skjer separat.

Liveberegninger vs Look Up-verdier

Dette er en optimalisering som har vært i bruk siden de tidlige dagene av spillesystemene. Fastsettelse av avvik mellom liveberegninger og verdioppslag kan bidra til å redusere behandlingstidene drastisk. En kjent bruk i spillets historie lagrer verdiene for trigonometrifunksjoner i tabeller, da det i de fleste tilfeller var mer effektivt å lagre et stort bord og hente det i stedet for å gjøre beregningene i fly og legge ekstra trykk på CPU.

I moderne databehandling trenger vi sjelden å velge mellom lagring av resultater og kjøring av en algoritme. Det er imidlertid fortsatt situasjoner når det gjøres, kan det reduseres ressursene som brukes, noe som gjør det mulig å inkludere andre funksjoner uten å overbelaste et system.

En enkel måte å begynne å implementere dette på er å identifisere vanlige beregninger, eller deler av beregninger, i spillet ditt: Jo større beregningen er, jo bedre. Å utføre gjentatte biter av algoritmer en gang og lagre den kan ofte lagre betydelige mengder prosessorkraft. Selv å isolere disse delene i spesifikke spillløkker kan det bidra til å optimalisere ytelsen.

For eksempel er det i mange top-down shooters ofte store grupper av fiender som utfører de samme oppføringene. Hvis det er 20 fiender, hver beveger seg langs en buet, i stedet for å beregne hver bevegelse individuelt, er det mer effektivt å lagre resultatene av algoritmen i stedet. Å gjøre det gjør at den kan endres basert på hver fiendens startposisjon.

For å finne ut om denne metoden er nyttig for spillet ditt, prøv å bruke benchmarking for å sammenligne forskjellen i ressurser som brukes mellom liveberegning og datalagring.

Utnyttelse av CPU Idleness

Mens dette spiller mer i bruk av sovende ressurser, med forsiktig tanke på objekter og algoritmer, kan vi stable oppgaver på en måte som skaper effektiviteten av koden vår.

For å begynne å bruke følsomhet for tomgang i din egen programvare, må du først skille hvilke oppgaver i spillet ditt som ikke er tidskritiske eller kan beregnes før de trengs. Det første området å lete etter kode som faller inn i denne kategorien er funksjonalitet som er strengt knyttet til spillets atmosfære. Værsystemer som ikke samhandler med geografi, bakgrunnsvisuelle effekter og bakgrunnslyd kan enkelt passe inn i tomgangsberegning.

Utover ting som er strengt atmosfæriske, er garanterte beregninger en annen type beregning som kan plasseres i tomgangsområder. Kunstige intelligensberegninger som skal skje uavhengig av spillerens interaksjon (enten fordi de ikke tar hensyn til spilleren eller at de ikke sannsynligvis krever spillerinteraksjon fra og med), kan gjøres mer effektive, som kan beregnede bevegelser, som f.eks. skripthendelser.

Å skape et system som utnytter ledighet gjør det enda mer enn å tillate høyere effektivitet, det kan brukes til å skalere "øye candy". For eksempel, på en low-end rig, opplever kanskje en spiller bare en vaniljeversjon av spillingen. Hvis systemet vårt oppdager tomgangsrammer, kan vi imidlertid bruke det til å legge til flere partikler, grafiske hendelser og andre atmosfæriske tweaks for å gi spillet litt mer panache.

For å implementere dette, bruk funksjonaliteten som er tilgjengelig i din favorittmotor, rammeverk eller språk for å måle hvor mye CPU som brukes. Angi flagg i koden din som gjør det enkelt å sjekke hvor mye ekstra prosessorkraft er tilgjengelig, og sett deretter opp dine undersystemer for å se på dette flagget og oppføre seg tilsvarende.

Kettingoptimaliseringer sammen

Ved å kombinere disse metodene, er det mulig å gjøre koden betydelig mer effektiv. Med den effektiviteten kommer muligheten til å legge til flere funksjoner, kjøre på flere systemer og sikre en mer solid opplevelse for spillerne.

Har du enkelt å implementere kodeoptimaliseringer du bruker regelmessig? Gi meg beskjed om dem!