Unity avec MVC : comment améliorer le développement de votre jeu

Publié: 2022-03-11

Les programmeurs débutants commencent généralement à apprendre le métier avec le programme classique Hello World . À partir de là, des missions de plus en plus importantes suivront. Chaque nouveau défi apporte une leçon importante :

Plus le projet est grand, plus les spaghettis sont gros.

Bientôt, il est facile de voir que dans les grandes ou les petites équipes, on ne peut pas faire ce qu'on veut avec imprudence. Le code doit être maintenu et peut durer longtemps. Les entreprises pour lesquelles vous avez travaillé ne peuvent pas simplement rechercher vos coordonnées et vous demander chaque fois qu'elles souhaitent corriger ou améliorer la base de code (et vous ne le souhaitez pas non plus).

C'est pourquoi les modèles de conception de logiciels existent ; ils imposent des règles simples pour dicter la structure globale d'un projet logiciel. Ils aident un ou plusieurs programmeurs à séparer les éléments centraux d'un grand projet et à les organiser de manière standardisée, éliminant ainsi la confusion lorsqu'une partie inconnue de la base de code est rencontrée.

Ces règles, lorsqu'elles sont suivies par tout le monde, permettent de mieux gérer et parcourir le code hérité, et d'ajouter plus rapidement du nouveau code. Moins de temps est passé à planifier la méthodologie de développement. Étant donné que les problèmes ne sont pas uniques, il n'existe pas de modèle de conception miracle. Il faut soigneusement considérer les points forts et les points faibles de chaque modèle, et trouver le meilleur ajustement pour le défi à relever.

Dans ce didacticiel, je vais relater mon expérience avec la populaire plate-forme de développement de jeux Unity et le modèle Model-View-Controller (MVC) pour le développement de jeux. Au cours de mes sept années de développement, après avoir lutté avec ma juste part de spaghettis de développement de jeux, j'ai atteint une excellente structure de code et une grande vitesse de développement en utilisant ce modèle de conception.

Je commencerai par expliquer un peu l'architecture de base de Unity, le modèle Entity-Component. Ensuite, je vais expliquer comment MVC s'y intègre et utiliser un petit projet fictif comme exemple.

Motivation

Dans la littérature des logiciels, on trouvera un grand nombre de patrons de conception. Même s'ils ont un ensemble de règles, les développeurs font généralement un peu de contournement des règles afin de mieux adapter le modèle à leur problème spécifique.

Cette « liberté de programmation » est la preuve que nous n'avons pas encore trouvé de méthode unique et définitive pour concevoir des logiciels. Ainsi, cet article n'est pas destiné à être la solution ultime à votre problème, mais plutôt à montrer les avantages et les possibilités de deux modèles bien connus : Entity-Component et Model-View-Controller.

Le modèle entité-composant

Entity-Component (EC) est un modèle de conception où nous définissons d'abord la hiérarchie des éléments qui composent l'application (Entités), et plus tard, nous définissons les fonctionnalités et les données que chacun contiendra (Composants). En termes plus "programmeurs", une entité peut être un objet avec un tableau de 0 ou plusieurs composants. Représentons une entité comme ceci :

 some-entity [component0, component1, ...]

Voici un exemple simple d'arbre EC.

 - app [Application] - game [Game] - player [KeyboardInput, Renderer] - enemies - spider [SpiderAI, Renderer] - ogre [OgreAI, Renderer] - ui [UI] - hud [HUD, MouseInput, Renderer] - pause-menu [PauseMenu, MouseInput, Renderer] - victory-modal [VictoryModal, MouseInput, Renderer] - defeat-modal [DefeatModal, MouseInput, Renderer]

EC est un bon modèle pour atténuer les problèmes d'héritage multiple, où une structure de classe complexe peut introduire des problèmes comme le problème du diamant où une classe D, héritant de deux classes, B et C, avec la même classe de base A, peut introduire des conflits car comment B et C modifient différemment les caractéristiques de A.

IMAGE : PROBLÈME DE DIAMANT

Ces types de problèmes peuvent être courants dans le développement de jeux où l'héritage est souvent largement utilisé.

En décomposant les fonctionnalités et les gestionnaires de données en composants plus petits, ils peuvent être attachés et réutilisés dans différentes entités sans s'appuyer sur plusieurs héritages (ce qui, soit dit en passant, n'est même pas une fonctionnalité de C # ou Javascript, les principaux langages utilisés par Unity ).

