Denne artikkelen er et høyt overblikk for å lage et JRPG (japansk rollespill), for eksempel de tidlige Final Fantasy-spillene. Vi ser på arkitekturen og systemene som utgjør skjelettet til et JRPG, hvordan man styrer spillmoduser, hvordan man bruker tilemaps til å vise verden, og hvordan man kodes et RPG kampsystem.
Merk: Denne artikkelen er skrevet med et Java-lignende pseudokodekspråk, men konseptene gjelder for ethvert spillutviklingsmiljø.
I 1983 fløy Yuji Horii, Koichi Nakamura og Yukinobu Chida til Amerika og deltok i AppleFest '83, en samling av utviklere som viste frem sine nyeste kreasjoner for Apple II. De ble blåst bort av den nyeste versjonen av en RPG som heter Wizardry.
På retur til Japan bestemte de seg for å lage Dragon Warrior, en RPG som var lik, men strømlinjeformet for NES. Det var en massiv hit, som definerte JRPG-sjangeren. Dragon Warrior gikk ikke så bra i Amerika, men et par år senere gjorde et annet spill.
I 1987 ble den opprinnelige Final Fantasy løslatt, gyting en av de bestselgende videospillfranchiseene på jorden som ble, i det minste i Vesten, den ikoniske JRPG.
Spill sjangere er aldri nøyaktig definert - de er mer en fuzzy samling av konvensjoner. RPGer har en tendens til å ha et nivelleringssystem, en eller flere spillerkarakterer med ferdigheter og statistikk, våpen og rustning, kamp- og utforskningsmoduser og sterke fortellinger; spillet fremgang er ofte oppnådd ved å fremme over et kart.
Japanske RPG er RPGer opprettet i formen av Dragon Warrior; De er mer lineære, kamp er ofte svingbasert, og det er vanligvis to typer kart: et verdenskart og et lokalt kart. Arketypiske JRPGs inkluderer Dragon Warrior, Final Fantasy, Wild Arms, Phantasy Star og Chrono Trigger. Den type JRPG vi skal snakke om i denne artikkelen er en som ligner på en tidlig Final Fantasy.
Spill som Final Fantasy VI og Chrono Trigger er fortsatt veldig hyggelig å spille. Hvis du lager en JRPG, lærer du et tidløst spillformat som moderne spillere fortsatt er veldig mottakelige for. De gir et flott rammeverk for å legge til egen vri og eksperimentere - være det i fortellingen, presentasjonen eller mekanikken. Det er en flott ting hvis du kan lage et spill som fortsatt spilles og nytes tiår etter at det er første utgivelsen!
Call of Duty, en av verdens mest populære FPS-spill, bruker RPG-elementer; den sosiale spillboomen som omgir FarmVille var i utgangspunktet en klone av SNES RPG Harvest Moon; og selv racing spill som Gran Turismo har nivåer og erfaring.
Mye som en forfatter kan bli skremt av et blankt ark, kan en spillutvikler bli lammet av det store antallet mulige valg når man designer et nytt spill. Med en JRPG er mange av valgene blitt bestemt for deg, så du har ikke det valget lammelse, du er fri til å følge konvensjonene for de fleste avgjørelser og avvike fra konvensjonen til de punktene som har betydning for deg.
Final Fantasy ble nesten helt kodet av en enkelt programmerer, Nasir Gebelli, og han gjorde det i samling! Med moderne verktøy og språk er det langt lettere å lage denne typen spill. Den største delen av de fleste RPG er ikke programmeringen, det er innholdet - men dette trenger ikke å være tilfelle for spillet ditt. Hvis du kaller det litt tilbake på innholdet og fokuserer på kvalitet over kvantitet, er en JRPG et flott soloprosjekt.
Å ha et lag kan hjelpe deg med ethvert spill, og du vil kanskje outsource kunst og musikk, eller bruke noen av de gode kreative commons-eiendelene fra steder som opengameart.org. (Redaktørens notat: Vår søsterside GraphicRiver selger også sprite ark.)
JRPGs har en dedikert følge, og en rekke indie JRPGs (som de som er vist nedenfor) har gjort godt kommersielt og er tilgjengelige på plattformer som Steam.
JRPGs deler så mange konvensjoner og mekanikk at det er mulig å bryte en typisk JRPG ned i en rekke systemer:
I programvareutvikling ses ett mønster igjen og igjen: lagdeling. Dette refererer til hvordan systemene i et program bygger oppå hverandre, med bredt anvendbare lag i bunnen og lagene som er mer intimt å håndtere problem ved hånden nær toppen. JRPGer er ikke forskjellige og kan sees som en rekke lag - lavere lag omhandler grunnleggende grafiske funksjoner og øvre lag omhandler oppdrag og karakterstatistikk.
Tips: Når du utvikler et nytt system, er det best å begynne med å lage de nederste lagene først og deretter flytte lag for lag til toppen. Ved hjelp av mellomvare hjelper du å hoppe over flere av de lavere lagene som er felles for mange spill. På arkitekturdiagrammet ovenfor håndteres alle lagene under den stiplede linjen av en 2D-spillmotor.Som du kan se fra arkitekturdiagrammet ovenfor, er det mange systemer som utgjør en JRPG, men de fleste systemer kan grupperes sammen som separate moduser av spillet. JRPG har svært forskjellige spillmoduser; de har et verdens kart, lokal kart, kampmodus og flere meny moduser. Disse modusene er nesten helt adskilte, selvstændige stykker kode, noe som gjør hver enkelt enkel å utvikle.
Moduser er viktige, men de ville være ubrukelige uten spillinnhold. En RPG inneholder mange kartfiler, monsterdefinisjoner, dialogruter, skript for å kjøre cutscenes og gameplay-kode for å kontrollere hvordan spilleren utvikler seg. Å dekke hvordan du bygger en JRPG i detalj vil fylle en hel bok, så vi skal konsentrere oss om noen av de viktigste delene. Håndtering av spillmodusene er avgjørende for å produsere en håndterbar JRPG, så det er det første systemet vi skal utforske.
Bildet under viser spillsløyfen pumpe bort, og kaller en oppdateringsfunksjon hver ramme. Dette er hjertet i spillet, og nesten alle spillene er strukturert på denne måten.
Har du noen gang startet et prosjekt, men stanset fordi du fant det for vanskelig å legge til nye funksjoner eller ble plaget av mystiske feil? Kanskje du prøvde å kramme all koden inn i oppdateringsfunksjonen med liten struktur og funnet at koden ble et kryptisk rot. En utmerket løsning på disse typer problemer er å skille koden ut i forskjellige spill stater, gir et mye tydeligere bilde av hva som skjer.
Et vanlig gamedev verktøy er state maskin; den brukes overalt, for å håndtere animasjoner, menyer, spillflyt, AI ... det er et viktig verktøy å ha i vårt sett. For JRPG kan vi bruke en statlig maskin for å håndtere de forskjellige spillmodusene. Vi tar en titt på en vanlig statlig maskin, og så blander vi det litt opp, for å gjøre det mer egnet for JRPG. Men først la oss ta litt tid til å vurdere generell spillflyt som vist nedenfor.
I en typisk JRPG vil du sannsynligvis starte i den lokale kartspillmodusen, gratis å vandre rundt i en by og samhandle med innbyggerne. Fra byen kan du gå - her kommer du til en annen spillmodus og se verdenskartet.
Verdenskartet virker veldig mye som det lokale kartet, men i større målestokk; Du kan se fjell og byer, i stedet for trær og gjerder. Mens du er på verdenskartet hvis du går tilbake til byen, går modusen tilbake til det lokale kartet.
I enten verdenskartet eller det lokale kartet kan du få opp en meny for å sjekke ut tegnene dine, og noen ganger på verdenskartet blir du kastet i kamp. Diagrammet ovenfor beskriver disse spillmodi og overganger; Dette er den grunnleggende strømmen av JRPG-spill og er hva vi skal skape våre spillstatninger fra.
En statlig maskin, til vårt formål, er et stykke kode som inneholder alle de forskjellige modiene i våre spill, som gjør det mulig for oss å flytte fra en modus til en annen, og som oppdaterer og gjør det uansett hvilken modus som nå er.
Avhengig av implementeringsspråket består en statlig maskin vanligvis av a State
klasse og et grensesnitt, iState
, at alle stater implementerer.
En statlig maskin beskrives best ved å skissere et grunnleggende system i pseudokode:
klasse StateMachine MapmStates = ny kart (); IState mCurrentState = EmptyState; offentlig ugyldig oppdatering (float elapsedTime) mCurrentState.Update (elapsedTime); offentlig ugyldig Render () mCurrentState.Render (); offentlig ugyldig endring (String stateName, valgfri var params) mCurrentState.OnExit (); mCurrentState = mStates [stateName]; mCurrentState.OnEnter (parametere); offentlig tomrom Legg til (Strenge navn, IState tilstand) mStates [name] = state;
Denne koden ovenfor viser en enkel tilstandsmaskin uten feilkontroll.
La oss se på hvordan ovennevnte tilstandsmaskinkode brukes i et spill. Ved starten av spillet a State
vil bli opprettet, alle de forskjellige tilstandene i spillet lagt til og det opprinnelige statssettet. Hver stat er unikt identifisert av a string
navn som brukes når du ringer til endringsstatusfunksjonen. Det er bare en nåværende tilstand, mCurrentState
, og det gjengis og oppdateres hver spillsløyfe.
Koden kan se slik ut:
StateMachine gGameMode = new StateMachine (); // En tilstand for hver spillmodus gGameMode.Add ("hovedmeny", nye MainMenuState (gGameMode)); gGameMode.Add ("localmap", nye LocalMapState (gGameMode)); gGameMode.Add ("worldmap", ny WorldMapState (gGameMode)); gGameMode.Add ("kamp", nytt BattleState (gGameMode)); gGameMode.Add ("ingamemenu", ny InGameMenuState (gGameMode)); gGameMode.Change ( "MainMenu"); // Main Game Update Loop offentlig ugyldig oppdatering () float elapsedTime = GetElapsedFrameTime (); gGameMode.Update (elapsedTime); gGameMode.Render ();
I eksempelet oppretter vi alle statene som kreves, legger dem til State
og sett starttilstanden til hovedmenyen. Hvis vi kjørte denne koden på MainMenuState
vil bli gjengitt og oppdatert først. Dette representerer menyen du ser i de fleste spill når du først starter opp, med alternativer som Start spill og Last Spill.
Når en bruker velger Start spill, de MainMenuState
kaller noe som gGameMode.Change ("localmap", "map_001")
og LocalMapState
blir den nye nåværende tilstanden. Denne tilstanden vil da oppdatere og gjengi kartet, slik at spilleren kan begynne å utforske spillet.
Diagrammet nedenfor viser en visualisering av en statlig maskin som beveger seg mellom WorldMapState
og BattleState
. I et spill vil dette være lik en spiller som vandrer rundt i verden, blir angrepet av monstre, går inn i kampmodus, og deretter tilbake til kartet.
La oss få en rask titt på det aktuelle grensesnittet og en EmptyState
klasse som implementerer det:
offentlig grensesnitt IState public virtual void Update (float elapsedTime); offentlig virtuell ugyldig Render (); offentlig virtuell ugyldig OnEnter (); offentlig virtuell ugyldig OnExit (); offentlig EmptyState: IState public void Update (float elapsedTime) // Ingenting å oppdatere i tom tilstand. offentlig ugyldig Render () // Ingenting å gjengi i tom tilstand offentlig ugyldig OnEnter () // Ingen handling å ta når staten er oppgitt offentlig ugyldig OnExit () // Ingen handling å ta når staten er avsluttet
Grensesnittet iState
krever at hver stat har fire metoder før den kan brukes som en stat i staten maskinen: Oppdater()
, Render ()
, OnEnter ()
og OnExit ()
.
Oppdater()
og Render ()
blir kalt hver ramme for den nåværende aktive tilstanden; OnEnter ()
og OnExit ()
kalles når du endrer tilstand. Bortsett fra det er det ganske grei. Nå vet du dette, du kan lage alle slags stater for alle de forskjellige delene av spillet ditt.
Det er den grunnleggende tilstandsmaskinen. Det er nyttig for mange situasjoner, men når det gjelder spillmoduser, kan vi forbedre det! Med dagens system kan endringstilstanden ha mye overhead - noen ganger når du bytter til en BattleState
vi vil forlate WorldState
, Kjør kampen, og kom tilbake til WorldState
i det nøyaktige oppsettet var det før kampen. Denne typen operasjon kan være klumpete ved å bruke standard state-maskinen vi har beskrevet. En bedre løsning ville være å bruke en stable av stater.
Vi kan bytte opp standard state maskinen til en stabel med stater, som vist diagrammet nedenfor. For eksempel, MainMenuState
blir presset på stakken først ved starten av spillet. Når vi starter et nytt spill, LocalMapState
blir presset på toppen av det. På dette punktet MainMenuState
er ikke lenger gjengitt eller oppdatert, men venter, klar for at vi skal komme tilbake til.
Deretter, hvis vi starter et slag, BattleState
skyves på toppen; når slaget slutter, er det poppet av stabelen, og vi kan fortsette på kartet akkurat der vi sluttet. Hvis vi dør i spillet da LocalMapState
er poppet av og vi kommer tilbake til MainMenuState
.
Diagrammet nedenfor gir en visualisering av en statsstabel, som viser InGameMenuState
blir presset på stakken og deretter poppet av.
Nå har vi en ide om hvordan stakken fungerer, la oss se på noen kode for å implementere den:
offentlig klasse StateStack MapmStates = ny kart (); Liste mStack = Liste (); offentlig tomgang Oppdatering (float elapsedTime) IState top = mStack.Top () top.Update (elapsedTime) offentlig tomrom Render () IState top = mStack.Top () top.Render () offentlig tomrom IState state = mStates [navn]; mStack.Push (tilstand); offentlig IState Pop () return mStack.Pop ();
Denne overstående statsstabelkoden har ingen feilkontroll og er ganske enkel. Stater kan skyves på stakken ved hjelp av Trykk()
ring og poppet av med a Pop ()
ring, og staten på toppen av stakken er den som er oppdatert og gjengitt.
Å bruke en stabelbasert tilnærming er bra for menyer, og med en liten modifikasjon kan den også brukes til dialogbokser og varsler. Hvis du føler deg eventyrlystne, kan du kombinere begge og ha en statlig maskin som også støtter stabler.
Ved hjelp av State
, StateStack
, eller en kombinasjon av de to skaper en utmerket struktur for å bygge din RPG på.
MenuMenuState
og GameState
arve fra iState
.
Kartene beskriver verden; ørkener, romskip og jungler kan alle bli representert ved hjelp av en tilkart. En tilkart er en måte å bruke et begrenset antall små bilder til å bygge opp en større. Diagrammet nedenfor viser hvordan det virker:
Ovennevnte diagram har tre deler: flisepaletten, en visualisering av hvordan skjemaet er konstruert, og det endelige kartet gjengitt til skjermen.
Flispaletten er en samling av alle fliser som brukes til å lage et kart. Hver flis i paletten er unikt identifisert med et heltall. For eksempel er flis nummer 1 gres; Legg merke til stedene der den brukes på tilemap-visualisering.
En tilkart er bare en rekke tall, hvert tall knyttet til en flis i paletten. Hvis vi ønsket å lage et kart fullt av gress, kunne vi bare ha et stort utvalg fylt med nummer 1, og da vi lagde disse flisene, ville vi se et kart av gress som er laget av mange små gressfliser. Flisepaletten er vanligvis lastet som en stor tekstur som inneholder mange mindre fliser, men hver oppføring i paletten kan like enkelt være sin egen grafiske fil.
Tips: Hvorfor ikke bruke en rekke arrays for å representere tilkartet? Den første gruppen kan representere med en rekke rader fliser.Grunnen til at vi ikke gjør dette er bare for enkelhet og effektivitet. Hvis du har en rekke heltal, er det en kontinuerlig blokk med minne. Hvis du har en rekke arrays, er det en blokk med minne for den første gruppen som inneholder pekere, med hver peker peker mot en rekke fliser. Denne indireksjonen kan bremse ting - og siden vi tegner kartet hver ramme, jo raskere jo bedre!
La oss se på noen kode for å beskrive et flisekart:
// // Tar et tekstur kart over flere fliser og bryter det opp i // individuelle bilder på 32 x 32. // Det endelige arrayet vil se ut: // gTilePalette [1] = Image // Vår første gressflis // gTilePalette [2] = Image // Andre gressfliser variant // ... // gTilePalette [15] = Bilde // Rock og gress flis // Array gTilePalette = SliceTexture ("grass_tiles.png", 32, 32) gMap1Width = 10 gMap1Height = 10 Array gMap1Layer1 = Ny Array () [2, 2, 7, 3, 11, 11, 11, 12, 2, 2, 1, 1, 10, 11, 11, 4, 11, 12, 2, 2, 2, 1, 11, 11, 11, 11, 11, 9, 10, 11, 12, 13, 5, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,];
Sammenlign den ovennevnte koden med diagrammet, og det er helt klart hvordan en skjema er bygget opp fra en liten serie fliser. Når et kart er beskrevet som dette, kan vi skrive en enkel gjengivelsesfunksjon for å tegne den på skjermen. De nøyaktige detaljene for funksjonen vil endres avhengig av visningsportoppsett og tegnefunksjoner. Vår gjengivelse er vist nedenfor.
statisk int TilePixelSize = 32; // Tegner en tilkart fra øverst til venstre, ved pikselposisjon x, y // x, y - pikselposisjonen som kartet skal gjengis fra // kart - kartet til gjengivelse // bredde - bredden på kartet i fliser offentlig tomrom RenderMap (int x, int y, Array map, int mapWidth) // Start med å indeksere øverste venstre flis int tileColumn = 1; int tileRow = 1; for (int i = 1; map.Count (); i ++) // Minus 1 slik at den første flisen trekker til 0, 0 int pixelPosX = x + (flisekolonne - 1) * TilePixelSize; int pixelPosY = y + (tileRow - 1) * TilePixelSize; RenderImage (x, y, gTilePalette [gMap1Layer1 [i]]); // Fortsett til neste flisekolonne + = 1; hvis (flisekolonne> mapbredde) tileColumn = 1; fliserRow + = 1; - Hvordan det brukes i hovedoppdateringssløyfen offentlig ugyldig oppdatering () // Egentlig tegne et kart på skjermen RenderMap (0, 0, gMap1Layer1, gMap1Width)
Kartet vi har brukt hittil er ganske grunnleggende; de fleste JRPGs vil bruke flere lag med tilemaps for å skape flere interessante scener. Diagrammet nedenfor viser vårt første kart, med tre lag lagde det til, noe som resulterte i et langt mer behagelig kart.
Som vi så tidligere, er hver tilemap bare en rekke tall, og derfor kan et fullstendig lagdelt kart gjøres fra en rekke av disse arrays. Selvfølgelig er rendering av tilkartet egentlig bare det første skrittet i å legge til leting etter spillet ditt; Kartene må også ha informasjon om kollisjon, støtte for flyttende enheter rundt, og grunnleggende interaktivitet ved hjelp av triggere.
En utløser er et stykke kode som bare avfyres når spilleren "utløser" det ved å utføre en handling. Det er mange handlinger som en utløser kan gjenkjenne. For eksempel kan du flytte spillerens tegn til en flis utløse en handling - dette skjer ofte når du beveger deg på en døråpning, teleporter eller kanten på kart. Utløsere kan plasseres på disse fliser for å teleportere tegnet til et innendørskart, verdens kart eller tilhørende lokalkart.
En annen utløser kan avhenge av at "bruk" -knappen blir trykket. For eksempel, hvis spilleren går opp til et tegn og trykker "bruk", blir en avtrekker avbrutt, og en dialogboks vises som viser teksten til tegnet. Utløsere brukes overalt for å hjelpe sammen med å stifte kart og gi interaktivitet.
JRPG har ofte mange detaljert og komplisert kart, så jeg anbefaler at du ikke prøver å gjøre dem for hånd, det er en mye bedre ide å bruke en tilemap editor. Du kan bruke en av de gode gratis eksisterende løsningene eller rulle dine egne. Hvis du vil prøve et eksisterende verktøy, så anbefaler jeg definitivt å sjekke ut flislagt som er verktøyet jeg pleide å lage disse eksemplarkartene.
Relaterte innleggTil slutt, til kampene! Hvilken god er en JRPG uten kamp? Bekjempelse er hvor mange spill velger å innovere, introdusere nye ferdighetssystemer, ny kampstruktur eller forskjellige staverystemer - det er ganske mye variasjon.
De fleste kampsystemer bruker en turbasert struktur, med bare en kampant tillatt å ta en handling om gangen. De aller første turbaserte kampsystemene var enkle, og hver enhet fikk en sving i rekkefølge: spillerens tur, fiendens tur, spillerens tur, fiendens tur, og så videre. Dette ga raskt vei til mer intrikate systemer som gir mer spillerom for taktikk og strategi.
Vi skal se nærmere på Aktiv-Time baserte kampsystemer, hvor stridsmenn ikke alle får nødvendigvis like mange svinger. Hurtigere enheter kan få flere svinger, og hvilken type handling som tas, påvirker også hvor lenge en sving tar. For eksempel kan en kriger som slashing med en dolk ta 20 sekunder, men en veiviser som samler et monster, kan ta to minutter.
Skjermbildet over viser kampmodusen i en typisk JRPG. Spillerkontrollerte tegn er til høyre, fiende-tegn til venstre, og en tekstboks nederst viser informasjon om kampantene.
I begynnelsen av kampen blir monsteret og spilleren sprites lagt til scenen, og så er det en avgjørelse om hvilken rekkefølge enhetene tar sine svinger. Denne avgjørelsen kan delvis avhenge av hvordan kampen ble lansert: Hvis spilleren ble rammet, vil monstrene alle komme til å angripe først, ellers er det vanligvis basert på en av enhetens statistikk som fart.
Alt spilleren eller monstrene gjør er en handling: Å angripe er en handling, og bruk av magi er en handling, selv bestemme hvilken tiltak du skal ta neste gang er en handling! Ordren av handlinger spores best ved hjelp av en kø. Handlingen øverst er handlingen som vil finne sted neste, med mindre ingen raskere handling foretrekker det. Hver handling vil ha en nedtelling som avtar når hver ramme passerer.
Kampflyten styres ved hjelp av en statlig maskin med to tilstander; en stat for å krysse handlingene og en annen stat for å utføre den øverste handlingen når tiden kommer. Som alltid er den beste måten å forstå noe på å se på koden. Følgende eksempel implementerer en grunnleggende kamptilstand med en handlingskø:
klasse BattleState: IState ListmActions = Liste (); Liste mEntities = Liste (); StateMachine mBattleStates = new StateMachine (); offentlig statisk bool SortByTime (handling a, handling b) return a.TimeRemaining ()> b.TimeRemaining () offentlig BattleState () mBattleStates.Add ("tick", ny BattleTick (mBattleStates, mActions)); mBattleStates.Add ("execute", ny BattleExecute (mBattleStates, mActions)); offentlig ugyldig OnEnter (var params) mBattleStates.Change ("tick"); // // Få en beslutningshandling for hver enhet i handlingen køen // Sorter den så at de raskeste handlingene er toppen // mEntities = params.entities; foreach (Entity e in mEntities) hvis (e.playerControlled) PlayerDecide action = ny PlayerDecide (e, e.Speed ()); mActions.Add (handling); ellers AIDecide action = nytt AIDecide (e, e.Speed ()); mActions.Add (handling); Sorter (mActions, BattleState :: SortByTime); offentlig ugyldig oppdatering (float elapsedTime) mBattleStates.Update (elapsedTime); offentlig tomrom Render () // Tegn scenen, gui, tegn, animasjoner etc mBattleState.Render (); offentlig ugyldig OnExit ()
Koden ovenfor viser kontrollen av kampmodusflyten ved hjelp av en enkel tilstandsmaskin og en kø av handlinger. Til å begynne med har alle enheter involvert i slaget en bestemmer handling lagt til køen.
En avgjørelse for spilleren vil gi opp en meny med RPG stabile alternativer angrep, magi, og Punkt; Når spilleren bestemmer seg for en handling, blir avgjørelsen fjernet fra køen og den nylig valgte handlingen er lagt til.
En beslutningshandling for AI vil inspisere scenen og bestemme hva som skal gjøres neste gang (ved hjelp av noe som et oppførselstreet, beslutningstreet eller lignende teknikk), og da vil det også fjerne besluttsaksjonen og legge til sin nye handling i køen.
De BattleTick
klassen kontrollerer oppdateringen av handlingene, som vist nedenfor:
klasse BattleTick: IState StateMachine mStateMachine; ListemActions; Offentlig BattleTick (StateMachine stateMachine, List handlinger): mStateMachine (stateMachine), mActions (handling) // Det kan skje ting i disse funksjonene, men ingenting vi er interessert i. offentlig ugyldig OnEnter () offentlig ugyldig OnExit () public void Render offentlig ugyldig oppdatering (float elapsedTime) foreach (Action a i mActions) a.Update (elapsedTime); Hvis (mActions.Top () .Ready ()) Action øverst = mActions.Pop (); mStateMachine: Change ("utfør", topp);
BattleTick
er en sub-tilstand i BattleMode-tilstanden, og det er bare ticks til toppaksjonens nedtelling er null. Det popper deretter den øverste handlingen ut av køen og endrer seg til henrette stat.
Diagrammet ovenfor viser en handlingskø i starten av en kamp. Ingen har ennå tatt en handling, og alle er bestilt av deres tid for å ta en avgjørelse.
The Giant Plant har en nedtelling på 0, så på neste kryss det kjører sin AIDecide
handling. I dette tilfellet AIDecide
handling resulterer i at monsteret bestemmer seg for å angripe. Angrepsaksjonen er nesten umiddelbar og blir lagt tilbake i køen som den andre handlingen.
På neste iterasjon av BattleTick
, Spilleren vil få til å velge hvilken handling hans dverg "Mark" skal ta, noe som vil forandre køen igjen. Den neste iterasjonen av BattleTick
Etter dette vil anlegget angripe en av dvergene. Angrepsaksjonen blir fjernet fra køen og overført til BattleExecute
state, og det vil animere anlegget angripende så vel som å gjøre alle nødvendige kamp beregninger.
Når monsterets angrep er ferdig med en annen AIDecide
handling vil bli lagt til køen for monsteret. De BattleState
vil fortsette denne veien til slutten av kampen.
Hvis noen enhet dør under kampen, må alle handlinger bli fjernet fra køen - vi vil ikke at døde monstre plutselig gjenoppretter og angriper under spillet (med mindre vi forsiktig gjør zombier eller en slags undead!).
Handlingen køen og enkel tilstand maskin er hjertet i kampsystemet og du bør nå ha en god følelse for hvordan det passer sammen. Det er ikke komplett nok til å være en frittstående løsning, men den kan brukes som en mal for å bygge noe mer fullt fungerende og intrikat. Tiltak og stater er god abstraksjon som bidrar til å håndtere kompleksiteten i kamp og gjøre det lettere å utvide og utvikle seg.
BattleExecute
stat.BattleMenuState
og AnimationState
.BattleOver
tilstand som viser loot og XP gain.Vi har hatt et høyt nivå på hvordan å lage en JRPG, dykke inn i noen av de mer interessante detaljene. Vi har dekket hvordan du kan strukturere kode ved hjelp av en statlig maskin eller stabel, hvordan du bruker tilemaps og lag for å vise vår verden, og hvordan du styrer strømmen av kamp ved hjelp av en handlingskø og statlig maskin. Funksjonene vi har dekket er en god base for å bygge videre på og utvikle seg fra.
Men det er også mye som ikke har vært dekket i det hele tatt. Å lage en full JRPG inkluderer XP og nivelleringssystemer, lagring og lasting av spillet, masse GUI-koden for menyene, grunnleggende animasjoner og spesialeffekter, tilstander for håndtering av klippekunst, kampmekanikk (som søvn, posion, elementære bonuser og motstander) , for å nevne noen få ting!
Du trenger ikke alle disse tingene for et spill, skjønt; Til Månen hadde i utgangspunktet bare kartutforskning og dialog. Du kan inkrementer legge til nye funksjoner når du lager spillet ditt.
Den vanskeligste delen av å lage et spill er å fullføre det, så start lite, tenk på en mini-rpg; unnslippe et fangehull, en enkelt hente søken, og deretter bygge opp. Hvis du finner at du sitter fast, reduser du omfanget av spillet, gjør det enklere og fullfør det. Du kan finne som du utvikler du får mange nye og spennende ideer, som er bra, skriv dem ned, men motstå trang til å øke omfanget av spillet ditt, eller enda verre, start en ny.
Å lage en JRPG er vanskelig; det er mange systemer