Come costruire un JRPG un primer per gli sviluppatori di giochi

Questo articolo è una panoramica di alto livello per la creazione di un JRPG (gioco di ruolo giapponese) come i primi giochi di Final Fantasy. Analizzeremo l'architettura e i sistemi che costituiscono lo scheletro di un JRPG, come gestire le modalità di gioco, come utilizzare le tilemaps per mostrare il mondo e come codificare un sistema di combattimento RPG.

Nota: Questo articolo è scritto usando un linguaggio pseudo-codice simile a Java, ma i concetti sono applicabili a qualsiasi ambiente di sviluppo di giochi.


Contenuto

  • L'improbabile luogo di nascita dei JRPG
  • The Genre Talk
  • Cinque motivi per cui devi creare un JRPG
  • Architettura
  • Gestione dello stato del gioco
  • Mappe
  • Combattere
  • Revisione


L'improbabile luogo di nascita dei JRPG

La melma - uno dei nemici iconici di Dragon Warrior.

Nel 1983, Yuji Horii, Koichi Nakamura e Yukinobu Chida volarono in America e presero parte all'AppleFest '83, un raduno di sviluppatori che sfoggiavano le loro ultime creazioni per Apple II. Sono stati spazzati via dall'ultima versione di un gioco di ruolo chiamato Wizardry.

Al loro ritorno in Giappone, hanno deciso di creare Dragon Warrior, un gioco di ruolo simile ma ottimizzato per il NES. E 'stato un grande successo, definendo il genere JRPG. Dragon Warrior non andava bene in America, ma pochi anni dopo lo fece un altro gioco.

Nel 1987, fu pubblicato l'originale Final Fantasy, che generò uno dei franchise di videogiochi più venduti sulla Terra che divenne, almeno in Occidente, l'iconico JRPG.



The Genre Talk

I generi di gioco non sono mai definiti con precisione - sono più una collezione di convenzioni confuse. I giochi di ruolo tendono ad avere un sistema di livellamento, uno o più personaggi con abilità e statistiche, armi e armature, modalità di combattimento e esplorazione e narrative forti; il progresso del gioco si ottiene spesso avanzando attraverso una mappa.

I giochi di ruolo giapponesi sono giochi di ruolo creati nello stile di Dragon Warrior; sono più lineari, il combattimento è spesso a turni e di solito ci sono due tipi di mappe: una mappa del mondo e una mappa locale. I JRPG archetipici includono Dragon Warrior, Final Fantasy, Wild Arms, Phantasy Star e Chrono Trigger. Il tipo di JRPG di cui parleremo in questo articolo è simile a uno dei primi Final Fantasy.




Cinque motivi per cui devi creare un JRPG

1. Hanno messo la prova del tempo

Giochi come Final Fantasy VI e Chrono Trigger sono ancora molto divertenti da giocare. Se fai un JRPG stai imparando un formato di gioco senza tempo a cui i giocatori moderni sono ancora molto ricettivi. Rappresentano un'ottima struttura per aggiungere la tua svolta e sperimentare, sia nella narrativa, nella presentazione o nella meccanica. È una grande cosa se riesci a realizzare un gioco che è ancora giocato e apprezzato decenni dopo la sua prima pubblicazione!

2. I meccanismi di gioco sono ampiamente applicabili

Call of Duty, uno dei giochi FPS più popolari al mondo, utilizza elementi RPG; il boom del social game che circondava FarmVille era fondamentalmente un clone del SNS RPG Harvest Moon; e persino i giochi di corse come Gran Turismo hanno livelli ed esperienza.

3. I vincoli favoriscono la creatività

Proprio come uno scrittore potrebbe essere intimidito da un foglio di carta bianco, uno sviluppatore di giochi potrebbe ritrovarsi paralizzato dal gran numero di scelte possibili quando si progetta un nuovo gioco. Con un JRPG sono state scelte molte delle scelte per te, quindi non hai quella scelta di paralisi, sei libero di seguire le convenzioni per la maggior parte delle decisioni e deviare dalla convenzione nei punti che contano per te.

