Unitate cu MVC: Cum să-ți ridici nivelul de dezvoltare a jocului

Publicat: 2022-03-11

Programatorii începători încep de obicei să învețe meseria cu programul clasic Hello World . De acolo, trebuie să urmeze sarcini din ce în ce mai mari. Fiecare nouă provocare aduce acasă o lecție importantă:

Cu cât proiectul este mai mare, cu atât spaghetele sunt mai mari.

În curând, este ușor de observat că, în echipe mari sau mici, nu se poate face cu nesăbuință ce dorește. Codul trebuie menținut și poate dura mult timp. Companiile pentru care ai lucrat nu pot doar să caute informațiile tale de contact și să te întrebe de fiecare dată când doresc să repare sau să îmbunătățească baza de cod (și nici tu nu vrei să o facă).

Acesta este motivul pentru care există modele de proiectare software; impun reguli simple pentru a dicta structura de ansamblu a unui proiect software. Ele ajută unul sau mai mulți programatori să separe piesele de bază ale unui proiect mare și să le organizeze într-un mod standardizat, eliminând confuzia atunci când se întâlnește o parte necunoscută a bazei de cod.

Aceste reguli, atunci când sunt respectate de toată lumea, permit ca codul vechi să fie mai bine întreținut și navigat, iar codul nou să fie adăugat mai rapid. Se petrece mai puțin timp pentru planificarea metodologiei de dezvoltare. Deoarece problemele nu vin într-o singură aromă, nu există un model de design cu glonț de argint. Trebuie să luați în considerare cu atenție punctele tari și slabe ale fiecărui tipar și să găsiți cel mai potrivit pentru provocarea la îndemână.

În acest tutorial, voi relata experiența mea cu populara platformă de dezvoltare a jocurilor Unity și modelul Model-View-Controller (MVC) pentru dezvoltarea jocului. În cei șapte ani de dezvoltare ai mei, după ce m-am luptat cu partea echitabilă de spaghete pentru dezvoltatori de jocuri, am obținut o structură de cod și o viteză de dezvoltare grozave folosind acest model de design.

Voi începe prin a explica un pic din arhitectura de bază a Unity, modelul Entity-Component. Apoi voi continua să explic cum se potrivește MVC peste el și voi folosi un mic proiect simulat ca exemplu.

Motivația

În literatura de software, vom găsi un număr mare de modele de design. Chiar dacă au un set de reguli, dezvoltatorii vor face de obicei puțină îndoire a regulilor pentru a adapta mai bine modelul la problema lor specifică.

Această „libertate de programare” este dovada că nu am găsit încă o metodă unică și definitivă pentru proiectarea software-ului. Astfel, acest articol nu este menit să fie soluția finală la problema dvs., ci mai degrabă să arate beneficiile și posibilitățile a două modele binecunoscute: Entity-Component și Model-View-Controller.

Modelul entitate-componentă

Entity-Component (EC) este un model de design în care definim mai întâi ierarhia elementelor care alcătuiesc aplicația (Entities), iar mai târziu, definim caracteristicile și datele pe care le va conține fiecare (Componente). În termeni mai „programatori”, o Entitate poate fi un obiect cu o matrice de 0 sau mai multe Componente. Să descriem o entitate astfel:

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

Iată un exemplu simplu de arbore 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 este un model bun pentru atenuarea problemelor de moștenire multiplă, în care o structură de clasă complexă poate introduce probleme precum problema diamant în care o clasă D, care moștenește două clase, B și C, cu aceeași clasă de bază A, poate introduce conflicte, deoarece B și C modifică caracteristicile lui A în mod diferit.

IMAGINE: PROBLEMA DIAMANTULUI

Aceste tipuri de probleme pot fi comune în dezvoltarea jocurilor, unde moștenirea este adesea folosită pe scară largă.

