Cet article est une vue d'ensemble de haut niveau pour la création d'un JRPG (jeu de rôle japonais) tel que les premiers jeux Final Fantasy. Nous examinerons l'architecture et les systèmes qui constituent le squelette d'un JRPG, comment gérer les modes de jeu, comment utiliser tilemaps pour afficher le monde et comment coder un système de combat RPG..
Remarque: Cet article utilise un langage de pseudo-code semblable à Java, mais les concepts sont applicables à tout environnement de développement de jeu..
En 1983, Yuji Horii, Koichi Nakamura et Yukinobu Chida ont pris l'avion pour l'Amérique et ont assisté à AppleFest '83, un rassemblement de développeurs présentant leurs dernières créations pour Apple II. Ils ont été époustouflés par la dernière version d'un RPG appelé Wizardry.
De retour au Japon, ils décident de créer Dragon Warrior, un RPG similaire mais simplifié pour la NDA. C'était un succès énorme, définissant le genre JRPG. Dragon Warrior n’a pas eu autant de succès en Amérique, mais quelques années plus tard, un autre jeu.
En 1987, Final Fantasy est sorti, créant l’une des franchises de jeux vidéo les plus vendues au monde, qui est devenue, du moins en Occident, l’emblématique JRPG..
Les genres de jeux ne sont jamais définis avec précision - ils constituent plutôt un ensemble de conventions floues. Les JdR ont généralement un système de mise à niveau, un ou plusieurs personnages avec compétences et statistiques, des armes et armures, des modes de combat et d’exploration et des récits forts; la progression du jeu est souvent obtenue en avançant sur une carte.
Les jeux de rôle japonais sont des jeux de rôle créés dans le moule de Dragon Warrior; elles sont plus linéaires, les combats sont souvent effectués tour par tour et il existe généralement deux types de cartes: une carte du monde et une carte locale. Les JRPG archétypaux comprennent Dragon Warrior, Final Fantasy, Wild Arms, Phantasy Star et Chrono Trigger. Le type de JRPG dont nous allons parler dans cet article est similaire à un ancien Final Fantasy..
Des jeux comme Final Fantasy VI et Chrono Trigger sont encore très agréables à jouer. Si vous fabriquez un JRPG, vous apprendrez un format de jeu intemporel auquel les joueurs modernes sont toujours très réceptifs. Ils constituent un excellent cadre pour ajouter votre propre tournure d’expérience - qu’il s’agisse de la narration, de la présentation ou de la mécanique. C'est une bonne chose si vous pouvez créer un jeu qui est toujours joué et apprécié des décennies après sa première sortie.!
Call of Duty, l'un des jeux FPS les plus populaires au monde, utilise des éléments RPG; le boom du jeu social entourant FarmVille était essentiellement un clone du jeu de rôle SNES RPG Harvest Moon; et même des jeux de course comme Gran Turismo ont des niveaux et de l'expérience.
Tout comme un écrivain peut être intimidé par une feuille de papier vierge, un développeur de jeux peut se trouver paralysé par le grand nombre de choix possibles lors de la conception d'un nouveau jeu. Avec un JRPG, de nombreux choix ont été choisis pour vous, vous n’avez donc pas cette paralysie, vous êtes libre de suivre les conventions pour la plupart des décisions et de vous écarter des conventions aux points qui vous intéressent.
Final Fantasy a été presque entièrement codé par un seul programmeur, Nasir Gebelli, et il le faisait en assembleur! Avec les outils et les langages modernes, il est beaucoup plus facile de créer ce type de jeu. La majeure partie de la plupart des RPG n'est pas la programmation, c'est le contenu - mais cela ne doit pas nécessairement être le cas pour votre jeu. Si vous rappelez un peu le contenu et vous concentrez sur la qualité plutôt que sur la quantité, un JRPG est un excellent projet solo..
Avoir une équipe peut vous aider pour n’importe quel jeu et vous voudrez peut-être externaliser l’art et la musique, ou utiliser certains des excellents actifs créatifs communs de lieux tels que opengameart.org.. (Note de la rédaction: notre site soeur, GraphicRiver, vend également des feuilles de sprite.)
Les JRPG ont un public dédié et un certain nombre de JRPG indépendants (tels que ceux illustrés ci-dessous) ont bien fonctionné commercialement et sont disponibles sur des plateformes telles que Steam..
Les JRPG partagent tellement de conventions et de mécanismes qu'il est possible de décomposer un JRPG typique en plusieurs systèmes:
Dans le développement logiciel, un modèle se voit encore et encore: superposition. Cela fait référence à la façon dont les systèmes d'un programme se superposent, avec des couches largement applicables en bas et des couches traitant plus intimement du problème à résoudre près du sommet. Les JRPG ne sont pas différents et peuvent être vus comme un certain nombre de couches - les couches inférieures traitent des fonctions graphiques de base et les couches supérieures traitent des quêtes et des statistiques de personnage..
Pointe: Lors du développement d'un nouveau système, il est préférable de commencer par créer les couches inférieures, puis de déplacer couche par couche vers le haut. L'utilisation d'un middleware vous permet d'ignorer plusieurs couches inférieures communes à de nombreux jeux. Sur le schéma d’architecture ci-dessus, toutes les couches en dessous de la ligne pointillée sont gérées par un moteur de jeu 2D..Comme vous pouvez le constater dans le diagramme d’architecture ci-dessus, de nombreux systèmes constituent un groupe JRPG, mais la plupart peuvent être regroupés séparément. modes Du jeu. Les JRPG ont des modes de jeu très distincts; ils ont une carte du monde, une carte locale, un mode combat et plusieurs modes de menu. Ces modes sont presque entièrement séparés, des morceaux de code autonomes, rendant chacun simple à développer.
Les modes sont importants mais ils seraient inutiles sans contenu de jeu. Un RPG contient de nombreux fichiers de carte, définitions de monstres, lignes de dialogue, scripts permettant d'exécuter des cinématiques et du code de jeu permettant de contrôler la progression du joueur. Décrire en détail comment construire un JRGP remplirait un livre entier, nous allons donc nous concentrer sur certaines des parties les plus importantes. La gestion propre des modes de jeu est essentielle pour produire un JRPG gérable. C'est le premier système que nous allons explorer.
L'image ci-dessous montre le déroulement du jeu en boucle, appelant une fonction de mise à jour chaque image. C'est le battement de coeur du jeu et presque tous les jeux sont structurés de cette façon.
Avez-vous déjà démarré un projet mais êtes-vous bloqué parce que vous avez trouvé trop difficile d'ajouter de nouvelles fonctionnalités ou que vous avez été victime de bugs mystérieux? Peut-être avez-vous essayé d’intégrer tout votre code dans la fonction de mise à jour avec peu de structure et vous avez trouvé que le code devenait un gâchis cryptique. Une excellente solution à ces types de problèmes consiste à séparer le code en différents états de jeu, donnant une vision beaucoup plus claire de ce qui se passe.
Un outil de jeu commun est le machine d'état; il est utilisé partout pour gérer les animations, les menus, le déroulement du jeu, l'IA… c'est un outil essentiel à avoir dans notre kit. Pour le JRPG, nous pouvons utiliser une machine à états pour gérer les différents modes de jeu. Nous examinerons une machine à états normale, puis nous la mélangerons un peu pour la rendre plus adaptée au JRPG. Mais prenons d’abord un peu de temps pour examiner le déroulement général du jeu illustré ci-dessous..
Dans un JRPG typique, vous commencerez probablement dans le mode de jeu de carte local, libre de vous promener dans une ville et d’interagir avec ses habitants. Vous pouvez partir de la ville. Ici, vous entrerez dans un mode de jeu différent et verrez la carte du monde..
La carte du monde ressemble beaucoup à la carte locale mais à une plus grande échelle; vous pouvez voir des montagnes et des villes au lieu d'arbres et de clôtures. Sur la carte du monde, si vous revenez dans la ville, le mode reviendra à la carte locale..
Sur la carte du monde ou la carte locale, vous pouvez afficher un menu permettant de consulter vos personnages. Parfois, sur la carte du monde, vous vous retrouvez au combat. Le diagramme ci-dessus décrit ces modes de jeu et transitions; c'est le flux de base du gameplay de JRPG et est ce que nous allons créer nos états de jeu de.
Une machine à états, pour nos besoins, est un morceau de code qui contient tous les différents modes de nos jeux, qui nous permet de passer d’un mode à l’autre, et qui met à jour et restitue le mode actuel..
En fonction du langage d’implémentation, une machine à états consiste généralement en une StateMachine
classe et une interface, Je déclare
, que tous les états mettent en œuvre.
La meilleure façon de décrire une machine à états consiste à dessiner un système de base en pseudocode:
Classe StateMachine CartemStates = nouvelle carte (); IState mCurrentState = EmptyState; Mise à jour publique void (float elapsedTime) mCurrentState.Update (elapsedTime); public void Render () mCurrentState.Render (); public void Change (String stateName, paramètres var facultatifs) mCurrentState.OnExit (); mCurrentState = mStates [stateName]; mCurrentState.OnEnter (paramètres); public void Add (nom de chaîne, état ISate) mStates [nom] = état;
Ce code ci-dessus montre une machine à états simple sans vérification d'erreur.
Regardons comment le code machine ci-dessus est utilisé dans un jeu. Au début du jeu a StateMachine
sera créé, tous les différents états du jeu ajoutés et l'état initial défini. Chaque État est identifié de manière unique par un Chaîne
nom utilisé lors de l'appel de la fonction change state. Il n'y a jamais qu'un seul état actuel, mCurrentState
, et il est rendu et mis à jour chaque boucle de jeu.
Le code pourrait ressembler à ceci:
StateMachine gGameMode = new StateMachine (); // Un état pour chaque mode de jeu gGameMode.Add ("mainmenu", new MainMenuState (gGameMode)); gGameMode.Add ("localmap", new LocalMapState (gGameMode)); gGameMode.Add ("worldmap", nouveau WorldMapState (gGameMode)); gGameMode.Add ("battle", nouveau BattleState (gGameMode)); gGameMode.Add ("ingamemenu", nouvel InGameMenuState (gGameMode)); gGameMode.Change ("mainmenu"); // Boucle principale de mise à jour du jeu public void Update () float elapsedTime = GetElapsedFrameTime (); gGameMode.Update (elapsedTime); gGameMode.Render ();
Dans l'exemple, nous créons tous les états requis, nous les ajoutons à la StateMachine
et définissez l’état de départ sur le menu principal. Si nous avons couru ce code la MainMenuState
serait rendu et mis à jour en premier. Cela représente le menu que vous voyez dans la plupart des jeux lorsque vous démarrez pour la première fois, avec des options comme Démarrer jeu et Chargement du jeu.
Lorsqu'un utilisateur sélectionne Démarrer jeu, la MainMenuState
appelle quelque chose comme gGameMode.Change ("localmap", "map_001")
et le LocalMapState
devient le nouvel état actuel. Cet état mettrait alors à jour et rendrait la carte, permettant au joueur de commencer à explorer le jeu.
Le diagramme ci-dessous montre une visualisation d’une machine à états se déplaçant entre les WorldMapState
et État de bataille
. Dans un jeu, cela équivaut à un joueur errant dans le monde entier, attaqué par des monstres, passant en mode combat, puis retournant sur la carte..
Jetons un coup d'œil à l'interface d'état et un État vide
classe qui l'implémente:
interface publique IState mise à jour publique virtuelle void (float elapsedTime); public virtuel vide Render (); public virtuel vide OnEnter (); public virtuel vide OnExit (); public EmptyState: IState mise à jour publique void (float elapsedTime) // Rien à mettre à jour à l'état vide. public void Render () // rien à restituer dans un état vide public void OnEnter () // Aucune action à entreprendre lorsque l'état est entré public void OnExit () // Aucune action à entreprendre lorsque l'état est quitté
L'interface Je déclare
requiert que chaque état ait quatre méthodes avant de pouvoir être utilisé comme état dans la machine à états: Mettre à jour()
, Rendre()
, OnEnter ()
et OnExit ()
.
Mettre à jour()
et Rendre()
sont appelés chaque trame pour l'état actuellement actif; OnEnter ()
et OnExit ()
sont appelés lors du changement d'état. En dehors de cela, tout est assez simple. Maintenant, vous savez que vous pouvez créer toutes sortes d'états pour toutes les différentes parties de votre jeu..
C'est la machine d'état de base. C'est utile dans de nombreuses situations, mais nous pouvons améliorer les modes de jeu! Avec le système actuel, le changement d’état peut être très onéreux - parfois lorsqu’on passe à un État de bataille
nous voudrons quitter le État du monde
, lancer la bataille, puis revenir à la État du monde
dans la configuration exacte, c'était avant la bataille. Ce type d'opération peut s'avérer fastidieux avec la machine à états standard que nous avons décrite. Une meilleure solution serait d’utiliser un empiler des états.
Nous pouvons basculer la machine à états standard en une pile d'états, comme indiqué sur le schéma ci-dessous. Par exemple, le MainMenuState
est d'abord poussé sur la pile, au début de la partie. Quand on commence un nouveau jeu, le LocalMapState
est poussé sur le dessus. À ce stade, le MainMenuState
n'est plus rendu ou mis à jour mais attend, prêt à nous retourner à.
Ensuite, si nous commençons une bataille, le État de bataille
est poussé sur le dessus; quand la bataille est finie, elle est sortie de la pile et nous pouvons reprendre sur la carte exactement où nous en sommes restés. Si nous mourons dans le jeu alors LocalMapState
est sauté et nous revenons à MainMenuState
.
Le diagramme ci-dessous donne une visualisation d’une pile d’états, montrant les InGameMenuState
être poussé sur la pile, puis sauté.
Maintenant que nous avons une idée du fonctionnement de la pile, voyons du code pour l'implémenter:
Classe publique StateStack CartemStates = nouvelle carte (); liste mStack = Liste (); Mise à jour du public void (float elapsedTime) IState top = mStack.Top () top.Update (elapsedTime) public void Render () IState top = mStack.Top () top.Render () public void Push (nom de chaîne) Etat d'etat = mStates [nom]; mStack.Push (état); public ISate Pop () return mStack.Pop ();
Ce code de pile d'états ci-dessus n'a pas d'erreur de vérification et est assez simple. Les états peuvent être poussés sur la pile en utilisant le Pousser()
appeler et sauté avec un Pop()
appel, et l'état tout en haut de la pile est celui qui a été mis à jour et rendu.
Utiliser une approche basée sur la pile convient aux menus et, moyennant quelques modifications, il peut également être utilisé pour les boîtes de dialogue et les notifications. Si vous vous sentez aventureux, vous pouvez combiner les deux et disposer d'une machine à états prenant également en charge les piles..
En utilisant StateMachine
, StateStack
, ou une combinaison des deux crée une excellente structure pour construire votre RPG sur.
MenuMenuÉtat
et GameState
héritant de Je déclare
.
Les cartes décrivent le monde; les déserts, les vaisseaux spatiaux et les jungles peuvent tous être représentés à l’aide d’un tilemap. Un tilemap permet d’utiliser un nombre limité de petites images pour en créer une plus grande. Le diagramme ci-dessous vous montre comment cela fonctionne:
Le diagramme ci-dessus comprend trois parties: la palette de tuiles, une visualisation de la construction du tilemap et le rendu final de la carte à l'écran..
La palette de mosaïques est une collection de toutes les mosaïques utilisées pour créer une carte. Chaque mosaïque de la palette est identifiée de manière unique par un entier. Par exemple, la tuile numéro 1 est en herbe; remarquez les endroits où il est utilisé sur la visualisation de tilemap.
Un tilemap est juste un tableau de nombres, chaque numéro correspondant à une tuile de la palette. Si nous voulions faire une carte pleine d'herbe, nous pourrions simplement avoir un grand tableau avec le chiffre 1, et lorsque nous aurons rendu ces tuiles, nous verrions une carte d'herbe composée de nombreuses petites tuiles d'herbe. La palette de mosaïques est généralement chargée sous la forme d’une grande texture contenant de nombreuses mosaïques plus petites, mais chaque entrée de la palette peut tout aussi bien être son propre fichier graphique..
Pointe: Pourquoi ne pas utiliser un tableau de tableaux pour représenter le tilemap? Le premier tableau pourrait représenter un tableau de rangées de tuiles.La raison pour laquelle nous ne faisons pas cela est juste pour la simplicité et l'efficacité. Si vous avez un tableau d'entiers, c'est un bloc continu de mémoire. Si vous avez un tableau de tableaux, il s’agit d’un bloc de mémoire pour le premier tableau contenant des pointeurs, chaque pointeur pointant vers une rangée de carreaux. Cette indirection peut ralentir les choses - et comme nous dessinons la carte à chaque image, le plus vite sera le mieux.!
Regardons un code pour décrire une carte de tuiles:
// // Prend une carte de texture de plusieurs tuiles et la divise en // images individuelles de 32 x 32. // Le tableau final ressemblera à ceci: // gTilePalette [1] = Image // Notre première tuile en gazon // gTilePalette [2] = Image // Deuxième variante de dalle en gazon //… // gTilePalette [15] = Image // en dalle de roche et d'herbe // Tableau gTilePalette = SliceTexture ("grass_tiles.png", 32, 32) gMap1Width = 10 gMap1Height = 10 Tableau gMap1Layer1 = new Array () [2, 2, 7, 3, 11, 11, 11, 12, 2, 2, 1, 1, 1, 10, 11, 11, 4, 11, 2, 2, 2, 2, 1, 13, 5, 11, 11, 11, 4, 8, 2, 1, 2, 1, 10, 11, 11, 11, 11, 11, 9, 10, 11, 11, 12, 13, 5, 11, 11, 11, 11, 4, 13, 14, 15, 1, 10, 11, 11, 11, 11, 11, 6, 2, 2, 2, 13, 14, 11, 11, 11, 11, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, 2, 2, 2, 2, 2, 2, 5, 11, 11, 11, 11, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,];
Comparez le code ci-dessus avec le diagramme et vous verrez clairement comment tilemap est construit à partir d'une petite série de tuiles. Une fois qu'une carte est décrite comme ceci, nous pouvons écrire une fonction de rendu simple pour la dessiner à l'écran. Les détails exacts de la fonction changeront en fonction de la configuration de la fenêtre et des fonctions de dessin. Notre fonction de rendu est indiquée ci-dessous.
static int TilePixelSize = 32; // Dessine un tilemap en haut à gauche, à la position du pixel x, y // x, y - la position en pixels de la carte sera rendue à partir de // map - la carte à restituer // width - la largeur de la carte en tuiles public void RenderMap (int x, int y, Array map, int mapWidth) // Commencez par indexer la mosaïque la plus à gauche int tileColumn = 1; int tileRow = 1; for (int i = 1; map.Count (); i ++) // Moins 1 de sorte que la première mosaïque dessine à 0, 0 int pixelPosX = x + (tileColumn - 1) * TilePixelSize; int pixelPosY = y + (tileRow - 1) * TilePixelSize; RenderImage (x, y, gTilePalette [gMap1Layer1 [i]]); // Avance à la prochaine tuile tileColumn + = 1; if (tileColumn> mapWidth) tileColumn = 1; tileRow + = 1; - Comment il est utilisé dans la boucle de mise à jour principale public void Update () // Dessine en réalité une carte à l'écran RenderMap (0, 0, gMap1Layer1, gMap1Width)
La carte que nous avons utilisée jusqu'à présent est assez basique. la plupart des JRPG utiliseront plusieurs couches de tilemaps pour créer des scènes plus intéressantes. Le diagramme ci-dessous montre notre première carte, avec trois couches supplémentaires ajoutées, ce qui donne une carte bien plus agréable..
Comme nous l'avons vu précédemment, chaque tilemap est juste un tableau de nombres et donc une carte en couches complète peut être faite à partir d'un tableau de ces tableaux. Bien sûr, le rendu du tilemap n'est vraiment que la première étape pour ajouter de l'exploration à votre jeu. Les cartes doivent également contenir des informations sur les collisions, la prise en charge du déplacement des entités et une interactivité de base avec déclencheurs.
Un déclencheur est un morceau de code qui n'est déclenché que lorsque le joueur le "déclenche" en effectuant une action. Un déclencheur peut reconnaître de nombreuses actions. Par exemple, déplacer le personnage du joueur sur une tuile peut déclencher une action - cela se produit généralement lorsque vous vous déplacez vers une porte, un téléporteur ou la tuile au bord de la carte. Des déclencheurs peuvent être placés sur ces tuiles pour téléporter le personnage sur une carte intérieure, une carte du monde ou une carte locale associée..
Un autre déclencheur peut dépendre de la pression du bouton "utiliser". Par exemple, si le joueur s'approche d'un signe et appuie sur "utiliser", un déclencheur est déclenché et une boîte de dialogue s'affiche pour afficher le texte du signe. Les déclencheurs sont utilisés partout pour aider à assembler des cartes et à offrir une interactivité.
Les JRPG ont souvent beaucoup de cartes assez détaillées et compliquées, je vous recommande donc de ne pas essayer de les créer à la main, c'est une bien meilleure idée d'utiliser un éditeur de tilemap. Vous pouvez utiliser l'une des excellentes solutions gratuites existantes ou créer votre propre solution. Si vous voulez essayer un outil existant, je vous recommande vivement de consulter Tiled, l'outil avec lequel j'ai utilisé ces cartes..
Articles SimilairesEnfin, au combat! A quoi sert un JRPG sans combat? Le combat est l'endroit où beaucoup de jeux choisissent d'innover, en introduisant de nouveaux systèmes de compétences, une nouvelle structure de combat ou différents systèmes de sorts - il y a beaucoup de variations.
La plupart des systèmes de combat utilisent une structure au tour par tour avec un seul combattant autorisé à effectuer une action à la fois. Les tout premiers systèmes de combat au tour par tour étaient simples, chaque entité obtenant un tour dans l'ordre: tour du joueur, tour de l'ennemi, tour du joueur, tour de l'ennemi, etc. Cela a rapidement fait place à des systèmes plus complexes offrant plus de marge de manœuvre pour la tactique et la stratégie..
Nous allons regarder de près Temps actif systèmes de combat basés, où les combattants ne reçoivent pas nécessairement un nombre égal de tours. Les entités plus rapides peuvent avoir plus de tours et le type d'action entreprise affecte également la durée d'un tour. Par exemple, un guerrier utilisant une dague peut prendre 20 secondes, mais un sorcier invoquant un monstre peut prendre deux minutes..
La capture d'écran ci-dessus montre le mode de combat dans un JRPG typique. Les personnages contrôlés par les joueurs se trouvent à droite, les personnages ennemis à gauche et une zone de texte en bas affiche des informations sur les combattants..
Au début du combat, les images de monstre et de joueur sont ajoutées à la scène, puis il est décidé de l'ordre dans lequel les entités se relaient. Cette décision peut dépendre en partie de la manière dont le combat a été lancé: si le joueur est pris dans une embuscade, les monstres pourront tous attaquer en premier, sinon cela est généralement basé sur l'une des statistiques de l'entité telle que la vitesse..
Tout ce que le joueur ou les monstres font est une action: attaquer est une action, utiliser la magie est une action, même décider quelle action entreprendre est une action! L'ordre des actions est mieux suivi à l'aide d'une file d'attente. L'action au sommet est l'action qui aura lieu ensuite, à moins qu'aucune action plus rapide ne la prévienne. Chaque action aura un compte à rebours qui diminue au fur et à mesure que chaque image passe.
Le flux de combat est contrôlé à l'aide d'une machine à états à deux états; un état pour cocher les actions et un autre état pour exécuter la première action le moment venu. Comme toujours, le meilleur moyen de comprendre quelque chose est de regarder le code. L'exemple suivant implémente un état de combat de base avec une file d'attente d'actions:
Classe BattleState: IState ListeActions = Liste (); liste mEntities = List (); StateMachine mBattleStates = new StateMachine (); public static bool SortByTime (Action a, Action b) return a.TimeRemaining ()> b.TimeRemaining () public BattleState () mBattleStates.Add ("tick", nouveau BattleTick (mBattleStates, actions)); mBattleStates.Add ("execute", nouvelle BattleExecute (mBattleStates, mActions)); public void OnEnter (paramètres var) mBattleStates.Change ("tick"); // // Obtient une action de décision pour chaque entité de la file d'attente des actions // Le trie pour que les actions les plus rapides soient en haut de la page // mEntities = params.entities; foreach (entité e dans les entités) if (action.playerControlled) action PlayerDecide = new PlayerDecide (e, e.Speed ()); Actions.Add (action); else action AIDecide = new AIDecide (e, e.Speed ()); Actions.Add (action); Sort (actions, BattleState :: SortByTime); public void Update (float elapsedTime) mBattleStates.Update (elapsedTime); public void Render () // Dessine la scène, l'interface graphique, les personnages, les animations, etc. mBattleState.Render (); public void OnExit ()
Le code ci-dessus illustre le contrôle du flux en mode bataille à l'aide d'une machine à états simple et d'une file d'attente d'actions. Pour commencer, toutes les entités impliquées dans la bataille ont un décider-action ajouté à la file d'attente.
Une décision-action pour le joueur fera apparaître un menu avec les options stables RPG Attaque, la magie, et Article; une fois que le joueur a décidé d'une action, l'action-décision est retirée de la file d'attente et l'action nouvellement choisie est ajoutée.
Une action-décision pour l'IA inspectera la scène et décidera de la suite (en utilisant quelque chose comme un arbre de comportement, un arbre de décision ou une technique similaire), puis elle supprimera également son action-décision et ajoutera sa nouvelle action à la file d'attente..
le BattleTick
class contrôle la mise à jour des actions, comme indiqué ci-dessous:
Classe BattleTick: IState StateMachine mStateMachine; listedes actions; public BattleTick (StateMachine stateMachine, List actions): mStateMachine (stateMachine), Actions (action) // Il se peut que certaines fonctions ne se passent pas, mais rien ne nous intéresse. public void OnEnter () public void OnExit () public void Render () public void Update (float elapsedTime) foreach (action a dans les actions) a.Update (elapsedTime); if (mActions.Top (). IsReady ()) Action haut = Actions.Pop (); mStateMachine: Change ("execute", en haut);
BattleTick
est un sous-état de l'état BattleMode et il suffit de cocher jusqu'à ce que le compte à rebours de la première action soit à zéro. Ensuite, la première action est extraite de la file et les modifications apportées à la exécuter Etat.
Le diagramme ci-dessus montre une file d'attente d'actions au début d'une bataille. Personne n'a encore pris de décision et chacun prend son temps pour prendre une décision..
L’usine géante a un compte à rebours de 0, elle exécute donc son prochain tick AIDecide
action. Dans ce cas, le