4. È fattibile come progetto solista

Final Fantasy era quasi interamente codificato da un solo programmatore, Nasir Gebelli, e lo stava facendo in assemblea! Con strumenti e linguaggi moderni è molto più facile creare questo tipo di gioco. La maggior parte della maggior parte dei giochi di ruolo non è la programmazione è il contenuto - ma questo non deve essere il caso per il tuo gioco. Se lo ridistrai un po 'sul contenuto e ti concentri sulla qualità piuttosto che sulla quantità, un JRPG è un grande progetto solista.

Avere una squadra può aiutare con qualsiasi gioco, e potresti voler esternalizzare l'arte e la musica, o usare alcune delle eccellenti risorse creative creative da luoghi come opengameart.org. (Nota dell'editore: il nostro sito sorella GraphicRiver vende anche fogli sprite).

5. Per profitto!

I JRPG hanno un seguito dedicato e un certo numero di JRPG indie (come quelli nella foto qui sotto) hanno ottenuto buoni risultati commerciali e sono disponibili su piattaforme come Steam.



Architettura


I JRPG condividono così tante convenzioni e meccaniche che è possibile abbattere un tipico JRPG in un numero di sistemi:

Nello sviluppo del software uno schema è visto più e più volte: stratificazione. Questo si riferisce al modo in cui i sistemi di un programma si basano l'uno sull'altro, con i livelli ampiamente applicabili nella parte inferiore e gli strati più intimamente che si occupano del problema vicino alla cima. I JRPG non sono diversi e possono essere visti come un numero di livelli: gli strati inferiori si occupano delle funzioni grafiche di base e gli strati superiori si occupano di missioni e statistiche dei personaggi.

Mancia: Quando si sviluppa un nuovo sistema, è meglio cominciare creando prima i livelli inferiori e poi spostando strato per strato verso l'alto. L'uso del middleware ti aiuta a saltare diversi livelli inferiori comuni a molti giochi. Nel diagramma dell'architettura sopra, tutti i livelli sotto la linea tratteggiata sono gestiti da un motore di gioco 2D.

Come puoi vedere dal diagramma dell'architettura sopra, ci sono molti sistemi che compongono un JRPG ma la maggior parte dei sistemi può essere raggruppata come separata modalità Del gioco. I JRPG hanno modalità di gioco ben distinte; hanno una mappa del mondo, una mappa locale, una modalità di combattimento e diverse modalità di menu. Queste modalità sono quasi completamente separate, pezzi di codice autonomi, rendendo ognuno semplice da sviluppare.

Le modalità sono importanti ma sarebbero inutili senza contenuti di gioco. Un gioco di ruolo contiene molti file di mappe, definizioni di mostri, linee di dialogo, script per eseguire filmati e codice di gioco per controllare come procede il giocatore. Coprire come costruire un JRPG in dettaglio riempirebbe un intero libro, quindi ci concentreremo su alcune delle parti più importanti. Gestire le modalità di gioco in modo pulito è fondamentale per la produzione di un JRPG gestibile, quindi questo è il primo sistema che esploreremo.



Gestione dello stato del gioco


L'immagine sotto mostra il ciclo di gioco che si allontana, chiamando una funzione di aggiornamento ogni fotogramma. Questo è il battito cardiaco del gioco e quasi tutti i giochi sono strutturati in questo modo.

Hai mai avviato un progetto ma sei rimasto in stallo perché hai trovato troppo difficile aggiungere nuove funzionalità o sono stati afflitti da bug misteriosi? Forse hai provato a racchiudere tutto il tuo codice nella funzione di aggiornamento con poca struttura e hai trovato che il codice diventava un pasticcio criptico. Un'ottima soluzione a questi tipi di problemi consiste nel separare il codice in diversi stati di gioco, dando una visione molto più chiara di ciò che sta accadendo.