Là où l'entité-composant est insuffisante

Étant un niveau au-dessus de la POO, EC aide à défragmenter et à mieux organiser votre architecture de code. Cependant, dans les grands projets, nous sommes encore "trop ​​libres" et nous pouvons nous retrouver dans un "océan de fonctionnalités", avoir du mal à trouver les bonnes entités et composants, ou à comprendre comment ils doivent interagir. Il existe une infinité de façons d'assembler des entités et des composants pour une tâche donnée.

IMAGE: EC FEATURE OCÉAN

Une façon d'éviter un gâchis est d'imposer des directives supplémentaires au-dessus de Entity-Component. Par exemple, une façon dont j'aime penser au logiciel est de le diviser en trois catégories différentes :

  • Certains manipulent les données brutes, leur permettant d'être créées, lues, mises à jour, supprimées ou recherchées (c'est-à-dire le concept CRUD).
  • D'autres implémentent l'interface avec laquelle d'autres éléments peuvent interagir, détectant les événements liés à leur portée et déclenchant des notifications lorsqu'ils se produisent.
  • Enfin, certains éléments sont chargés de recevoir ces notifications, de prendre des décisions de logique métier et de décider comment les données doivent être manipulées.

Heureusement, nous avons déjà un modèle qui se comporte exactement de cette manière.

Le modèle modèle-vue-contrôleur (MVC)

Le modèle Modèle-Vue-Contrôleur (MVC) divise le logiciel en trois composants principaux : Modèles (Data CRUD), Vues (Interface/Détection) et Contrôleurs (Décision/Action). MVC est suffisamment flexible pour être implémenté même au-dessus d'ECS ou d'OOP.

Le développement de jeux et d'interfaces utilisateur suit le flux de travail habituel consistant à attendre l'entrée d'un utilisateur ou une autre condition de déclenchement, à envoyer une notification de ces événements à un endroit approprié, à décider quoi faire en réponse et à mettre à jour les données en conséquence. Ces actions montrent clairement la compatibilité de ces applications avec MVC.

Cette méthodologie introduit une autre couche d'abstraction qui aidera à la planification du logiciel et permettra également aux nouveaux programmeurs de naviguer même dans une base de code plus grande. En divisant le processus de réflexion en données, interface et décisions, les développeurs peuvent réduire le nombre de fichiers source à rechercher pour ajouter ou corriger des fonctionnalités.

Unité et CE

Examinons d'abord de plus près ce que Unity nous offre en amont.

Unity est une plate-forme de développement basée sur EC, où toutes les entités sont des instances de GameObject et les fonctionnalités qui les rendent « visibles », « mobiles », « interactives », etc., sont fournies par des classes étendant Component .

Le panneau de hiérarchie et le panneau d' inspecteur de l'éditeur Unity offrent un moyen puissant d'assembler votre application, d'attacher des composants, de configurer leur état initial et de démarrer votre jeu avec beaucoup moins de code source qu'il ne le ferait normalement.

CAPTURE D'ÉCRAN : PANNEAU HIÉRARCHIQUE
Panneau Hiérarchie avec quatre GameObjects sur la droite

CAPTURE D'ÉCRAN : PANNEAU D'INSPECTION
Panneau d'inspection avec les composants d'un GameObject

Pourtant, comme nous en avons discuté, nous pouvons rencontrer le problème du "trop ​​de fonctionnalités" et nous retrouver dans une hiérarchie gigantesque, avec des fonctionnalités éparpillées partout, rendant la vie d'un développeur beaucoup plus difficile.

En pensant à la manière MVC, nous pouvons plutôt commencer par diviser les choses selon leur fonction, en structurant notre application comme dans l'exemple ci-dessous :

CAPTURE D'ÉCRAN : EXEMPLE DE STRUCTURE UNITY MVC

Adapter MVC à un environnement de développement de jeux