Prin împărțirea caracteristicilor și a gestionarilor de date în Componente mai mici, acestea pot fi atașate și reutilizate în diferite entități fără a se baza pe moșteniri multiple (care, apropo, nici măcar nu este o caracteristică a C# sau Javascript, principalele limbaje folosite de Unity ).

Acolo unde Entitatea-Componenta este scurtă

Fiind cu un nivel peste OOP, EC ajută la defragmentarea și la organizarea mai bună a arhitecturii codului. Cu toate acestea, în proiectele mari, suntem încă „prea liberi” și ne putem găsi într-un „ocean de caracteristici”, având dificultăți în a găsi Entitățile și Componentele potrivite sau ne dăm seama cum ar trebui să interacționeze. Există moduri infinite de a asambla Entități și Componente pentru o anumită sarcină.

IMAGINE: EC FEATURE OCEAN

O modalitate de a evita dezordinea este să impuneți câteva linii directoare suplimentare pe partea de sus a Entității-Componentă. De exemplu, un mod în care îmi place să mă gândesc la software este să-l împart în trei categorii diferite:

  • Unii gestionează datele brute, permițând să fie create, citite, actualizate, șterse sau căutate (adică, conceptul CRUD).
  • Alții implementează interfața pentru alte elemente cu care să interacționeze, detectând evenimente legate de domeniul lor și declanșând notificări atunci când apar.
  • În cele din urmă, unele elemente sunt responsabile pentru primirea acestor notificări, luarea deciziilor logice de afaceri și deciderea modului în care datele ar trebui să fie manipulate.

Din fericire, avem deja un model care se comportă exact în acest fel.

Modelul Model-View-Controller (MVC).

Modelul Model-View-Controller (MVC) împarte software-ul în trei componente majore: Modele (Data CRUD), Vizualizări (Interfață/Detecție) și Controlere (Decizie/Acțiune). MVC este suficient de flexibil pentru a fi implementat chiar și peste ECS sau OOP.

Dezvoltarea jocului și a interfeței de utilizare au fluxul de lucru obișnuit de așteptare a intrării unui utilizator sau a unei alte condiții de declanșare, trimiterea unei notificări cu privire la aceste evenimente undeva potrivit, deciderea ce să facă ca răspuns și actualizarea datelor în consecință. Aceste acțiuni arată clar compatibilitatea acestor aplicații cu MVC.

Această metodologie introduce un alt strat de abstractizare care va ajuta la planificarea software-ului și, de asemenea, le va permite noilor programatori să navigheze chiar și într-o bază de cod mai mare. Prin împărțirea procesului de gândire în date, interfață și decizii, dezvoltatorii pot reduce numărul de fișiere sursă care trebuie căutate pentru a adăuga sau a remedia funcționalități.

Unitate și CE

Să aruncăm mai întâi o privire mai atentă la ceea ce ne oferă Unity în față.

Unity este o platformă de dezvoltare bazată pe CE, în care toate Entitățile sunt instanțe ale GameObject și caracteristicile care le fac să fie „vizibile”, „deplasabile”, „interactable” și așa mai departe, sunt furnizate de clasele care extind Component .

Panoul ierarhic al editorului Unity și Panoul de inspecție oferă o modalitate puternică de a vă asambla aplicația, de a atașa Componente, de a configura starea lor inițială și de a porni jocul cu mult mai puțin cod sursă decât ar fi în mod normal.

CAPTURA DE ECRAN: PANOUL IERARHIE
Panoul ierarhic cu patru GameObjects în dreapta

CAPTURA DE ECRAN: PANOUL INSPECTOR
Panou inspector cu componentele unui GameObject

Totuși, așa cum am discutat, putem atinge problema „prea multe funcții” și ne putem găsi într-o ierarhie gigantică, cu funcții împrăștiate peste tot, ceea ce face viața unui dezvoltator mult mai grea.

Gândind în modul MVC, putem, în schimb, să începem prin a împărți lucrurile în funcție de funcția lor, structurând aplicația noastră ca în exemplul de mai jos:

CAPTURA DE ECRAN: EXEMPLU DE STRUCTURA UNITY MVC

Adaptarea MVC la un mediu de dezvoltare a jocurilor

Acum, aș dori să introduc două mici modificări ale modelului MVC generic, care ajută la adaptarea lui la situațiile unice cu care am întâlnit construirea proiectelor Unity cu MVC:

  1. Referințele de clasă MVC sunt ușor împrăștiate în cod. - În Unity, dezvoltatorii trebuie de obicei să trageți și să plasați instanțele pentru a le face accesibile, sau să le ajungă prin instrucțiuni greoaie precum GetComponent( ... ) . - Iadul referințelor pierdute va urma dacă Unity se prăbușește sau dacă vreo eroare face să dispară toate referințele târâte. - Acest lucru face necesar să existe un singur obiect de referință rădăcină, prin care toate instanțele din Aplicație să poată fi accesate și recuperate.
  2. Unele elemente încapsulează funcționalitate generală care ar trebui să fie foarte reutilizabilă și care nu se încadrează în mod natural într-una dintre cele trei categorii principale de model, vizualizare sau controler. Acestea îmi place să le numesc pur și simplu Componente . Ele sunt, de asemenea, „Componente” în sensul Entitate-Componentă, dar acționează doar ca ajutoare în cadrul MVC. - De exemplu, o componentă Rotator , care rotește lucrurile doar cu o viteză unghiulară dată și nu notifică, stochează sau decide nimic.

Pentru a ajuta la atenuarea acestor două probleme, am venit cu un model modificat pe care îl numesc AMVCC sau Application-Model-View-Controller-Component.

IMAGINE: DIAGRAMA AMVCC

  • Aplicație - Punct de intrare unic în aplicația dvs. și containerul tuturor instanțelor critice și a datelor legate de aplicație.
  • MVC - Ar trebui să știi asta până acum. :)
  • Componentă - Script mic, bine conținut, care poate fi reutilizat.