Uno strumento di gioco comune è il macchina di stato; è usato dappertutto, per gestire animazioni, menu, flusso di gioco, AI ... è uno strumento essenziale da avere nel nostro kit. Per il JRPG possiamo usare una macchina a stati per gestire le diverse modalità di gioco. Daremo un'occhiata a una normale macchina a stati e poi la mescoleremo un po ', per renderla più adatta al JRPG. Ma prima prendiamo un po 'di tempo per considerare il flusso generale del gioco nella foto qui sotto.

In un tipico JRPG probabilmente inizierai nella modalità di gioco della mappa locale, libero di girovagare per una città e interagire con i suoi abitanti. Dalla città puoi partire - qui entrerai in una modalità di gioco diversa e vedrai la mappa del mondo.

La mappa del mondo è molto simile alla mappa locale ma su scala più ampia; puoi vedere montagne e città, invece di alberi e recinzioni. Mentre sei sulla mappa del mondo, se torni in città, la modalità tornerà alla mappa locale.

Nella mappa del mondo o nella mappa locale puoi aprire un menu per controllare i tuoi personaggi e, a volte, sulla mappa del mondo, sarai lanciato in combattimento. Lo schema sopra descrive queste modalità di gioco e le transizioni; questo è il flusso di base del gameplay di JRPG ed è ciò da cui creeremo i nostri stati di gioco.

Gestire la complessità con una macchina a stati

Una macchina a stati, per i nostri scopi, è un pezzo di codice che contiene tutte le varie modalità dei nostri giochi, che ci consente di passare da una modalità all'altra, e che aggiorna e rende qualunque sia la modalità corrente..

A seconda del linguaggio di implementazione, una macchina a stati di solito consiste in a StateMachine classe e un'interfaccia, Premetto, che tutti gli stati implementano.

Mancia: Un'interfaccia è solo una classe con definizioni di funzioni membro ma nessuna implementazione. Le classi che ereditano da un'interfaccia sono necessarie per implementare le sue funzioni membro. Ciò significa che un'interfaccia non ha codice, specifica solo che altre classi forniscono determinate funzionalità. Ciò consente di utilizzare classi diverse nello stesso modo poiché sappiamo che hanno un gruppo di funzioni membro definite da un'interfaccia comune. Post correlati
  • Introduzione alla programmazione orientata agli oggetti per lo sviluppo di giochi

Una macchina a stati è descritta al meglio disegnando un sistema di base in pseudocodice:

class StateMachine Map mStati = nuova mappa(); IState mCurrentState = EmptyState; public void Update (float elapsedTime) mCurrentState.Update (elapsedTime);  public void Render () mCurrentState.Render ();  public void Change (String stateName, facoltativi var params) mCurrentState.OnExit (); mCurrentState = mStates [nomeStato]; mCurrentState.OnEnter (params);  public void Add (Nome stringa, stato IState) mStati [nome] = stato; 

Questo codice sopra mostra una semplice macchina a stati senza controllo degli errori.

Diamo un'occhiata a come il codice macchina di cui sopra è utilizzato in un gioco. All'inizio del gioco a StateMachine verrà creato, verranno aggiunti tutti i diversi stati del gioco e verrà impostato lo stato iniziale. Ogni stato è identificato in modo univoco da a Stringa nome che viene utilizzato quando si chiama la funzione di cambio stato. C'è sempre e solo uno stato attuale, mCurrentState, ed è reso e aggiornato ogni ciclo di gioco.

Il codice potrebbe apparire come questo:

StateMachine gGameMode = new StateMachine (); // Uno stato per ogni modalità di gioco gGameMode.Add ("mainmenu", new MainMenuState (gGameMode)); gGameMode.Add ("localmap", new LocalMapState (gGameMode)); gGameMode.Add ("worldmap", new WorldMapState (gGameMode)); gGameMode.Add ("battaglia", nuovo BattleState (gGameMode)); gGameMode.Add ("ingamemenu", nuovo InGameMenuState (gGameMode)); gGameMode.Change ( "mainmenu"); // Main Game Update Loop public void Update () float elapsedTime = GetElapsedFrameTime (); gGameMode.Update (elapsedTime); gGameMode.Render (); 

Nell'esempio, creiamo tutti gli stati richiesti, li aggiungiamo al StateMachine e impostare lo stato iniziale sul menu principale. Se abbiamo eseguito questo codice il MainMenuState sarebbe reso e aggiornato prima. Questo rappresenta il menu che vedi nella maggior parte dei giochi al primo avvio, con opzioni come Inizia il gioco e Carica partita.

Quando un utente seleziona Inizia il gioco, il MainMenuState chiama qualcosa come gGameMode.Change ("localmap", "map_001") e il LocalMapState diventa il nuovo stato attuale. Questo stato aggiornerebbe e renderizzerebbe la mappa, permettendo al giocatore di iniziare ad esplorare il gioco.

Lo schema seguente mostra una visualizzazione di una macchina a stati che si muove tra il WorldMapState e BattleState. In una partita questo sarebbe equivalente a un giocatore che gira per il mondo, viene attaccato dai mostri, entra in modalità combattimento e poi torna alla mappa.

Diamo una rapida occhiata all'interfaccia di stato e a EmptyState classe che lo implementa:

interfaccia pubblica IState public virtual void Update (float elapsedTime); public virtual void Render (); pubblico virtuale vuoto OnEnter (); pubblico virtuale vuoto OnExit ();  public EmptyState: IState public void Update (float elapsedTime) // Nulla da aggiornare nello stato vuoto.  public void Render () // Niente da rendere nello stato vuoto public void OnEnter () // Nessuna azione da eseguire quando lo stato è immesso public void OnExit () // Nessuna azione da eseguire quando lo stato è uscito

L'interfaccia Premetto richiede che ogni stato abbia quattro metodi prima che possa essere utilizzato come stato nella macchina a stati: Aggiornare(), Render (), OnEnter () e OnExit ().

Aggiornare() e Render () sono chiamati ogni frame per lo stato attualmente attivo; OnEnter () e OnExit () vengono chiamati quando si cambia stato. A parte questo, è tutto abbastanza semplice. Ora sai che puoi creare tutti i tipi di stati per tutte le diverse parti del tuo gioco.

Questa è la macchina di stato di base. È utile in molte situazioni, ma quando si ha a che fare con le modalità di gioco possiamo migliorarci! Con il sistema attuale, il cambiamento dello stato può avere un sovraccarico, a volte quando si passa a BattleState vorremmo lasciare il WorldState, corri la battaglia e poi torna al WorldState nella configurazione esatta era prima della battaglia. Questo tipo di operazione può essere maldestro usando la macchina a stati standard che abbiamo descritto. Una soluzione migliore sarebbe usare a pila di stati.

Rendere la logica di gioco più facile con uno stack di stato

Possiamo convertire la macchina a stati standard in una pila di stati, come mostrato nello schema seguente. Ad esempio, il MainMenuState viene messo in pila per primo, all'inizio del gioco. Quando iniziamo un nuovo gioco, il LocalMapState è spinto in cima a quello. A questo punto il MainMenuState non è più renderizzato o aggiornato ma è in attesa, pronto per tornare.

Quindi, se iniziamo una battaglia, il BattleState è spinto in alto; quando la battaglia finisce, viene fuori dallo stack e possiamo riprendere la mappa esattamente dove avevamo lasciato. Se moriamo nel gioco, allora LocalMapState viene saltato fuori e noi ritorniamo a MainMenuState.

Lo schema seguente mostra una pila di stati, mostrando il InGameMenuState essere spinto in pila e poi fatto scattare.

