Den här artikeln är en överblick över hur man skapar ett JRPG (japanskt rollspel), till exempel de tidiga Final Fantasy-spelen. Vi tittar på arkitekturen och systemen som utgör skelettet för en JRPG, hur man hanterar spellägen, hur man använder flikar för att visa världen och hur man kodar ett RPG-kampsystem.
Notera: Denna artikel är skriven med ett Java-liknande pseudokodsspråk, men koncepten är tillämpliga på alla spelutvecklingsmiljöer.
1983 flyttade Yuji Horii, Koichi Nakamura och Yukinobu Chida till Amerika och deltog i AppleFest '83, en samling utvecklare som visade sina senaste skapelser för Apple II. De blåses bort med den senaste versionen av en RPG som heter Wizardry.
När de återvände till Japan bestämde de sig för att skapa Dragon Warrior, en RPG som var liknande men strömlinjeformad för NES. Det var en enorm hit, som definierade JRPG-genren. Dragon Warrior gick inte lika bra i Amerika, men några år senare gjorde ett annat spel.
År 1987 släpptes den ursprungliga Final Fantasy, gawning en av de bästsäljande spelautomaterna på jorden som blev, åtminstone i väst, den ikoniska JRPG.
Spelgenrer definieras aldrig exakt - de är mer en fuzzy samling av konventioner. RPGs tenderar att ha ett nivelleringssystem, en eller flera spelare karaktärer med färdigheter och statistik, vapen och rustning, kamp och utforskningslägen och starka berättelser. spelframsteg uppnås ofta genom att flytta över en karta.
Japanska RPG är RPG skapade i form av Dragon Warrior; De är mer linjära, striden är ofta vridbaserad och det finns oftast två typer av kartor: en världskarta och en lokal karta. Arketypiska JRPG: er inkluderar Dragon Warrior, Final Fantasy, Wild Arms, Phantasy Star och Chrono Trigger. Den typ av JRPG vi ska prata om i den här artikeln är en som liknar en tidig Final Fantasy.
Spel som Final Fantasy VI och Chrono Trigger är fortfarande mycket roliga att spela. Om du gör en JRPG lär du dig ett tidlöst spelformat som moderna spelare fortfarande är mycket mottagliga för. De skapar en bra ram för att lägga till egen twist och experiment - var det i berättelsen, presentationen eller mekaniken. Det är en bra sak om du kan göra ett spel som fortfarande spelas och haft årtionden efter det att det släpptes första gången!
Call of Duty, en av världens mest populära FPS-spel, använder RPG-element; den sociala spelboom som omger FarmVille var i grunden en klon av SNES RPG Harvest Moon; och till och med tävlingsspel som Gran Turismo har nivåer och erfarenheter.
Så mycket som en författare kan skrämmas av ett tomt pappersark kan en spelutvecklare bli förlamad av det stora antalet möjliga val när man utformar ett nytt spel. Med en JRPG har många val bestämts för dig, så du har inte det valet för valet, du är fri att följa konventionerna för de flesta beslut och avvika från konventionen på de punkter som är viktiga för dig.
Final Fantasy var nästan helt kodad av en enda programmerare, Nasir Gebelli, och han gjorde det i montering! Med moderna verktyg och språk är det mycket lättare att skapa denna typ av spel. Den största delen av de flesta RPG är inte programmeringen det är innehållet - men det behöver inte vara fallet för ditt spel. Om du ringer lite tillbaka på innehållet och fokuserar på kvalitet över kvantitet är en JRPG ett bra soloprojekt.
Att ha ett lag kan hjälpa till med vilket spel som helst, och du kanske vill outsourca konst och musik eller använda några av de utmärkta kreativa commons tillgångarna från platser som opengameart.org. (Redaktörens anteckning: Vår systersida GraphicRiver säljer också spritark.)
JRPGs har ett särskilt efterföljande och ett antal indie-JRPG (som de som visas nedan) har fungerat bra kommersiellt och finns tillgängliga på plattformar som Steam.
JRPG: er delar så många konventioner och mekaniker att det är möjligt att bryta en typisk JRPG ner till ett antal system:
I mjukvaruutveckling ses ett mönster om och om igen: skiktning. Det här hänvisar till hur system i ett program bygger på varandra, med brett tillämpliga lager längst ner och lager som mer noggrant hanterar problem vid handen nära toppen. JRPGs är inte annorlunda och kan ses som ett antal lager - lägre skikt handlar om grundläggande grafiska funktioner och övre skikt handlar om uppdrag och karaktärsstatistik.
Tips: När du utvecklar ett nytt system är det bäst att börja med att skapa bottenlagren först och sedan flytta skikt för lager till toppen. Med hjälp av middleware kan du hoppa över flera av de lägre lagren som är gemensamma för många spel. På arkitekturdiagrammet ovan hanteras alla lager under den prickade linjen av en 2D-spelmotor.Som du kan se från arkitekturdiagrammet ovan finns det många system som utgör en JRPG men de flesta system kan grupperas som separata lägen av spelet. JRPG har mycket olika spellägen; De har en världskarta, lokal karta, kampläge och flera menylägen. Dessa lägen är nästan helt separata, fristående kodstycken, vilket gör att var och en enkelt kan utvecklas.
Modes är viktiga men de skulle vara värdelösa utan spelinnehåll. En RPG innehåller många kartfiler, monsterdefinitioner, dialogrutor, skript för att köra cutscenes och gameplay-kod för att styra hur spelaren fortskrider. Att täcka hur man bygger en JRPG i detalj skulle fylla en hel bok, så vi kommer att koncentrera oss på några av de viktigaste delarna. Att hantera spellägena rent är avgörande för att producera en hanterbar JRPG, så det är det första systemet vi ska utforska.
Bilden nedan visar spelet slinga pumpning bort, kräver en uppdatering funktion varje ram. Detta är hjärtslaget i spelet och nästan alla spel är uppbyggda på detta sätt.
Har du någonsin startat ett projekt men stallat eftersom du fann det för svårt att lägga till nya funktioner eller plågades av mystiska buggar? Kanske försökte du klämma in hela koden i uppdateringsfunktionen med liten struktur och hittade koden blev en kryptisk röra. En utmärkt lösning på dessa typer av problem är att skilja koden ut i olika spel tillstånd, vilket ger en mycket tydligare bild av vad som händer.
Ett gemensamt gamedevverktyg är statsmaskin; den används överallt för att hantera animeringar, menyer, spelflöde, AI ... det är ett viktigt verktyg att ha i vårt kit. För JRPG kan vi använda en statlig maskin för att hantera olika spellägen. Vi tar en titt på en vanlig statlig maskin och sedan blandar vi det lite, så att det blir mer lämpligt för JRPG. Men först tar vi lite tid att överväga det allmänna spelflödet som visas nedan.
I en typisk JRPG startar du förmodligen i det lokala kartspelet, fritt att vandra runt en stad och interagera med sina invånare. Från staden kan du lämna - här kommer du att ange ett annat spelläge och se världskartan.
Världskartan fungerar väldigt mycket som den lokala kartan, men i större skala; Du kan se berg och städer, i stället för träd och staket. På världskartan om du går tillbaka till staden återgår läget till den lokala kartan.
I antingen världskartan eller den lokala kartan kan du ta fram en meny för att kolla in dina karaktärer, och ibland på världskartan blir du kastad i strid. Diagrammet ovan beskriver dessa spellägen och övergångar; Detta är det grundläggande flödet av JRPG-spel och det är vad vi ska skapa våra spelstilar från.
En statlig maskin är i vårt syfte en del kod som rymmer alla olika lägen i våra spel, vilket gör det möjligt för oss att flytta från ett läge till ett annat, och det uppdaterar och gör vad det nuvarande läget är.
Beroende på implementeringsspråket består en statlig maskin vanligen av a Statsmaskin
klass och ett gränssnitt, Jag påstår
, som alla stater genomför.
En statlig maskin beskrivs bäst genom att skissera ett grundläggande system i pseudokod:
klass StateMachine KartamStates = ny karta (); IState mCurrentState = EmptyState; public void Update (float elapsedTime) mCurrentState.Update (elapsedTime); public void Render () mCurrentState.Render (); public void Change (String stateName, optional var params) mCurrentState.OnExit (); mCurrentState = mStates [stateName]; mCurrentState.OnEnter (params); public void Add (Stringnamn, IState-tillstånd) mStates [name] = state;
Denna kod ovan visar en enkel tillståndsmaskin utan felkontroll.
Låt oss titta på hur ovanstående maskinkod används i ett spel. I början av spelet a Statsmaskin
kommer att skapas, alla olika tillstånd i spelet läggs till och den ursprungliga tillståndssatsen. Varje stat identifieras unikt av a Sträng
namn som används när man ringer till ändringsstatusfunktionen. Det finns bara någonsin ett nuvarande tillstånd, mCurrentState
, och det görs och uppdateras varje spelslinga.
Koden kan se ut så här:
StateMachine gGameMode = new StateMachine (); // En stat för varje spelläge gGameMode.Add ("huvudmeny", nya MainMenuState (gGameMode)); gGameMode.Add ("localmap", nya LocalMapState (gGameMode)); gGameMode.Add ("världskarta", nya WorldMapState (gGameMode)); gGameMode.Add ("battle", nya BattleState (gGameMode)); gGameMode.Add ("ingamemenu", nya InGameMenuState (gGameMode)); gGameMode.Change ( "Huvudmeny"); // Main Game Update Loop public void Uppdatering () float elapsedTime = GetElapsedFrameTime (); gGameMode.Update (ElapsedTime); gGameMode.Render ();
I exemplet skapar vi alla tillstånd som krävs, lägger till dem i Statsmaskin
och ställ in starttillståndet till huvudmenyn. Om vi sprang den här koden MainMenuState
skulle göras och uppdateras först. Detta representerar den meny du ser i de flesta spel när du först startar upp, med alternativ som Starta spelet och Lastspel.
När en användare väljer Starta spelet, de MainMenuState
kallar något liknande gGameMode.Change ("localmap", "map_001")
och den LocalMapState
blir det nya nuvarande tillståndet. Detta tillstånd skulle då uppdatera och göra kartan, så att spelaren kan börja utforska spelet.
Diagrammet nedan visar en visualisering av en statlig maskin som rör sig mellan WorldMapState
och BattleState
. I ett spel skulle detta vara likvärdigt med en spelare som vandrade runt om i världen, attackerades av monster, gick i kampläge och återvände sedan till kartan.
Låt oss snabbt titta på tillståndsgränssnittet och en EmptyState
klass som implementerar det:
offentligt gränssnitt IState public virtual void Update (float elapsedTime); public virtual void Render (); allmän virtuell ogiltig OnEnter (); allmän virtuell tomgång OnExit (); public EmptyState: IState public void Update (float elapsedTime) // Inget att uppdatera i tomt tillstånd. public void Render () // Inget att göra i det tomma tillståndet public void OnEnter () // Ingen åtgärd att ta när staten är inloggad public void OnExit () // Ingen åtgärd att ta när staten är avslutad
Gränssnittet Jag påstår
kräver att varje stat har fyra metoder innan den kan användas som ett tillstånd i statens maskin: Uppdatering()
, Göra()
, OnEnter ()
och OnExit ()
.
Uppdatering()
och Göra()
kallas varje ram för det nuvarande aktiva tillståndet; OnEnter ()
och OnExit ()
kallas när du byter tillstånd. Bortsett från det är det allt ganska enkelt. Nu vet du att du kan skapa alla typer av stater för alla olika delar av ditt spel.
Det är den grundläggande tillståndsmaskinen. Det är användbart för många situationer men när man hanterar spellägen kan vi förbättra det! Med det nuvarande systemet kan byt tillstånd ha mycket överhuvudtaget - ibland vid byte till a BattleState
vi vill lämna Världs
, springa slaget och återvänd sedan till Världs
i den exakta inställningen var det före slaget. Denna typ av operation kan vara klumpig med hjälp av den standardmaskinsmaskin vi har beskrivit. En bättre lösning skulle vara att använda a stack av stater.
Vi kan växla upp standardstandardmaskinen till en stapel stater, vilket visas i diagrammet nedan. Till exempel, MainMenuState
trycks på stacken först, i början av spelet. När vi börjar ett nytt spel, LocalMapState
pressas ovanpå det. Vid denna tidpunkt MainMenuState
är inte längre gjord eller uppdaterad men väntar, redo för oss att återvända till.
Därefter, om vi börjar en kamp, BattleState
skjuts på toppen; när slaget slutar slår det av stapeln och vi kan återuppta på kartan exakt var vi slutade. Om vi dör i spelet då LocalMapState
är poppad och vi återvänder till MainMenuState
.
Diagrammet nedan ger en visualisering av en statsstack, som visar InGameMenuState
pressas på stapeln och sedan poppas av.
Nu har vi en uppfattning om hur stacken fungerar, låt oss titta på någon kod för att implementera den:
offentlig klass StateStack KartamStates = ny karta (); Lista mStack = Lista (); public void Update (float elapsedTime) IState top = mStack.Top () top.Update (elapsedTime) public void Render () IState top = mStack.Top () top.Render () public void Push IState state = mStates [namn]; mStack.Push (stat); offentliga IState Pop () returnera mStack.Pop ();
Denna ovanstående statsstapelkod har ingen felkontroll och är ganska okomplicerad. Stater kan skjutas på stapeln med hjälp av Tryck()
ringa och poppade av med a Pop()
ring, och staten högst upp i stapeln är den som uppdateras och återges.
Att använda en stapelbaserad metod är bra för menyer, och med lite modifiering kan den också användas för dialogrutor och meddelanden. Om du känner dig äventyrlig kan du kombinera båda och ha en statlig maskin som också stöder staplar.
Använder sig av Statsmaskin
, StateStack
, eller någon kombination av de två skapar en utmärkt struktur för att bygga din RPG på.
MenuMenuState
och GameState
arv från Jag påstår
.
Kartor beskriver världen; öknar, rymdskepp och djungler kan alla representeras med hjälp av en tillemap. En tillemap är ett sätt att använda ett begränsat antal små bilder för att bygga upp en större. Diagrammet nedan visar hur det fungerar:
Ovanstående diagram har tre delar: kakelpaletten, en visualisering av hur kretskortet är konstruerat och den slutliga kartan som återges till skärmen.
Kakelplattan är en samling av alla plattor som används för att skapa en karta. Varje kakel i paletten identifieras unikt med ett heltal. Till exempel är kakel nummer 1 gräs; Lägg märke till de platser där den används vid tilemap-visualisering.
En tillemap är bara en rad siffror, varje nummer som hänför sig till en kakel i paletten. Om vi ville göra en karta full av gräs kunde vi bara ha ett stort sortiment fyllt med nummer 1, och när vi gjordes dessa plattor såg vi en gräskarta som gjordes av många små gräsplattor. Kakelplattan är vanligtvis laddad som en stor konsistens som innehåller många mindre kakel men varje ingång i paletten kan lika lätt vara sin egen grafiska fil.
Tips: Varför inte använda en array av arrayer för att representera tillemap? Den första matrisen kan representera en rad rader av plattor.Anledningen till att vi inte gör det här är bara för enkelhet och effektivitet. Om du har en rad heltal, det är ett kontinuerligt block av minne. Om du har en array av arrayer är det ett block av minne för den första arrayen som innehåller pekare, med varje pekare som pekar på en rad kakel. Denna indirektion kan sakta ner sakerna - och eftersom vi ritar kartan varje ram, desto snabbare desto bättre!
Låt oss titta på någon kod för att beskriva en kakel karta:
// // Tar en texturkarta över flera plattor och bryter upp den i // enskilda bilder på 32 x 32. // Den slutliga matrisen kommer att se ut: // gTilePalette [1] = Bild // Vår första gräsplatta // gTilePalette [2] = Bild // Andra gräsplattform variant // ... // gTilePalette [15] = Bild // Rock och gräsplatta // 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, 13, 5, 11, 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, 5, 11, 11, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,];
Jämför ovanstående kod med diagrammet och det är helt klart hur en kartong byggs upp från en liten serie kakel. När en karta beskrivs så här kan vi skriva en enkel renderfunktion för att rita den på skärmen. De exakta detaljerna för funktionen ändras beroende på inställningarna för visningsport och ritning. Vår renderfunktion visas nedan.
statisk int TilePixelSize = 32; // Ritar en tillemap från det övre vänstra, vid pixelpositionen x, y // x, y - pixelpositionen som kartan kommer att göras från // karta - kartan för att göra // bredd - bredden på kartan i plattor public void RenderMap (int x, int y, Array map, int mapWidth) // Börja med att indexera den övre vänstra flertalet int tileColumn = 1; int tileRow = 1; för (int i = 1; map.Count (); i ++) // Minus 1 så att den första plattan drar vid 0, 0 int pixelPosX = x + (tileColumn - 1) * TilePixelSize; int pixelPosY = y + (tileRow - 1) * TilePixelSize; RenderImage (x, y, gTilePalette [gMap1Layer1 [i]]); // Förflyttning till nästa kakelkolumn + = 1; om (tileColumn> mapWidth) tileColumn = 1; kakel Row + = 1; - Hur det används i huvuduppdateringsslingan public void Update () // Egentligen rita en karta på skärmen RenderMap (0, 0, gMap1Layer1, gMap1Width)
Kartan som vi har använt hittills är ganska grundläggande; de flesta JRPG-skivor kommer att använda flera lager av flikar för att skapa mer intressanta scener. Diagrammet nedan visar vår första karta, med tre fler lager lagt till den, vilket resulterar i en mycket mer tilltalande karta.
Som vi såg tidigare, är varje tillemap bara en rad olika tal och därför kan en fullskiktad karta göras från en uppsättning av dessa arrays. Självklart är rendering av tillemap egentligen bara det första steget i att lägga till utforskning av ditt spel; kartor måste också ha information om kollision, stöd för rörliga enheter runt och grundläggande interaktivitet med triggers.
En utlösare är ett kodstycke som bara avfyras när spelaren "triggar" den genom att utföra en viss åtgärd. Det finns många åtgärder som en trigger kan känna igen. Om du exempelvis flyttar spelarens tecken till en kakel kan det utlösa en åtgärd - det händer vanligtvis när du flyttar till en dörröppning, teleporter eller kanten på kanten. Utlösare kan placeras på dessa plattor för att teleportera tecknet till en inomhuskarta, världskarta eller relaterad lokal karta.
En annan utlösare kan bero på att "använd" -knappen trycks in. Till exempel, om spelaren går upp till ett tecken och trycker på "använd", slås en avtryckare av och en dialogruta visas som visar teckens text. Utlösare används överallt för att hjälpa till att sticka kartor tillsammans och ge interaktivitet.
JRPG har ofta mycket detaljerade och komplicerade kartor, så jag rekommenderar att du inte försöker göra dem för hand, det är en mycket bättre idé att använda en tillemap-editor. Du kan använda en av de fantastiska fria befintliga lösningarna eller rulla din egen. Om du vill prova ett befintligt verktyg rekommenderar jag definitivt att du checkar ut Tiled vilket är verktyget jag brukade skapa dessa exempel kartor.
relaterade inläggSlutligen vidare till striderna! Vad bra är en JRPG utan kamp? Bekämpning är där många spel väljer att uppfinna, introducera nya kompetenssystem, ny stridsstruktur eller olika stavningssystem - det finns en hel del variationer.
De flesta stridsystem använder en vändbaserad struktur med endast en stridsflygare tillåtet att vidta en åtgärd i taget. De allra första vridbaserade stridsystemen var enkla, med varje enhet som en tur i ordning: spelarens tur, fiendens tur, spelarens tur, fiendens tur och så vidare. Detta gav snabbt sätt till mer invecklade system som erbjuder mer utrymme för taktik och strategi.
Vi ska titta på Active-Time baserade stridsystem, där stridsflygare inte nödvändigtvis får lika många varv. Snabbare enheter kan få fler varv och typen av åtgärder som påverkas påverkar också hur lång tid en tur tar. Till exempel kan en krigare som slashar med en dolk ta 20 sekunder, men en trollkarl som hämtar ett monster kan ta två minuter.
Ovanstående skärmdump visar kampläget i en typisk JRPG. Spelarstyrda tecken finns till höger, fiende-tecken till vänster, och en textruta längst ner visar information om kampanterna.
I början av striden läggs monster och spelare sprites till scenen och då är det ett beslut om vilken ordning enheterna tar sina turar. Det här beslutet kan delvis bero på hur kampen lanserades: om spelaren blev överbelastad så kommer alla monster att attackera först, annars är det vanligtvis baserat på en av enhetens statistik som hastighet.
Allt som spelaren eller monster gör är en åtgärd: Attacking är en handling, med hjälp av magi är en handling, även bestämmer vilken åtgärd att ta nästa är en åtgärd! Ordningsföljden är bäst spårad med en kö. Åtgärden längst upp är den åtgärd som kommer att äga rum nästa, om ingen snabbare åtgärd förutsätter det. Varje åtgärd kommer att ha en nedräkning som minskar när varje ram passerar.
Kampflödet styrs med hjälp av en statlig maskin med två tillstånd; ett tillstånd att kryssa åtgärden och ett annat tillstånd att genomföra den bästa åtgärden när tiden kommer. Som alltid är det bästa sättet att förstå något att titta på koden. Följande exempel implementerar ett grundläggande stridstillstånd med en åtgärdskö:
klass BattleState: IState ListmActions = Lista (); Lista mEntities = Lista (); StateMachine mBattleStates = new StateMachine (); public static bool SortByTime (Action a, Action b) return a.TimeRemaining ()> b.TimeRemaining () offentliga BattleState () mBattleStates.Add ("tick", nya BattleTick (mBattleStates, mActions)); mBattleStates.Add ("execute", nya BattleExecute (mBattleStates, mActions)); public void OnEnter (var params) mBattleStates.Change ("tick"); // // Få en beslutsåtgärd för varje enhet i åtgärdskön // Sortera så att de snabbaste åtgärderna är de bästa // mEntities = params.entities; foreach (Entity e in mEntities) om (e.playerControlled) PlayerDecide action = ny PlayerDecide (e, e.Speed ()); mActions.Add (åtgärder); annars AIDecide action = nytt AIDecide (e, e.Speed ()); mActions.Add (åtgärder); Sortera (mActions, BattleState :: SortByTime); public void Update (float elapsedTime) mBattleStates.Update (elapsedTime); public void Render () // Rita scenen, gui, karaktärer, animationer etc mBattleState.Render (); offentligt ogiltigt OnExit ()
Koden ovan visar kontrollen av stridsläget genom att använda en enkel tillståndsmaskin och en kö av åtgärder. Till att börja med har alla enheter som är involverade i slaget en besluta-action läggas till i kön.
En beslutsverkan för spelaren kommer att ge en meny med RPG-stabila alternativ Ge sig på, Magi, och Artikel; När spelaren bestämmer sig för en åtgärd tas beslutet bort från köen och den nyvalda åtgärden läggs till.
En avgörande åtgärd för AI kommer att inspektera scenen och bestämma vad man ska göra nästa (med hjälp av något som ett beteende träd, beslutsträd eller liknande teknik) och då kommer det också att avlägsna beslutsbeslutet och lägga till sin nya åtgärd i kön.
De BattleTick
klassen kontrollerar uppdateringen av åtgärderna, som visas nedan:
klass BattleTick: IState StateMachine mStateMachine; ListamActions; offentlig BattleTick (StateMachine stateMachine, List handlingar): mStateMachine (stateMachine), mActions (action) // Saker kan hända i dessa funktioner men ingenting vi är intresserade av. public void OnEnter () public void OnExit () public void Render public void Update (float elapsedTime) foreach (Åtgärd a i mActions) a.Update (elapsedTime); om (mActions.Top (). IsReady ()) Action top = mActions.Pop (); mStateMachine: Change ("execute", topp);
BattleTick
är en delstat i BattleMode-tillståndet och det är bara tikar tills toppaktionens nedräkning är noll. Det dyker då upp den övre åtgärden ur köen och ändras till Kör stat.
Diagrammet ovan visar en åtgärdskö i början av en kamp. Ingen har ännu tagit en åtgärd och alla är beordrade efter sin tid för att fatta ett beslut.
The Giant Plant har en nedräkning av 0, så på nästa kryssrutan exekveras det AIDecide
verkan. I detta fall AIDecide
Åtgärder resulterar i att monsteret bestämmer sig för att attackera. Anfallsåtgärden är nästan omedelbar och läggs tillbaka i kön som den andra åtgärden.
Vid nästa iteration av BattleTick
, spelaren kommer att få välja vilken åtgärd hans dvärg "Mark" ska ta, vilket kommer att ändra köen igen. Nästa iteration av BattleTick
Efter det kommer växten att attackera en av dvärgarna. Anfallsåtgärden kommer att tas bort från kön och skickas över till BattleExecute
state, och det kommer att animera växtattackerna samt göra alla nödvändiga stridsberäkningar.
När monsterets attack har avslutats en annan AIDecide
åtgärd kommer att läggas till köen för monsteret. De BattleState
kommer att fortsätta så långt fram till kampens slut.
Om någon enhet dör under striden måste alla handlingar avlägsnas från köen - vi vill inte att döda monster plötsligt reanimerar och attackerar under spelet (såvida vi inte avsiktligt gör zombies eller någon form av odöd!).
Åtgärdskön och enkel tillståndsmaskin är hjärtat i stridsystemet och du borde nu ha en bra känsla för hur det passar ihop. Det är inte tillräckligt för att vara en fristående lösning, men den kan användas som en mall för att bygga något mer fungerande och invecklat. Åtgärder och stater är bra abstraktion som hjälper till att hantera komplexiteten i strid och göra det lättare att expandera och utveckla.
BattleExecute
stat.BattleMenuState
och AnimationState
.BattleOver
tillstånd som visar loot och XP gain.Vi har haft en hög nivå på hur man gör en JRPG, dykning i några av de mer intressanta detaljerna. Vi har täckt hur man strukturerar kod med en statlig maskin eller stapel, hur man använder kakel och lager för att visa vår värld, och hur man styr strömmen av strid med hjälp av en åtgärdskö och en statlig maskin. Funktionerna som vi har täckt gör en bra bas för att bygga vidare på och utvecklas från.
Men det finns också mycket som inte har täckts alls. Att göra en fullständig JRPG inkluderar XP och nivelleringssystem, sparar och laddar spelet, massor av GUI-kod för menyerna, grundläggande animationer och specialeffekter, tillstånd för hantering av klippscener, kampmekanik (som sömn, posion, elementarbonus och motstånd) , för att bara nämna några saker!
Du behöver inte alla dessa saker för ett spel. Till Månen hade i princip endast kartprospektering och dialog. Du kan stegvis lägga till nya funktioner när du gör ditt spel.
Den svåraste delen av att göra något spel är att klara det, så börja små, tänk på en mini-rpg; fly en fängelsehålan, en enda hämtningsuppdrag och bygg sedan upp. Om du upptäcker att du fastnar, reducera spelets omfattning, gör det enklare och avsluta det. Du kan hitta som du utvecklar dig och får massor av nya och spännande idéer, vilket är bra, skriv ner dem men motstå strävan att öka spelets omfattning, eller än värre starta en ny.
Att göra en JRPG är svårt; det finns många system, och utan en bra karta kan det vara svårt att veta vilka som ska hanteras först. En nog