Maintenant, je voudrais introduire deux petites modifications au modèle MVC générique, qui aident à l'adapter aux situations uniques que j'ai rencontrées lors de la construction de projets Unity avec MVC :

  1. Les références de classe MVC sont facilement dispersées dans le code. - Dans Unity, les développeurs doivent généralement faire glisser et déposer des instances pour les rendre accessibles, ou bien les atteindre via des instructions de recherche encombrantes telles que GetComponent( ... ) . - L'enfer des références perdues s'ensuivra si Unity plante ou si un bogue fait disparaître toutes les références déplacées. - Cela rend nécessaire d'avoir un seul objet de référence racine, à travers lequel toutes les instances de l' application peuvent être atteintes et récupérées.
  2. Certains éléments encapsulent des fonctionnalités générales qui devraient être hautement réutilisables et qui ne relèvent pas naturellement de l'une des trois principales catégories de modèle, vue ou contrôleur. J'aime les appeler simplement Composants . Ce sont également des « composants » au sens d'entité-composant, mais ils agissent simplement comme des assistants dans le cadre MVC. - Par exemple, un composant Rotator , qui ne fait tourner les choses que d'une vitesse angulaire donnée et ne notifie, ne stocke ni ne décide quoi que ce soit.

Pour aider à résoudre ces deux problèmes, j'ai proposé un modèle modifié que j'appelle AMVCC , ou Application-Model-View-Controller-Component.

IMAGE : SCHÉMA AMVCC

  • Application - Point d'entrée unique vers votre application et conteneur de toutes les instances critiques et des données liées à l'application.
  • MVC - Vous devriez le savoir maintenant. :)
  • Composant - Petit script bien contenu pouvant être réutilisé.

Ces deux modifications ont satisfait mes besoins pour tous les projets dans lesquels je les ai utilisées.

Exemple : 10 rebonds

Comme exemple simple, regardons un petit jeu appelé 10 Bounces , où j'utiliserai les éléments de base du modèle AMVCC.

La configuration du jeu est simple : Une Ball avec un SphereCollider et un Rigidbody (qui commencera à tomber après "Play"), un Cube comme sol et 5 scripts pour constituer l'AMVCC.

Hiérarchie

Avant de rédiger des scripts, je commence généralement par la hiérarchie et crée un aperçu de ma classe et de mes actifs. Toujours en suivant ce nouveau style AMVCC.

CAPTURE D'ÉCRAN : CONSTRUIRE LA HIÉRARCHIE

Comme nous pouvons le voir, la view GameObject contient tous les éléments visuels ainsi que ceux avec d'autres scripts View . Le model et controller GameObjects, pour les petits projets, ne contiennent généralement que leurs scripts respectifs. Pour les projets plus importants, ils contiendront des GameObjects avec des scripts plus spécifiques.

Lorsqu'une personne naviguant dans votre projet souhaite accéder :

  • Données : accédez à l' application > model > ...
  • Logique/Workflow : Accédez à l' application > controller > ...
  • Rendu/Interface/Détection : Allez dans application > view > ...

Si toutes les équipes suivent ces règles simples, les projets hérités ne devraient pas devenir un problème.

Notez qu'il n'y a pas de conteneur de Component car, comme nous l'avons vu, ils sont plus flexibles et peuvent être attachés à différents éléments au gré du développeur.

Script

Remarque : Les scripts présentés ci-dessous sont des versions abstraites d'implémentations réelles. Une implémentation détaillée ne profiterait pas beaucoup au lecteur. Cependant, si vous souhaitez en savoir plus, voici le lien vers mon framework MVC personnel pour Unity, Unity MVC. Vous trouverez des classes de base qui implémentent le cadre structurel AMVCC nécessaire à la plupart des applications.

Examinons la structure des scripts pour 10 Bounces .

Avant de commencer, pour ceux qui ne sont pas familiers avec le flux de travail d'Unity, clarifions brièvement comment les scripts et les GameObjects fonctionnent ensemble. Dans Unity, les "Composants", au sens Entité-Composant, sont représentés par la classe MonoBehaviour . Pour qu'un existe pendant l'exécution, le développeur doit soit faire glisser et déposer son fichier source dans un GameObject (qui est l'"Entité" du modèle Entity-Component) ou utiliser la commande AddComponent<YourMonobehaviour>() . Après cela, le script sera instancié et prêt à être utilisé lors de l'exécution.

Pour commencer, nous définissons la classe Application (le "A" dans AMVCC), qui sera la classe principale contenant les références à tous les éléments de jeu instanciés. Nous allons également créer une classe de base d'assistance appelée Element , qui nous donne accès à l'instance de Application et aux instances MVC de ses enfants.