Ora abbiamo un'idea di come funziona lo stack, diamo un'occhiata ad un codice per implementarlo:

public class StateStack Mappa mStati = nuova mappa(); Elenco mStack = List(); public void Update (float elapsedTime) IState top = mStack.Top () top.Update (elapsedTime) public void Render () IState top = mStack.Top () top.Render () public void Push (Nome stringa) Stato IState = mStati [nome]; mStack.Push (stato);  public IState Pop () return mStack.Pop (); 

Questo codice stack sopra lo stato non ha controllo degli errori ed è abbastanza semplice. Gli stati possono essere spinti in pila usando il Spingere() chiama e scatta con a Pop() chiamata, e lo stato in cima alla pila è quello che viene aggiornato e reso.

L'utilizzo di un approccio basato su stack è utile per i menu e con una piccola modifica può essere utilizzato anche per finestre di dialogo e notifiche. Se ti senti avventuroso, puoi combinare entrambi e disporre di una macchina a stati che supporti anche gli stack.

utilizzando StateMachine, StateStack, o una combinazione dei due crea una struttura eccellente per costruire il tuo gioco di ruolo.

Prossime azioni:

  1. Implementa il codice macchina dello stato nel tuo linguaggio di programmazione preferito.
  2. Creare un MenuMenuState e gamestate ereditando da Premetto.
  3. Imposta lo stato del menu principale come stato iniziale.
  4. Entrambi gli stati rendono immagini diverse.
  5. Premendo un pulsante, lo stato cambia dal menu principale allo stato di gioco.


Mappe


Le mappe descrivono il mondo; deserti, astronavi e giungle possono essere rappresentati utilizzando una tilemap. Una tilemap è un modo di utilizzare un numero limitato di piccole immagini per crearne uno più grande. Lo schema seguente mostra come funziona:

Il diagramma qui sopra ha tre parti: la tavolozza delle tessere, una visualizzazione di come viene costruita la mappa del tondo e la mappa finale resa allo schermo.

La tavolozza delle tessere è una raccolta di tutte le tessere utilizzate per creare una mappa. Ogni tessera nella tavolozza è identificata univocamente da un numero intero. Ad esempio, la tessera numero 1 è erba; nota i punti in cui è utilizzato nella visualizzazione della mappa tandem.

Una tilemap è solo una serie di numeri, ogni numero relativo a una tessera nella tavolozza. Se volessimo creare una mappa piena di erba potremmo avere solo un grande array riempito con il numero 1, e quando abbiamo reso quelle tessere vedremmo una mappa di erba composta da tante piccole tessere di erba. La tavolozza delle tessere viene solitamente caricata come una grande trama contenente molte tessere più piccole, ma ogni voce nella tavolozza potrebbe altrettanto facilmente essere il proprio file grafico.

Mancia: Perché non utilizzare una matrice di matrici per rappresentare la mappa del testo? Il primo array potrebbe rappresentare da una serie di file di tessere.

Il motivo per cui non lo facciamo è solo per semplicità ed efficienza. Se si dispone di una matrice di numeri interi, questo è un blocco di memoria continuo. Se hai una matrice di array, allora questo è un blocco di memoria per il primo array che contiene puntatori, con ogni puntatore che punta a una fila di tessere. Questa indirezione può rallentare le cose - e dal momento che stiamo disegnando la mappa ogni fotogramma, tanto più veloce è e meglio è!

Diamo un'occhiata ad un codice per descrivere una mappa di tile:

// // Prende una mappa texture di più riquadri e la suddivide in // singole immagini di 32 x 32. // L'array finale sarà simile a: // gTilePalette [1] = Immagine // La nostra prima tessera erba // gTilePalette [2] = Immagine // Seconda variante tessera erba // ... // gTilePalette [15] = Immagine // Tegola roccia ed erba // Array gTilePalette = SliceTexture ("grass_tiles.png", 32, 32) gMap1Width = 10 gMap1Height = 10 Array gMap1Layer1 = new 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, 4, 8, 2, 1, 2, 1, 10, 11, 11, 11, 11, 11, 9, 10, 11, 12, 13, 5, 11, 11, 11, 11, 4, 13, 14, 15, 1, 10, 11, 11, 11, 11, 6, 2, 2, 2, 13, 14, 11, 11, 11, 11, 2, 2, 2, 2, 2, 11, 11, 11, 11, 2, 2, 2, 2, 2, 5, 11, 11, 11, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,];

