Prestasjoner er ekstremt populære blant spillere. De kan brukes på flere måter, fra undervisning til måling av fremgang, men hvordan kan vi kode dem? I denne opplæringen vil jeg presentere en enkel tilnærming for å gjennomføre prestasjoner.
Merk: Selv om denne opplæringen er skrevet med AS3 og Flash, bør du kunne bruke de samme teknikkene og konseptene i nesten hvilket som helst spillutviklingsmiljø.
Du kan laste ned eller gaffel den endelige koden fra GitHub repo: https://github.com/Dovyski/Achieve
Ved første øyekast virker programmering av et prestasjonssystem lite trivielt - og det er delvis sant. De er vanligvis implementert med tellere, hver som representerer et viktig spillmåte, som antall fiender drept eller spillerens liv.
En prestasjon er låst opp hvis disse tellerene stemmer overens med bestemte tester:
dreptEnemies = dreptEnemies + 1; hvis (killedEnemies> = 10 && lives> = 2) // unlock oppnåelse
Det er ikke noe galt med denne tilnærmingen, men forestill deg en test med 10 eller flere tellere. Avhengig av antall prestasjoner (og tellere), kan du ende opp med spagetti-kode replikert over alt:
hvis (killedEnemies> 5 && lives> 2 && deaths <= 3 && perfectHits > 20 && ammo> = 100 && wrongHits <= 1) // unlock achievement
Min tilnærming er også basert på tellere, men de styres av enkle regler:
En prestasjon er låst opp når alle relaterte egenskaper er aktive. En prestasjon kan ha en eller flere beslektede egenskaper, hver enkelt styres av en enkelt klasse, så det er ikke nødvendig å skrive hvis()
uttalelser over hele koden.
Her er ideen:
For å kunne gjennomføre en prestasjon må man definere hvilke egenskaper som skal være aktive for å låse opp prestasjonen. Deretter må egenskapene oppdateres i løpet av spillet, og du er ferdig!
De neste avsnittene presenterer en implementering for den ideen.
Det første implementeringstrinnet er representasjonen av eiendommer og prestasjoner. Klassen Eiendom
kan være følgende:
offentlig klasse Eiendom private var mName: String; privat var mValue: int; privat var mActivation: String; private var mActivationValue: int; privat var mInitialValue: int; offentlig funksjon Eiendom (theName: String, theInitialValue: int, theActivation: String, theActivationValue: int) mName = theName; mActivation = theActivation; mActivationValue = theActivationValue; mInitialValue = theInitialValue;
En eiendom har et navn (mName
), en verdi (Lvalue
, som er telleren), en aktiveringsverdi (mActivationValue
) og en aktiveringsregel (mActivation
).
Aktiveringsregelen er noe som "aktiv hvis større enn" og det kontrollerer om en eiendom er aktiv (mer om det senere). En egenskap sies å være aktiv når verdien sin sammenlignes med aktiveringsverdien, og resultatet tilfredsstiller aktiveringsregelen.
En prestasjon kan beskrives som følger:
offentlig klasse Achievement private var mName: String; // prestasjonsnavn privat var mProps: Array; // rekke relaterte egenskaper private var mUnlocked: boolsk; // prestasjon er ulåst eller ikke offentlig funksjon Oppnåelse (theId: String, theRelatedProps: Array) mName = theId; mProps = theRelatedProps; mUnlocked = false;
En prestasjon har et navn (mName
) og et flagg for å indikere om det allerede er låst opp (mUnlocked
). Arrayet mProps
inneholder en oppføring for hver eiendom som trengs for å låse opp prestasjonen. Når alle disse egenskapene er aktive, må prestasjonen låses opp.
Alle eiendommer og prestasjoner vil bli forvaltet av en sentralisert klasse som heter Oppnå
. Denne klassen bør oppføre seg som en svart boks som mottar eiendomsoppdateringer og forteller om en prestasjon var låst opp. Den grunnleggende strukturen er:
offentlig klasse Oppnå // aktiveringsregler offentlig statisk const ACTIVE_IF_GREATER_THAN: String = ">"; offentlig statisk konst ACTIVE_IF_LESS_THAN: String = "<"; public static const ACTIVE_IF_EQUALS_TO :String = "=="; private var mProps :Object; // dictionary of properties private var mAchievements :Object; // dictionary of achievements public function Achieve() mProps = ; mAchievements = ;
Siden alle egenskapene vil bli oppdatert ved å bruke navnet som oppslagsindeksen, er det praktisk å lagre dem i en ordbok ( mProps
attributt i klassen). Resultatene skal håndteres på samme måte, så de lagres på samme måte (mAchievements
Egenskap).
For å håndtere tillegg av egenskaper og prestasjoner, lager vi metodene defineProperty ()
og defineAchievement ()
:
offentlig funksjon defineProperty (theName: String, theInitialValue: int, theAActivationMode: String, theValue: int): void mProps [theName] = ny egenskap (theName, theInitialValue, theAActivationMode, theValue); offentlig funksjon defineAchievement (theName: String, theRelatedProps: Array): void mAchievements [theName] = ny prestasjon (theName, theRelatedProps);
Begge metodene legger bare til en oppføring i egenskapen eller prestasjonsordlisten.
Nå da Oppnå
klassen kan håndtere egenskaper og prestasjoner, det er på tide å gjøre det mulig å oppdatere eiendomsverdier. En eiendom vil bli oppdatert i løpet av spillet, og det vil fungere som en teller. For eksempel eiendommen killedEnemies
bør økes hver gang en fiende blir ødelagt.
Bare to metoder er nødvendige for det: en til å lese og en annen for å sette en eiendomsverdi. Begge metodene tilhører Oppnå
klasse og kan implementeres som følger:
offentlig funksjon getValue (theProp: String): int return mProps [theProp] .value; privat funksjon setValue (theProp: String, theValue: int): void mProps [theProp] .value = theValue;
Det er også nyttig å ha en metode for å legge til en verdi for en gruppe av egenskaper, noe som en batch-økning / reduksjon:
offentlig funksjon addValue (theProps: Array, theValue: int): void for (var i: int = 0; i < theProps.length; i++) var aPropName :String = theProps[i]; setValue(aPropName, getValue(aPropName) + theValue);
Å sjekke om ulåste prestasjoner er enkle og enkle: gjenta over prestasjonsordlisten, kontroller om alle relaterte egenskaper til en prestasjon er aktive.
For å utføre denne iterasjonen trenger vi først en metode for å kontrollere om en eiendom er aktiv:
offentlig klasse Eiendom // // resten av klassekoden ble utelatt ... // offentlig funksjon isActive (): Boolean var aRet: Boolean = false; bytte (mActivation) case Achieve.ACTIVE_IF_GREATER_THAN: aRet = mValue> mActivationValue; gå i stykker; tilfelle Achieve.ACTIVE_IF_LESS_THAN: aRet = mValue < mActivationValue; break; case Achieve.ACTIVE_IF_EQUALS_TO: aRet = mValue == mActivationValue; break; return aRet;
La oss nå implementere checkAchievements ()
metode i Oppnå
klasse:
offentlig funksjon checkAvements (): Vector var aRet: Vector = new Vector (); for (var n: String i mAchievements) var aAchivement: Achievement = mAchievements [n]; hvis (aAchivement.unlocked == false) var aActiveProps: int = 0; for (var p: int = 0; s < aAchivement.props.length; p++) var aProp :Property = mProps[aAchivement.props[p]]; if (aProp.isActive()) aActiveProps++; if (aActiveProps == aAchivement.props.length) aAchivement.unlocked = true; aRet.push(aAchivement); return aRet;
De checkAchievements ()
Metoden detererer over alle prestasjoner. På slutten av hver iterasjon tester det om antall aktive egenskaper for den aktuelle prestasjonen er lik mengden av beslektede egenskaper. Hvis det er sant, er 100% av de tilhørende egenskapene for den prestasjonen aktiv, så spilleren har låst opp en ny prestasjon.
For enkelhets skyld returnerer metoden a Vector
(som virker som et skrivet array eller en liste) som inneholder alle prestasjoner som ble låst opp under sjekken. Metoden markerer også alle prestasjoner funnet som "ulåste", slik at de ikke blir analysert igjen i fremtiden.
Hittil har egenskaper ingen begrensninger, noe som betyr at en verdi går gjennom SetValue ()
vil oppdatere eiendommen. Tenk deg en eiendom som heter killedWithASingleBomb
, som lagrer antall fiender spilleren drept ved hjelp av en enkelt bombe.
Hvis aktiveringsregelen er "hvis større enn 5" og spilleren dreper seks fiender, bør den låse opp prestasjonen. Anta imidlertid checkAchievements ()
Metoden ble ikke påkalt rett etter at bomben eksploderte. Hvis spilleren detonerer en annen bombe og den dreper tre fiender, vil eiendommen bli oppdatert til 3
.
Denne endringen vil føre til at spilleren savner prestasjonen. For å fikse det, kan vi bruke egenskapsaktiveringsregelen som en begrensning. Det betyr at en eiendom med "hvis større enn 5" vil bli oppdatert bare hvis den nye verdien er større enn den nåværende:
privat funksjon setValue (theProp: String, theValue: int): void // Hvilken aktiveringsregel? bytt (mProps [theProp] .activation) case Achieve.ACTIVE_IF_GREATER_THAN: theValue = theValue> mProps [theProp] .value? theValue: mProps [theProp] .value; gå i stykker; tilfelle Achieve.ACTIVE_IF_LESS_THAN: theValue = theValue < mProps[theProp].value ? theValue : mProps[theProp].value; break; mProps[theProp].value = theValue;
Ofte er prestasjoner ikke relatert til hele spillet, men spesifikt for perioder som nivåer. Noe som "slå et nivå som dreper 40 eller flere fiender" må telles under nivået, og nullstilles så spilleren kan prøve igjen på neste nivå.
En mulig løsning på dette problemet er tillegg av tags til eiendommer. Bruken av tagger tillater manipulering av en gruppe av egenskaper. Ved hjelp av det forrige eksempelet, killedEnemies
Eiendommen kan merkes som levelStuff
, for eksempel.
Som en konsekvens er det mulig å sjekke prestasjoner og tilbakestille egenskaper basert på koder:
// Definer egenskapen ved hjelp av en tag defineProperty ("killedEnemies", ..., "levelStuff"); hvis (levelIsOver ()) // Sjekk etter prestasjoner, men bare de som er basert på egenskaper // merket med "levelStuff". Alle andre egenskaper vil bli ignorert, // så det vil ikke forstyrre andre prestasjoner. checkAchievements ( "levelStuff"); // Tilbakestill alle egenskaper merket med 'levelStuff' resetProperties ("levelStuff");
Metoden checkAchievements ()
blir mye mer allsidig med tagger. Den kan påberopes når som helst, så lenge den driver den riktige eiendomsgruppen.
Nedenfor finner du en kodestykke som demonstrerer hvordan du bruker denne gjennomføringen:
var a: Oppnå = ny oppnå (); funksjon initGame (): void a.defineProperty ("killedEnemies", 0, Achieve.ACTIVE_IF_GREATER_THAN, 10, "levelStuff"); a.defineProperty ("lives", 3, Achieve.ACTIVE_IF_EQUALS_TO, 3, "levelStuff"); a.defineProperty ("completedLevels", 0, Achieve.ACTIVE_IF_GREATER_THAN, 5); a.defineProperty ("dødsfall", 0, Oppnå.ACTIVE_IF_EQUALS_TO, 0); a.defineAchievement ("masterKill", ["killedEnemies"]); // Drep 10 + fiender. a.defineAchievement ("cantTouchThis", ["lives"]); // Fullfør et nivå og ikke dø. a.defineAchievement ("nothingElse", ["completedLevels"]); // Beat alle 5 nivåer. a.defineAchievement ("hero", ["completedLevels", "deaths"]); // Beat alle 5 nivåer, ikke dø under prosessen funksjon gameLoop (): void if (enemyWasKilled ()) a.addValue ([killedEnemies »], 1); hvis (playerJustDied ()) a.addValue (["lives"], -1); a.addValue (["deaths"], 1); funksjonsnivåUp (): void a.addValue (["completedLevels"], 1); a.checkAchievements (); // Tilbakestill alle egenskaper merket med 'levelStuff' a.resetProperties ("levelStuff");
Denne opplæringen viste en enkel tilnærming til å gjennomføre prestasjoner i kode. Med fokus på administrerte tellere og koder forsøker ideen å eliminere flere tester spredt over hele koden.
Du kan laste ned eller gaffel koden fra sin GitHub repo: https://github.com/Dovyski/Achieve
Jeg håper denne tilnærmingen hjelper deg med å gjennomføre prestasjoner på en enklere og bedre måte. Takk for at du leser! Ikke glem å holde deg oppdatert ved å følge oss på Twitter, Facebook eller Google+.
Relaterte innlegg