Dans cet esprit, définissons la classe Application (le "A" dans AMVCC), qui aura une instance unique. À l'intérieur, trois variables, model , view et controller , nous donneront des points d'accès pour toutes les instances MVC pendant l'exécution. Ces variables doivent être MonoBehaviour avec des références public aux scripts souhaités.

Ensuite, nous allons également créer une classe de base d'assistance appelée Element , qui nous donne accès à l'instance de Application. Cet accès permettra à chaque classe MVC d'atteindre les autres.

Notez que les deux classes étendent MonoBehaviour . Ce sont des "Composants" qui seront attachés aux "Entités" de GameObject.

 // BounceApplication.cs // Base class for all elements in this application. public class BounceElement : MonoBehaviour { // Gives access to the application and all instances. public BounceApplication app { get { return GameObject.FindObjectOfType<BounceApplication>(); }} } // 10 Bounces Entry Point. public class BounceApplication : MonoBehaviour { // Reference to the root instances of the MVC. public BounceModel model; public BounceView view; public BounceController controller; // Init things here void Start() { } }

À partir de BounceElement nous pouvons créer les classes de base MVC. Les BounceModel , BounceView et BounceController agissent généralement comme des conteneurs pour des instances plus spécialisées, mais comme il s'agit d'un exemple simple, seule la vue aura une structure imbriquée. Le modèle et le contrôleur peuvent être créés dans un seul script pour chacun :

 // BounceModel.cs // Contains all data related to the app. public class BounceModel : BounceElement { // Data public int bounces; public int winCondition; }
 // BounceView .cs // Contains all views related to the app. public class BounceView : BounceElement { // Reference to the ball public BallView ball; }
 // BallView.cs // Describes the Ball view and its features. public class BallView : BounceElement { // Only this is necessary. Physics is doing the rest of work. // Callback called upon collision. void OnCollisionEnter() { app.controller.OnBallGroundHit(); } }
 // BounceController.cs // Controls the app workflow. public class BounceController : BounceElement { // Handles the ball hit event public void OnBallGroundHit() { app.model.bounces++; Debug.Log(“Bounce ”+app.model.bounce); if(app.model.bounces >= app.model.winCondition) { app.view.ball.enabled = false; app.view.ball.GetComponent<RigidBody>().isKinematic=true; // stops the ball OnGameComplete(); } } // Handles the win condition public void OnGameComplete() { Debug.Log(“Victory!!”); } }

Une fois tous les scripts créés, nous pouvons procéder à leur attachement et à leur configuration.

La disposition de la hiérarchie devrait ressembler à ceci :

 - application [BounceApplication] - model [BounceModel] - controller [BounceController] - view [BounceView] - ... - ball [BallView] - ...

En utilisant le BounceModel comme exemple, nous pouvons voir à quoi il ressemble dans l'éditeur de Unity :

CAPTURE D'ÉCRAN : BounceModel DANS INSPECTEUR
BounceModel avec les champs bounces et winCondition .

Avec tous les scripts définis et le jeu en cours d'exécution, nous devrions obtenir cette sortie dans le panneau de la console .

CAPTURE D'ÉCRAN : SORTIE DE LA CONSOLE

Avis

Comme le montre l'exemple ci-dessus, lorsque la balle touche le sol, sa vue exécute app.controller.OnBallGroundHit() qui est une méthode. Ce n'est en aucun cas "mal" de le faire pour toutes les notifications de l'application. Cependant, d'après mon expérience, j'ai obtenu de meilleurs résultats en utilisant un système de notification simple implémenté dans la classe Application AMVCC.

Pour implémenter cela, mettons à jour la mise en page de BounceApplication pour qu'elle soit :

 // BounceApplication.cs class BounceApplication { // Iterates all Controllers and delegates the notification data // This method can easily be found because every class is “BounceElement” and has an “app” // instance. public void Notify(string p_event_path, Object p_target, params object[] p_data) { BounceController[] controller_list = GetAllControllers(); foreach(BounceController c in controller_list) { c.OnNotification(p_event_path,p_target,p_data); } } // Fetches all scene Controllers. public BounceController[] GetAllControllers() { /* ... */ } }

Ensuite, nous avons besoin d'un nouveau script où tous les développeurs ajouteront les noms de l'événement de notification, qui peuvent être envoyés lors de l'exécution.

 // BounceNotifications.cs // This class will give static access to the events strings. class BounceNotification { static public string BallHitGround = “ball.hit.ground”; static public string GameComplete = “game.complete”; /* ... */ static public string GameStart = “game.start”; static public string SceneLoad = “scene.load”; /* ... */ }