Confronta il codice sopra con il diagramma ed è abbastanza chiaro come una tilemap è costituita da una piccola serie di tessere. Una volta descritta una mappa come questa, possiamo scrivere una semplice funzione di rendering per disegnarla sullo schermo. I dettagli esatti della funzione cambieranno a seconda della configurazione della vista e delle funzioni di disegno. La nostra funzione di rendering è mostrata sotto.

static int TilePixelSize = 32; // Disegna una tilemap in alto a sinistra, in posizione dei pixel x, y // x, y - la posizione dei pixel della mappa sarà resa da // map - la mappa per render // larghezza - la larghezza della mappa in tile public void RenderMap (int x, int y, mappa array, int mapWidth) // Inizia indicizzando la piastrella più in alto a sinistra int tileColumn = 1; int tileRow = 1; for (int i = 1; map.Count (); i ++) // Meno 1 in modo che la prima tessera disegni a 0, 0 int pixelPosX = x + (tileColumn - 1) * TilePixelSize; int pixelPosY = y + (tileRow - 1) * TilePixelSize; RenderImage (x, y, gTilePalette [gMap1Layer1 [i]]); // Avanza alla piastrella successiva TileColumn + = 1; if (tileColumn> mapWidth) tileColumn = 1; tileRow + = 1;  - Come viene utilizzato nel ciclo di aggiornamento principale public void Update () // In realtà traccia una mappa sullo schermo RenderMap (0, 0, gMap1Layer1, gMap1Width)

La mappa che abbiamo usato fino ad ora è abbastanza semplice; la maggior parte dei JRPG utilizza più livelli di tilemaps per creare scene più interessanti. Lo schema seguente mostra la nostra prima mappa, con altri tre livelli aggiunti, risultante in una mappa molto più piacevole.

Come abbiamo visto in precedenza, ogni tilemap è solo una serie di numeri e quindi una mappa a più livelli può essere creata da una matrice di tali matrici. Ovviamente, il rendering della tilemap è davvero solo il primo passo per aggiungere esplorazione al tuo gioco; le mappe devono anche avere informazioni sulla collisione, il supporto per le entità in movimento e l'utilizzo dell'interattività di base trigger.

Un innesco è un pezzo di codice che viene sparato solo quando il giocatore lo "innesca" eseguendo un'azione. Ci sono molte azioni che un trigger può riconoscere. Ad esempio, spostare il personaggio del giocatore su una tessera può innescare un'azione - questo di solito accade quando si passa su una porta, un teletrasporto o una tessera bordo della mappa. È possibile posizionare i trigger su queste tessere per teletrasportare il personaggio su una mappa interna, una mappa del mondo o una mappa locale correlata.

Un altro trigger potrebbe dipendere dal pulsante "usa" premuto. Ad esempio, se il giocatore sale a un segno e preme "usa", viene attivato un trigger e viene visualizzata una finestra di dialogo che mostra il testo del segno. I trigger sono usati dappertutto per aiutare a unire mappe e fornire interattività.

I JRPG spesso hanno mappe molto dettagliate e complicate, quindi ti consiglio di non provare a crearle a mano, è un'idea molto migliore usare un editor di mappe di dati. È possibile utilizzare una delle eccellenti soluzioni esistenti gratuite o eseguire il rollover. Se vuoi provare uno strumento esistente, ti consiglio di provare Tiled, che è lo strumento con cui ho creato queste mappe di esempio.