Aceste două modificări mi-au satisfăcut nevoile pentru toate proiectele în care le-am folosit.

Exemplu: 10 sărituri

Ca exemplu simplu, să ne uităm la un mic joc numit 10 Bounces , în care voi folosi elementele de bază ale modelului AMVCC.

Configurarea jocului este simplă: o Ball cu un SphereCollider și un Rigidbody (care va începe să cadă după „Play”), un Cube ca sol și 5 scripturi pentru a alcătui AMVCC.

Ierarhie

Înainte de a scrie scripturi, de obicei încep de la ierarhie și creez o schiță a clasei și a activelor mele. Urmând mereu acest nou stil AMVCC.

CAPTURA DE ECRAN: CONSTRUIREA IERARHIEI

După cum putem vedea, view GameObject conține toate elementele vizuale și, de asemenea, pe cele cu alte scripturi View . model și controller GameObjects, pentru proiecte mici, conțin de obicei doar scripturile respective. Pentru proiecte mai mari, acestea vor conține GameObjects cu scripturi mai specifice.

Când cineva care navighează în proiectul dvs. dorește să acceseze:

  • Date: Accesați application > model > ...
  • Logic/Flux de lucru: Accesați application > controller > ...
  • Redare/Interfață/Detecție: Accesați application > view > ...

Dacă toate echipele respectă aceste reguli simple, proiectele vechi nu ar trebui să devină o problemă.

Rețineți că nu există un container de Component deoarece, așa cum am discutat, acestea sunt mai flexibile și pot fi atașate la diferite elemente după bunul plac al dezvoltatorului.

Scripting

Notă: Scripturile prezentate mai jos sunt versiuni abstracte ale implementărilor din lumea reală. O implementare detaliată nu ar avantaja prea mult cititorul. Cu toate acestea, dacă doriți să explorați mai multe, iată linkul către cadrul meu personal MVC pentru Unity, Unity MVC. Veți găsi clase de bază care implementează cadrul structural AMVCC necesar pentru majoritatea aplicațiilor.

Să aruncăm o privire la structura scripturilor pentru 10 Bounces .

Înainte de a începe, pentru cei care nu sunt familiarizați cu fluxul de lucru al Unity, să clarificăm pe scurt cum funcționează împreună scripturile și GameObjects. În Unity, „Componentele”, în sensul Entitate-Componentă, sunt reprezentate de clasa MonoBehaviour . Pentru ca unul să existe în timpul rulării, dezvoltatorul trebuie fie să trage și să plaseze fișierul sursă într-un GameObject (care este „Entitatea” a modelului Entity-Component) sau să folosească comanda AddComponent<YourMonobehaviour>() . După aceasta, scriptul va fi instanțiat și gata de utilizare în timpul execuției.

Pentru început, definim clasa Aplicație („A” în AMVCC), care va fi clasa principală care conține referințe la toate elementele de joc instanțiate. Vom crea, de asemenea, o clasă de bază de ajutor numită Element , care ne oferă acces la instanța Aplicației și la instanțele MVC ale copiilor acesteia.

Având în vedere acest lucru, să definim clasa de Application („A” în AMVCC), care va avea o instanță unică. În interiorul acestuia, trei variabile, model , view și controller , ne vor oferi puncte de acces pentru toate instanțele MVC în timpul rulării. Aceste variabile ar trebui să fie MonoBehaviour cu referințe public la scripturile dorite.