Il est facile de voir que, de cette façon, la lisibilité du code est améliorée car les développeurs n'ont pas besoin de rechercher partout dans le code source les méthodes controller.OnSomethingComplexName afin de comprendre quel type d'actions peuvent se produire pendant l'exécution. En ne vérifiant qu'un seul fichier, il est possible de comprendre le comportement global de l'application.

Maintenant, nous n'avons plus qu'à adapter BallView et BounceController pour gérer ce nouveau système.

 // BallView.cs // Describes the Ball view and its features. public class BallView : BounceElement { // Only this is necessary. Physics is doing the rest of work. // Callback called upon collision. void OnCollisionEnter() { app.Notify(BounceNotification.BallHitGround,this); } }
 // BounceController.cs // Controls the app workflow. public class BounceController : BounceElement { // Handles the ball hit event public void OnNotification(string p_event_path,Object p_target,params object[] p_data) { switch(p_event_path) { case BounceNotification.BallHitGround: app.model.bounces++; Debug.Log(“Bounce ”+app.model.bounce); if(app.model.bounces >= app.model.winCondition) { app.view.ball.enabled = false; app.view.ball.GetComponent<RigidBody>().isKinematic=true; // stops the ball // Notify itself and other controllers possibly interested in the event app.Notify(BounceNotification.GameComplete,this); } break; case BounceNotification.GameComplete: Debug.Log(“Victory!!”); break; } } }

Les projets plus importants auront beaucoup de notifications. Ainsi, pour éviter d'avoir une grande structure de cas de commutation, il est conseillé de créer différents contrôleurs et de leur faire gérer différentes portées de notification.

AMVCC dans le monde réel

Cet exemple a montré un cas d'utilisation simple pour le modèle AMVCC. Ajuster votre façon de penser en fonction des trois éléments de MVC, et apprendre à visualiser les entités comme une hiérarchie ordonnée, sont les compétences qui doivent être peaufinées.

Dans les projets plus importants, les développeurs seront confrontés à des scénarios plus complexes et à des doutes quant à savoir si quelque chose doit être une vue ou un contrôleur, ou si une classe donnée doit être plus complètement séparée en plus petites.

Règles empiriques (par Eduardo)

Il n'y a pas de "Guide universel pour le tri MVC" nulle part. Mais il y a quelques règles simples que je suis généralement pour m'aider à déterminer s'il faut définir quelque chose comme un modèle, une vue ou un contrôleur et aussi quand diviser une classe donnée en plus petits morceaux.

Habituellement, cela se produit de manière organique pendant que je réfléchis à l'architecture du logiciel ou pendant le script.

Tri des classes

Des modèles

  • Conservez les données de base et l'état de l'application, telles que health du joueur ou les ammo d'armes à feu.
  • Sérialiser, désérialiser et/ou convertir entre types.
  • Charger/sauvegarder des données (localement ou sur le Web).
  • Informer les contrôleurs de l'avancement des opérations.
  • Enregistrez l'état du jeu pour la machine à états finis du jeu.
  • N'accédez jamais aux vues.

Vues

  • Peut obtenir des données à partir de modèles afin de représenter l'état du jeu à jour pour l'utilisateur. Par exemple, une méthode View player.Run() peut utiliser en interne model.speed pour manifester les capacités du joueur.
  • Ne devrait jamais muter les modèles.
  • Implémente strictement les fonctionnalités de sa classe. Par exemple:
    • Un PlayerView ne doit pas implémenter la détection d'entrée ni modifier l'état du jeu.
    • Une vue doit agir comme une boîte noire dotée d'une interface et notifiant les événements importants.
    • Ne stocke pas les données de base (comme la vitesse, la santé, les vies,…).

Contrôleurs

  • Ne stockez pas les données de base.
  • Peut parfois filtrer les notifications des vues indésirables.
  • Mettre à jour et utiliser les données du modèle.
  • Gère le workflow de scène d'Unity.

Hiérarchie des classes

