Unity3D Design Patterns – State – Building a Unity3D State Machine

  • Accueil /
  • Unity3D
  • / Unity3D Design Patterns – State – Building a Unity3D State Machine
Par Jason Weimann /26 mai, 2017

Les patrons de conception sont des solutions réutilisables aux problèmes courants rencontrés dans les projets logiciels. L’utilisation de bons design patterns dans Unity peut aider à garder votre projet propre, maintenable et facile à étendre. Cela les rend également plus faciles à construire… Aujourd’hui, je vais couvrir le pattern d’état et les bases de la construction d’une machine d’état dans Unity3D.

Le pattern d’état est un pattern de conception logicielle comportementale qui implémente une machine d’état d’une manière orientée objet. (…) Ce motif est utilisé en programmation informatique pour encapsuler un comportement variable pour le même objet en fonction de son état interne.

Une implémentation que je n’aime pas..

Il y a quelques façons d’implémenter un motif d’état dans Unity. Une des méthodes communes que je vois est faite en utilisant l’instruction switch. Cela ressemble généralement à quelque chose comme ceci.

Problèmes

Bien que cela fonctionne techniquement, il y a quelques problèmes sérieux qui vont surgir au fil du temps.

Premièrement, toute notre logique est maintenant à l’intérieur de cette seule classe. L’attaque, la défense, le retour à la maison, et tout autre état que nous voulons créer doivent tous être ajoutés ici. Cela va conduire à une grande classe maîtresse gonflée pour notre IA. Cela rendra également notre code plus susceptible de se briser puisque nous éditons la même classe pour chaque type de logique. Avant longtemps, cette classe pourrait facilement se transformer en un désastre de plus de 1000 lignes.

Il n’y a également pas une grande façon de traiter les événements qui se produisent sur une entrée et une sortie d’état. Très souvent, nous voulons qu’un personnage ou un objet fasse quelque chose quand il entre dans un état, répète une action pendant qu’il est dans cet état, et fasse quelque chose de totalement différent quand il quitte l’état.

Avec cette configuration, nous devrions ajouter plus de commutateurs pour l’entrée et la sortie d’état, ce qui gonfle encore plus la classe et rend tout plus difficile à comprendre.

Une meilleure implémentation

Une meilleure approche est que chaque état soit sa propre classe, généralement avec une classe de base ‘state’ dont ils héritent.

De cette façon, le personnage ou l’objet a seulement besoin d’avoir une référence à son état actuel et un appel pour faire la mise à jour de cet état. Lorsque nous voulons ajouter un nouvel état, nous créons simplement une nouvelle classe.

En utilisant cette méthode, notre classe de personnage a l’air un peu différente:

Vous pouvez remarquer que les classes ont à peu près la même taille… mais c’est seulement parce que la première version ne fait en fait rien (principalement pour rester digeste). Si cette première classe faisait tout ce que le système que je vais vous montrer fait, elle serait énorme.

Comment ça marche?

Si vous regardez la méthode Update, vous verrez qu’elle appelle simplement la méthode Tick() sur l’état actuel. Avec cet appel, l’état contrôle ce que le personnage fera à chaque image.

Nous avons également une méthode SetState qui nous permet de passer dans un état que nous voulons que le personnage entre. En appelant SetState et en passant dans différents états, nous pouvons changer complètement le comportement de ce personnage, sans changer quoi que ce soit dans la classe de personnage elle-même.

Selon votre configuration, il peut être conseillé de garder des états autour et de passer de l’un à l’autre pour éviter la collecte de déchets. Pour garder cela facile à comprendre, j’ai sauté cela, mais cela vaut la peine de garder à l’esprit si vos changements d’état sont rapides et si vous êtes sur une plate-forme où cela compte.

Qu’est-ce que la classe State?

La classe State est une classe de base abstraite que nous utiliserons pour tous les états que nous créons. Elle définit un contrat très basique pour ce qu’un état doit implémenter et d’autres choses qu’il peut optionnellement implémenter.