Apoi, vom crea și o clasă de bază de ajutor numită Element , care ne oferă acces la instanța Aplicației. Acest acces va permite fiecărei clase MVC să ajungă la toate celelalte.

Rețineți că ambele clase extind MonoBehaviour . Sunt „Componente” care vor fi atașate la „Entități” 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() { } }

Din BounceElement putem crea clasele de bază MVC. BounceModel , BounceView și BounceController acționează de obicei ca containere pentru instanțe mai specializate, dar deoarece acesta este un exemplu simplu, numai View va avea o structură imbricată. Modelul și controlerul pot fi realizate într-un singur script pentru fiecare:

 // 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!!”); } }

Cu toate scripturile create, putem trece la atașarea și configurarea acestora.

Aspectul ierarhiei ar trebui să fie astfel:

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

Folosind BounceModel ca exemplu, putem vedea cum arată în editorul Unity:

CAPTURA DE ECRAN: BounceModel ÎN INSPECTOR
BounceModel cu câmpurile bounces și winCondition .

Cu toate scripturile setate și jocul rulând, ar trebui să obținem această ieșire în Panoul de consolă .

CAPTURA DE ECRAN: IEȘIRE CONSOLĂ

Notificări

După cum se arată în exemplul de mai sus, când mingea lovește pământul, vizualizarea sa execută app.controller.OnBallGroundHit() care este o metodă. Nu este, în niciun caz, „greșit” să faci asta pentru toate notificările din aplicație. Cu toate acestea, din experiența mea, am obținut rezultate mai bune folosind un sistem de notificare simplu implementat în clasa Aplicație AMVCC.

Pentru a implementa asta, să actualizăm aspectul aplicației BounceApplication pentru a fi:

 // 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() { /* ... */ } }

Apoi, avem nevoie de un nou script în care toți dezvoltatorii vor adăuga numele evenimentului de notificare, care pot fi trimise în timpul execuției.

 // 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”; /* ... */ }

Este ușor de observat că, în acest fel, lizibilitatea codului este îmbunătățită, deoarece dezvoltatorii nu trebuie să caute în tot codul sursă metodele controller.OnSomethingComplexName pentru a înțelege ce fel de acțiuni se pot întâmpla în timpul execuției. Prin verificarea unui singur fișier, este posibil să înțelegem comportamentul general al aplicației.

Acum, trebuie doar să adaptăm BallView și BounceController pentru a gestiona acest nou sistem.

 // 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; } } }

Proiectele mai mari vor avea o mulțime de notificări. Așadar, pentru a evita obținerea unei structuri de tip switch-case mare, este recomandabil să creați controlere diferite și să le faceți să gestioneze diferite domenii de notificare.

AMVCC în lumea reală

Acest exemplu a arătat un caz simplu de utilizare pentru modelul AMVCC. Ajustarea modului de gândire în ceea ce privește cele trei elemente ale MVC și învățarea să vizualizeze entitățile ca o ierarhie ordonată sunt abilitățile care ar trebui să fie șlefuite.

În proiectele mai mari, dezvoltatorii se vor confrunta cu scenarii mai complexe și îndoieli cu privire la faptul dacă ceva ar trebui să fie o vizualizare sau un controler, sau dacă o anumită clasă ar trebui separată mai bine în altele mai mici.

Regulile generale (de Eduardo)

Nu există niciun „Ghid universal pentru sortarea MVC” nicăieri. Dar există câteva reguli simple pe care le urmez de obicei pentru a mă ajuta să determin dacă să defines ceva ca model, vizualizare sau controler și, de asemenea, când să împart o anumită clasă în bucăți mai mici.

De obicei, acest lucru se întâmplă organic în timp ce mă gândesc la arhitectura software sau în timpul scripturilor.

Sortarea clasei

Modele

  • Păstrați datele și starea de bază ale aplicației, cum ar fi health jucătorului sau ammo pentru arme .
  • Serializați, deserializați și/sau convertiți între tipuri.
  • Încărcați/salvați date (local sau pe web).
  • Notifică controlorii cu privire la progresul operațiunilor.
  • Stocați starea jocului pentru mașina cu stări finite a jocului.
  • Nu accesați niciodată Views.