Dans ce cas, il n'y a pas beaucoup d'étapes à suivre. Habituellement, je perçois que certaines classes doivent être divisées lorsque les variables commencent à afficher trop de "préfixes" ou que trop de variantes du même élément commencent à apparaître (comme les classes de Player dans un MMO ou les types d' Gun à feu dans un FPS).

Par exemple, un seul Model contenant les données du joueur aurait beaucoup de playerDataA, playerDataB playerDataA, playerDataB,... ou un Controller gérant les notifications du joueur aurait OnPlayerDidA,OnPlayerDidB,... . Nous voulons réduire la taille du script et supprimer les préfixes de player et OnPlayer .

Permettez-moi de démontrer l'utilisation d'une classe Model car il est plus simple à comprendre en utilisant uniquement des données.

Lors de la programmation, je commence généralement avec une seule classe Model contenant toutes les données du jeu.

 // Model.cs class Model { public float playerHealth; public int playerLives; public GameObject playerGunPrefabA; public int playerGunAmmoA; public GameObject playerGunPrefabB; public int playerGunAmmoB; // Ops Gun[CDE ...] will appear... /* ... */ public float gameSpeed; public int gameLevel; }

Il est facile de voir que plus le jeu est complexe, plus les variables seront nombreuses. Avec suffisamment de complexité, nous pourrions nous retrouver avec une classe géante contenant des variables model.playerABCDFoo . L'imbrication des éléments simplifiera la complétion du code et donnera également de la place pour basculer entre les variations de données.

 // Model.cs class Model { public PlayerModel player; // Container of the Player data. public GameModel game; // Container of the Game data. }
 // GameModel.cs class GameModel { public float speed; // Game running speed (influencing the difficulty) public int level; // Current game level/stage loaded }
 // PlayerModel.cs class PlayerModel { public float health; // Player health from 0.0 to 1.0. public int lives; // Player “retry” count after he dies. public GunModel[] guns; // Now a Player can have an array of guns to switch ingame. }
 // GunModel.cs class GunModel { public GunType type; // Enumeration of Gun types. public GameObject prefab; // Template of the 3D Asset of the weapon. public int ammo; // Current number of bullets public int clips; // Number of reloads possible }

Avec cette configuration de classes, les développeurs peuvent naviguer intuitivement dans le code source un concept à la fois. Supposons un jeu de tir à la première personne, où les armes et leurs configurations peuvent devenir très nombreuses. Le fait que GunModel soit contenu dans une classe permet la création d'une liste de Prefabs (GameObjects préconfigurés pour être rapidement dupliqués et réutilisés dans le jeu) pour chaque catégorie et stockés pour une utilisation ultérieure.

En revanche, si les informations sur les armes à feu étaient toutes stockées ensemble dans la seule classe GunModel , dans des variables telles que gun0Ammo , gun1Ammo , gun0Clips , etc., l'utilisateur, confronté à la nécessité de stocker les données Gun , aurait besoin de stocker l'ensemble Model incluant les données Player indésirables. Dans ce cas, il serait évident qu'une nouvelle classe GunModel serait préférable.

IMAGE : HIÉRARCHIE DES CLASSES
Amélioration de la hiérarchie des classes.

Comme pour tout, il y a deux faces de la médaille. Parfois, on peut inutilement trop compartimenter et augmenter la complexité du code. Seule l'expérience peut affiner suffisamment vos compétences pour trouver le meilleur tri MVC pour votre projet.

Nouveau développeur de jeu Capacité spéciale déverrouillée : jeux Unity avec le modèle MVC.
Tweeter

Conclusion

Il existe des tonnes de modèles de logiciels. Dans cet article, j'ai essayé de montrer celui qui m'a le plus aidé dans les projets passés. Les développeurs doivent toujours absorber de nouvelles connaissances, mais aussi toujours les remettre en question. J'espère que ce didacticiel vous aidera à apprendre quelque chose de nouveau et, en même temps, vous servira de tremplin pour développer votre propre style.

De plus, je vous encourage vraiment à rechercher d'autres modèles et à trouver celui qui vous convient le mieux. Un bon point de départ est cet article de Wikipedia, avec son excellente liste de motifs et leurs caractéristiques.

Si vous aimez le modèle AMVCC et souhaitez le tester, n'oubliez pas d'essayer ma bibliothèque, Unity MVC , qui contient toutes les classes de base nécessaires pour démarrer une application AMVCC.


Lectures complémentaires sur le blog Toptal Engineering :

  • Développement Unity AI : un didacticiel sur les machines à états finis