Post correlati
  • Introduzione a Tiled
  • Analisi di mappe in formato TMX affiancate nel tuo motore di gioco
  • Conoscere Ogmo Editor

Prossime azioni:

  1. Get Tiled.
  2. Prendi alcune tessere da opengameart.org.
  3. Crea una mappa e caricala nel tuo gioco.
  4. Aggiungi un personaggio giocatore.
  5. Sposta il personaggio da una tessera a una piastrella.
  6. Fai in modo che il personaggio si muova senza problemi da una tessera all'altra.
  7. Aggiungi il rilevamento delle collisioni (puoi usare un nuovo livello per memorizzare le informazioni sulle collisioni).
  8. Aggiungi un semplice trigger per scambiare le mappe.
  9. Aggiungi un trigger per leggere i segni - considera di utilizzare lo stack di stato di cui abbiamo parlato in precedenza per visualizzare la finestra di dialogo.
  10. Crea uno stato del menu principale con l'opzione "Inizia partita" e uno stato della mappa locale e collegali tra loro.
  11. Disegna alcune mappe, aggiungi alcuni PNG, prova una semplice missione di recupero: libera la tua immaginazione!


Combattere

Finalmente ai combattimenti! A cosa serve un JRPG senza combattere? Il combattimento è il luogo in cui molti giochi scelgono di innovare, introducendo nuovi sistemi di abilità, nuove strutture di combattimento o sistemi di incantesimi diversi - c'è parecchia differenza.

La maggior parte dei sistemi di combattimento utilizza una struttura a turni con un solo combattente autorizzato a compiere un'azione alla volta. I primi sistemi di battaglia a turni erano semplici, con ogni entità che otteneva un turno nell'ordine: turno del giocatore, turno del nemico, turno del giocatore, turno del nemico e così via. Questo ha rapidamente dato il via a sistemi più complessi che offrono più margine di manovra per tattiche e strategia.

Daremo un'occhiata da vicino Attivo-Time sistemi di combattimento basati, dove i combattenti non ottengono necessariamente un numero uguale di turni. Le entità più veloci possono ottenere più turni e il tipo di azione adottata influisce anche sulla durata di una svolta. Ad esempio, un guerriero che colpisce con un pugnale può impiegare 20 secondi, ma un mago che evoca un mostro può impiegare due minuti.


L'immagine sopra mostra la modalità di combattimento in un tipico JRPG. I personaggi controllati dal giocatore sono sulla destra, i personaggi nemici sulla sinistra e una casella di testo nella parte inferiore mostra le informazioni sui combattenti.

All'inizio del combattimento, gli sprite di mostri e giocatori vengono aggiunti alla scena e poi c'è una decisione su quale ordine le entità eseguono i loro turni. Questa decisione può dipendere in parte dal modo in cui il combattimento è stato lanciato: se il giocatore è caduto in un'imboscata, i mostri attaccheranno tutti per primi, altrimenti di solito è basato su una delle statistiche dell'entità come la velocità.

Tutto ciò che il giocatore oi mostri fanno è un'azione: attaccare è un'azione, usare la magia è un'azione, anche decidere quale azione intraprendere dopo è un'azione! L'ordine delle azioni viene tracciato meglio utilizzando una coda. L'azione in alto è l'azione che si svolgerà in seguito, a meno che nessuna azione più rapida lo prevenga. Ogni azione avrà un conto alla rovescia che diminuisce man mano che passa ogni frame.

Il flusso di combattimento è controllato usando una macchina a stati con due stati; uno stato per spuntare le azioni e un altro stato per eseguire l'azione migliore quando arriva il momento. Come sempre, il modo migliore per capire qualcosa è guardare il codice. L'esempio seguente implementa uno stato di combattimento di base con una coda di azioni:

class BattleState: IState List mActions = Lista(); Elenco mEntities = List(); StateMachine mBattleStates = new StateMachine (); public static bool SortByTime (Action a, Action b) return a.TimeRemaining ()> b.TimeRemaining () public BattleState () mBattleStates.Add ("tick", nuovo BattleTick (mBattleStates, mActions)); mBattleStates.Add ("esegui", nuovo BattleExecute (mBattleStates, mActions));  public void OnEnter (var params) mBattleStates.Change ("tick"); // // Ottieni un'azione decisionale per ogni entità nella coda di azioni // L'ordinamento in modo che le azioni più veloci siano in cima // mEntities = params.entities; foreach (Entità e in mità) if (e.playerControlled) PlayerDecide action = new PlayerDecide (e, e.Speed ​​()); mActions.Add (azione);  else AIDecide action = new AIDecide (e, e.Speed ​​()); mActions.Add (azione);  Sort (mActions, BattleState :: SortByTime);  public void Update (float elapsedTime) mBattleStates.Update (elapsedTime);  public void Render () // Disegna la scena, gui, caratteri, animazioni ecc. mBattleState.Render ();  public void OnExit () 

Il codice sopra mostra il controllo del flusso in modalità battaglia utilizzando una semplice macchina a stati e una coda di azioni. Per cominciare, tutte le entità coinvolte nella battaglia hanno un decidere-action aggiunto alla coda.

Un'azione decisionale per il giocatore farà apparire un menu con le opzioni stabili degli RPG attacco, Magia, e Articolo; una volta che il giocatore decide un'azione, l'azione decisionale viene rimossa dalla coda e l'azione appena scelta viene aggiunta.

Un'azione decisionale per l'IA controllerà la scena e deciderà cosa fare dopo (usando qualcosa come un albero dei comportamenti, un albero delle decisioni o una tecnica simile) e poi rimuoverà la sua azione decisionale e aggiungerà la sua nuova azione alla coda.

Il BattleTick classe controlla l'aggiornamento delle azioni, come mostrato di seguito:

class BattleTick: IState StateMachine mStateMachine; Elenco mActions; BattleTick pubblico (StateMachine stateMachine, List azioni): mStateMachine (stateMachine), mActions (action)  // Le cose possono accadere in queste funzioni ma nulla a cui siamo interessati. public void OnEnter ()  public void OnExit ()  public void Render ()   public void Update (float elapsedTime) foreach (Azione a in mActions) a.Update (elapsedTime);  if (mActions.Top (). IsReady ()) Action top = mActions.Pop (); mStateMachine: Change ("execute", top); 

BattleTick è uno stato secondario dello stato BattleMode e fa semplicemente tic tac finché il conto alla rovescia dell'azione superiore è zero. Quindi apre l'azione in cima alla coda e passa a eseguire stato.


Lo schema sopra mostra una coda di azione all'inizio di una battaglia. Nessuno ha ancora intrapreso un'azione e tutti sono ordinati in base al loro tempo per prendere una decisione.

Il Giant Plant ha un conto alla rovescia pari a 0, quindi al successivo tick esegue il suo conto alla rovescia AIDecide azione. In questo caso il AIDecide l'azione risulta nel mostro che decide di attaccare. L'azione di attacco è quasi immediata e viene aggiunta nuovamente in coda come seconda azione.

Alla successiva iterazione di BattleTick, il giocatore sceglierà quale azione deve compiere il suo nano "Marco", che cambierà di nuovo la coda. La prossima iterazione di BattleTick dopo di ciò, la pianta attaccherà uno dei nani. L'azione di attacco verrà rimossa dalla coda e passata al BattleExecute stato, e animerà la pianta attaccando e facendo tutti i calcoli di combattimento necessari.

Una volta che l'attacco del mostro è finito, un altro AIDecide l'azione verrà aggiunta alla coda per il mostro. Il BattleState continuerà in questo modo fino alla fine del combattimento.

Se qualche entità muore durante il combattimento, tutte le sue azioni devono essere rimosse dalla coda - non vogliamo che mostri mor