Vizualizări

  • Poate obține date de la Modele pentru a reprezenta starea actualizată a jocului pentru utilizator. De exemplu, o metodă View player.Run() poate folosi intern model.speed pentru a manifesta abilitățile jucătorului.
  • Nu ar trebui să modifice niciodată Modelele.
  • Implementează strict funcționalitățile clasei sale. De exemplu:
    • Un PlayerView nu ar trebui să implementeze detectarea intrării sau să modifice starea jocului.
    • O vizualizare ar trebui să acționeze ca o cutie neagră care are o interfață și notifică evenimente importante.
    • Nu stochează date de bază (cum ar fi viteza, sănătatea, viețile etc.).

Controlori

  • Nu stocați datele de bază.
  • Uneori poate filtra notificările de la vizualizări nedorite.
  • Actualizați și utilizați datele modelului.
  • Gestionează fluxul de lucru al scenei Unity.

Ierarhia de clasă

În acest caz, nu sunt mulți pași pe care îi urmez. De obicei, percep că o anumită clasă trebuie împărțită atunci când variabilele încep să arate prea multe „prefixe” sau prea multe variante ale aceluiași element încep să apară (cum ar fi clasele de Player într-un MMO sau tipurile de Gun într-un FPS).

De exemplu, un singur Model care conține date Player ar avea o mulțime de playerDataA, playerDataB, playerDataA, playerDataB,... sau un Controller care gestionează notificări Player ar avea OnPlayerDidA,OnPlayerDidB,... . Vrem să reducem dimensiunea scriptului și să scăpăm de prefixele player și OnPlayer .

Permiteți-mi să demonstrez utilizarea unei clase Model , deoarece este mai simplu de înțeles folosind numai date.

În timpul programării, de obicei încep cu o singură clasă Model care conține toate datele pentru joc.

 // 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; }

Este ușor de observat că, cu cât jocul este mai complex, cu atât variabilele vor deveni mai numeroase. Cu suficientă complexitate, am putea ajunge la o clasă gigantică care conține variabile model.playerABCDFoo . Elementele de imbricare vor simplifica completarea codului și, de asemenea, vor oferi spațiu de comutare între variațiile de date.

 // 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 }

Cu această configurație de clase, dezvoltatorii pot naviga intuitiv în codul sursă, câte un concept. Să presupunem un joc cu împușcături la persoana întâi, în care armele și configurațiile lor pot deveni foarte numeroase. Faptul că GunModel este conținut într-o clasă permite crearea unei liste de Prefabs (GameObjects preconfigurate care să fie rapid duplicate și reutilizate în joc) pentru fiecare categorie și stocate pentru utilizare ulterioară.

În schimb, dacă toate informațiile despre armă au fost stocate împreună în o singură clasă GunModel , în variabile precum gun0Ammo , gun1Ammo , gun0Clips și așa mai departe, atunci utilizatorul, atunci când se confruntă cu nevoia de a stoca datele Gun , ar trebui să stocheze întregul Model care include datele nedorite ale Player . În acest caz, ar fi evident că o nouă clasă GunModel ar fi mai bună.

IMAGINE: IERARHIE DE CLASĂ
Îmbunătățirea ierarhiei claselor.

Ca în toate, există două fețe ale monedei. Uneori se poate supracompartimenta în mod inutil și crește complexitatea codului. Doar experiența îți poate perfecționa abilitățile suficient pentru a găsi cea mai bună sortare MVC pentru proiectul tău.

Abilitate specială nouă dezvoltată de jocuri: jocuri Unity cu model MVC.
Tweet

Concluzie

Există o mulțime de modele de software acolo. În această postare, am încercat să arăt pe cea care m-a ajutat cel mai mult în proiectele trecute. Dezvoltatorii ar trebui să absoarbă întotdeauna cunoștințe noi, dar întotdeauna să le pună la îndoială. Sper că acest tutorial vă va ajuta să învățați ceva nou și, în același timp, să vă servească drept o piatră de temelie pe măsură ce vă dezvoltați propriul stil.

De asemenea, vă încurajez cu adevărat să cercetați alte modele și să găsiți pe cel care vi se potrivește cel mai bine. Un bun punct de plecare este acest articol Wikipedia, cu lista sa excelentă de modele și caracteristicile acestora.

Dacă vă place modelul AMVCC și doriți să-l testați, nu uitați să încercați biblioteca mea, Unity MVC , care conține toate clasele de bază necesare pentru a porni o aplicație AMVCC.


Citiți suplimentare pe blogul Toptal Engineering:

  • Unity AI Development: Un tutorial de mașină cu stări finite