La référence au caractère est également ajoutée à cette classe de base d’état, parce que tous nos états dans cet exemple s’interfaceront avec un caractère d’une certaine manière. Nous prenons le caractère dans le constructeur de notre classe d’état et le gardons dans une variable protégée afin qu’il soit disponible pour nos classes d’état.

Nous avons également la méthode abstraite Tick(). La rendre abstraite signifie que nos classes d’état sont obligées de l’implémenter.

En revanche, nous avons deux méthodes virtuelles pour OnStateEnter() &OnStateExit() qui sont facultatives. Parce qu’elles sont virtuelles, elles ont une implémentation par défaut qui ne fait rien, et ne sont pas tenues d’être implémentées.

C’est l’intégralité de la classe d’état… assez simple non ?

Créer un état – Retour à la maison

Maintenant que nous avons les bases, créons une implémentation d’un état que nous pouvons accrocher à notre personnage.

L’état de retour à la maison fait simplement revenir le personnage à son emplacement de départ. Puisque nous ne définissons pas réellement la destination pour home, c’est le Vector3.zero par défaut.

Tick

La méthode Tick indique au personnage de se déplacer vers la destination (0, 0, 0).

La méthode MoveToward est sur une version du personnage à la fin de ce post. Dans un vrai projet, ce mouvement serait probablement contrôlé par un autre composant comme une classe ‘CharacterMovement’ ou quelque chose d’autre.

Puis elle vérifie si le personnage est arrivé à la maison, et si oui, elle dit au personnage de passer à un état ‘wander’. Remarquez que nous passons dans le personnage lors de la création du nouvel état. C’est parce que notre état est conçu pour nécessiter un personnage.

EnterState

Nous changeons également la couleur du personnage lorsque l’état de retour à la maison est entré via OnStateEnter. Donc, comme un personnage rentre à la maison, il est bleu.

Création d’un autre état – Wander

Pour qu’une machine à états ait un sens, nous avons besoin de plus d’un état. En fait, notre état de retour à la maison veut en fait basculer le personnage dans un état d’errance une fois qu’il a terminé, donc nous devons créer cet état d’errance.

L’état d’errance va choisir un emplacement aléatoire et déplacer le personnage vers cette position. Lorsqu’il atteindra cette position, il choisira un autre endroit pour se balader.

Il continuera à faire cela jusqu’à ce qu’il se soit baladé pendant 5 secondes (en fonction de notre variable wanderTime). A ce moment-là, il dira au personnage de retourner à un état ReturnHome (ligne 43).

L’état d’errance change également la couleur de notre personnage en vert via la méthode OnStateEnter.

Voyons à quoi cela ressemble

Ici, j’ai créé une scène avec une douzaine de ‘personnages’ représentés par des cubes.

Vous les verrez se promener pendant qu’ils sont verts, puis retourner à la maison quand ils sont bleus, et répéter le processus.

Le personnage complet

J’ai mentionné plus haut que la classe de personnage complète est ici avec la méthode MoveToward. Encore une fois, dans un projet réel, je recommanderais de garder votre mouvement de base séparé dans un autre composant, mais pour garder cela simple, le voici dans la classe de personnage.

Conclusions

Le pattern d’état est étonnamment puissant. Vous pouvez construire des systèmes d’IA, des menus et plus encore avec facilité une fois que vous êtes à l’aise avec ce pattern.

Cette machine d’état est également très basique et peut être étendue pour faire des choses comme la mise en cache des états (pour éviter de nouvelles allocations de mémoire) ou personnalisée plus selon vos besoins.

Un autre excellent moyen de construire des machines d’état est via scriptableobjects. Une configuration de machine d’état utilisant scriptableobjects peut être facilement configurée dans l’éditeur/inspecteur par les concepteurs sans jamais toucher au code, mais c’est un sujet un peu plus compliqué dans lequel je peux plonger plus tard.

La prochaine fois que vous devez construire une petite logique de jeu, essayez de configurer une machine d’état de votre propre (ou copiez celle-ci comme point de départ), je suis sûr que cela vous aidera et que vous apprécierez le nouveau motif.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.