Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pjv:laboratoare:07 [2018/11/14 22:15]
alexandru.gradinaru
pjv:laboratoare:07 [2019/10/02 12:57] (current)
alexandru.gradinaru
Line 1: Line 1:
-===== Laboratorul 07. =====+===== Mecanici avansate in 3D ===== 
 + 
 +==== Agenti ==== 
 + 
 +Pentru a programa inamici sau agenti NPC (Non-Playable Character) se poate folosi aceeasi functionalitate de navigare automata (NavMesh) si componenta de tip NavMeshAgent,​ pentru navigatie, similar cu sectiunea de Navigare automata din laboratorul precedent. 
 + 
 +Diferenta este ca acesti agenti vor raspunde automat la anumite evenimente:​ 
 +  * inamicii de obicei incep sa interactioneze atunci cand player-ul intra intr-o anumita raza de actiune 
 +  * NPC-urile interactoneaza la fel, bazate pe o raza de actiune sau efectiv interactiune directa (click) 
 + 
 +=== Inamici === 
 + 
 +Astfel, pentru inamici putem defini un controller cu un radius de actiune, si un gizmos pentru vizualizare usoara a acestuia in editor. 
 + 
 +<​code>​ 
 +public class EnemyController : MonoBehaviour { 
 + 
 +  public float radius = 2; 
 +   
 +  void OnDrawGizmosSelected() { 
 +    Gizmos.color = Color.red;​ 
 +    Gizmos.DrawWireSphere(transform.position,​ radius); 
 +  } 
 + 
 + 
 +
 +</​code>​ 
 + 
 +{{ :​pjv:​laboratoare:​enemy-radius.png?​500 |}} 
 + 
 +Pentru ca un inamic sa se miste spre player, atunci cand player-ul intra in raza de actiune putem folosi componenta de NavMeshAgent 
 + 
 +<​code>​ 
 + 
 +void Update() { 
 +  //calculam distanta intre player si inamic 
 +  float distance = Vector3.Distance(target.position,​ transform.position);​ 
 + 
 +  if(distance <= radius) 
 +  { 
 +    //misca agentul pana la player 
 +    agent.SetDestination(target.position);​ 
 +     
 +    //in momentul in care intra in raza de atac, ataca 
 +  
 +  } else { 
 +    // agentul se misca in treaba lui / patruleaza etc. 
 +  } 
 +</​code>​ 
 +   
 +Mai departe, se poate folosi o alta distanta, pentru a determina raza de atac. Un inamic poate avea atac melee (de aproape) sau de la o anumita distanta.  
 + 
 +<​code>​ 
 + 
 +public float attackRadius = 1; 
 + 
 +void Update() { 
 +  //calculam distanta intre player si inamic 
 +  float distance = Vector3.Distance(target.position,​ transform.position);​ 
 + 
 +  if(distance <= radius) 
 +  { 
 +    //misca agentul pana la player 
 +    agent.SetDestination(hit.point);​ 
 +     
 +    //in momentul in care intra in raza de atac, ataca 
 +    if(distance <= attack)  
 +    { 
 +      //ataca 
 +    } 
 +  
 +  } 
 +   
 +</​code>​ 
 + 
 +O problema in activitatea agentilor este detectarea player-ului,​ in sensul de referinta. Astfel, avem mai multe variante: 
 +  * putem cauta un obiect dupa tag 
 +  * intr-o variabila target putem referentia direct player-ul (dar asta inseamna ca la fiecare agent trebuie mapat) 
 +  * putem folosi un singleton in care se tine referentiaza playerul si poate fi accesat de oriunde 
 + 
 +<​code>​ 
 +public class PlayerManager : MonoBehaviour { 
 +  
 +  public static PlayerManager instance; 
 +  public GameObject player; 
 +     
 +  void Awake() 
 +  { 
 +    instance = this; 
 +  } 
 + 
 +
 +</​code>​ 
 + 
 +Folosind varianta simpla cu singleton, putem lua pozitia player-ului de inters, similar cu laboratorul precedent:​ 
 + 
 +<​code>​ 
 +target = PlayerManager.instance.player.transform;​ 
 +</​code>​ 
 + 
 +<​code>​ 
 + 
 +        //Roteste cu 90 grade 
 +        void RotateN() { 
 +         ​Vector3 currentRotation = transform.rotation;​ 
 +         ​Vector3 wantedRotation = currentRotation * Quaternion.AngleAxis(-90,​ Vector3.up);​ 
 +         ​transform.rotation = Quaternion.Slerp(currentRotation,​ wantedRotation,​ Time.deltaTime * rotationSpeed);​ 
 +        } 
 +        //Roteste inamicul cu fata catre player  
 + void FaceTarget () 
 +
 + Vector3 direction = (target.position - transform.position).normalized;​ 
 + Quaternion lookRotation = Quaternion.LookRotation(new Vector3(direction.x,​ 0, direction.z));​ 
 + transform.rotation = Quaternion.Slerp(transform.rotation,​ lookRotation,​ Time.deltaTime * 5f); 
 +
 +</​code>​ 
 + 
 +=== NPC === 
 + 
 +In ceea ce priveste NPC-urile, acestea de obicei interactioneaza cu player-ul prin dialog: oferirea de informatii, gossip, quest-uri etc. 
 + 
 +Sistemele de dialog si de quest-uri pot fi foarte complexe, iar posibilitatile nelimitate. In continuare voi prezenta cateva lucruri de baza prezente in aceste sisteme. 
 + 
 +Din punct de vedere programatic,​ NPC-urile sunt tot un tip de obiect cu care se poate interactiona,​ astfe ca se poate folosi aceasi paradigma din laboratorul precedent, de InteractionObject. 
 + 
 +Primul punct important in interactiunea cu NPC-urile este sistemul de dialog. 
 + 
 +=== Dialog System === 
 + 
 +Pentru crearea unui sistem de dialog, se folosesc elemente de UI Canvas: Panel, Button, Image etc. 
 + 
 +{{ :​pjv:​laboratoare:​dialog-panel.png?​500 |}}  
 + 
 +Mai departe, putem face un DialogManager,​ tot sub format singleton, pentru a putea referentia elementele de UI mai usor, o singura data. In acest manager putem configura diverse linii de dialog, numele NPC-ului care sa apara in caseta de dialog etc. De asemenea, va trebui sa tinem minte si in ce moment, sau la care linie de dialog ne aflam. 
 + 
 +<​code>​ 
 + 
 +public class DialogManager : MonoBehaviour { 
 +   
 +  public static DialogManager instance; 
 +  public GameObject panel; 
 +  public string NPCName; 
 +  public List<​string>​ sentences = new List<​string>​();​ 
 +   
 +  Button continueButton;​ 
 +  Text dialogeTextContainer,​ NPCNameContainer;​  
 +   
 +  int lineIndex;​ 
 +     
 +  void Awake() 
 +  { 
 +    //get child components 
 +    dialogeTextContainer = ...  
 +    NPCNameContainer = ... 
 +    continueButton = ... 
 +    continueButton.onClick.AddListener(delegate { ContinueDialog();​ }); 
 +     
 +    instance = this; 
 +  } 
 +   
 +  public void AddNewDialogue(string[] lines, string NPCName) 
 +  { 
 +    //​initializeaza dialogul curent din NPC 
 +    lineIndex=0;​ 
 +    ... 
 +  } 
 +   
 +  public void showDialog() 
 +  { 
 +    dialogeTextContainer.text = sentences[lineIndex];​ //afiseaza linia de dialog curenta 
 +    NPCNameContainer.text= NPCName; 
 +    panel.SetActive(true);​ 
 +     
 +  } 
 +   
 +  public void ContinueDialog() 
 +  { 
 +    lineIndex++;​ 
 +    showDialog();​ 
 +  } 
 +   
 +
 + 
 +</​code>​ 
 + 
 +Apoi, in clasa NPC-ului, putem instantia un dialog personalizat pentru NPC-ul respectiv:​ 
 + 
 +<​code>​ 
 + 
 +public class NPC : InteractionObject { 
 + 
 +  public string[] sentences; //se pot configura liniile de dialog in editor 
 +  public string name; 
 +   
 +  public override void Interaction() 
 +  { 
 +    base.Interaction();​ // se apeleaza metoda parinte 
 + 
 +    DialogManager.Instance.AddNewDialog(sentences,​ name); 
 +  } 
 + 
 +</​code>​ 
 + 
 +{{ :​pjv:​laboratoare:​dialog.png?​500 |}} 
 + 
 +=== Quest System === 
 + 
 +In ceea ce priveste quest-urile,​ sunt foarte multe posibilitati de abordare, dar in general implica urmatoarele elemente: 
 +  * obiectivele quest-ului 
 +  * recompensele 
 +  * cine gestioneaza quest-ul 
 + 
 +Astfel, o abordare sugerata este sa se abstractizeze o clasa de tip Quest, una de tip Goal (obiectiv) si una de tip Recompensa, intrucat exista multe tipuri in care se pot instantia aceste lucruri. 
 + 
 +Exemple de tipuri de Obiective:​ 
 +  * Kill - kill a bunch of stuff 
 +  * Gather - gather stuff for me 
 +  * Deliver - deliver my live letter 
 +  * etc. 
 + 
 +Exemple de tipuri de Recompense:​ 
 +  * items 
 +  * experience 
 +  * gold 
 +  * skill 
 +  * etc. 
 + 
 +Exemplu de clasa generica de tip Quest 
 + 
 +<​code>​ 
 +public class Quest : MonoBehaviour { 
 + 
 +  public List<​Goal>​ Goals = new List<​Goal>​();​ 
 +  public List<​Reward>​ Rewards = new List<​Reward>​();​ 
 +  public bool completed;​ 
 +   
 +  public void CheckGoals() { 
 +    completed = Goals.All(g => g.completed);​ //questul este gata cand toate obiectivele sunt complete 
 +  } 
 +   
 +  public void GiveReward() { 
 +    //in functie de tipul recompensei se adauga obiecte in inventar, sau se adauga experienta, skill points etc. 
 +  } 
 +   
 +   
 + 
 +
 +</​code>​ 
 + 
 +Apoi, un exemplu de un quest concret. 
 + 
 +<​code>​ 
 + 
 +public class RandomSlayThingsQuest : Quest { 
 + 
 +  void Start() 
 +  { 
 +    QuestName = "​Random Slayer";​ 
 +    Description = "Kill some time";​ 
 +     
 +    Goals.Add(new KillGoal( ...)); 
 +    Goals.Add(new KillGoal( ...)); 
 +     
 +    Rewards.Add(new ItemReward( ...)); 
 +    Rewards.Add(new ExperienceReward( ...)); ​    
 +   
 +  } 
 + 
 +
 + 
 +</​code>​ 
 + 
 +Si un exemplu de Obiectiv 
 + 
 +<​code>​ 
 + 
 +public class KillGoal : Goal { 
 +   
 +  bool completed;​ 
 +  int currentAmmount;​ 
 +   
 +  public KillGoal(int enemyId, int ammount) { 
 +    ... 
 +  } 
 +   
 +  public override void Init() { 
 +    //listen to enemy death event 
 +    EnemyManager.onDie += EnemyDied;​ 
 +  } 
 +   
 +  void EnemyDied(enemy) { 
 +    this.currentAmmount++;​ 
 +    if(this.currentAmmount >= ammount) { 
 +      this.completed = true; 
 +    } 
 +  } 
 +   
 +   
 +
 + 
 +</​code>​ 
 + 
 +In ceea ce priveste cine gestioneaza questul, acesta este de obicei un NPC, deci putem extinde clasa NPC cu cateva lucuri specifice:​ 
 + 
 +<​code>​ 
 + 
 +public class QuestNPC : NPC { 
 +  
 + 
 +  public bool assigned; 
 +  public Quest quest; 
 +   
 +  public override void Interaction() 
 +  { 
 +    base.Interaction();​ // se apeleaza metoda parinte 
 + 
 +    if(!assigned) { 
 +      //dialog 
 +      //assign 
 +    } 
 +     
 +    void CheckQuest() { 
 +      if(quest.completed) { 
 +        quest.GiveReward();​ 
 +      } 
 +    } 
 +  } 
 +
 + 
 +</​code>​ 
 + 
 +Gestiunea interfetei de quest pentru player, se poate face similar cu cea de inventar, prezentata in laboratorul precedent. 
 + 
 + 
 +===Cerinte ​==== 
 + 
 +Realizarea unui joc 3D RPG 
 + 
 +  - Adaugati unul sau mai multi NPC care: 
 +      - stiu sa converseze (text) prin raspunsuri la intrebari standard 
 +      - pot oferi un quest (quest-urile au obiective si recompense) 
 +  - Adaugati unul sau mai multi inamici in scena scriptati astfel incat: 
 +      - sa fie animati 
 +      - sa se plimbe intr-o proximitate 
 +      - la apropierea jucatorului,​ sa il atace  
 + 
 +<​hidden>​ 
 +   
 +   
 + 
 +NPC 
 +Agenti de la care se pot prelua/​rezolva quest-uri 
 +Sunt animati 
 +Pot purta o conversatie cu mai multe fire de dialog si mai multe variante de raspuns 
 +</​hidden>​
  
 <​hidden>​ <​hidden>​
Line 9: Line 363:
       - poate folosi skill-uri/​abilitati speciale       - poate folosi skill-uri/​abilitati speciale
   ​   ​
-  - Adaugati unul sau mai multi NPC care: +  
-      - stiu sa converseze (text) prin raspunsuri la intrebari standard +      -  
-      - pot oferi un quest  +</​hidden>​
-  - Adaugati unul sau mai multi inamici in scena scriptati astfel incat: +
-      - sa fie animati +
-      - sa se plimbe intr-o proximitate +
-      - la apropierea jucatorului,​ sa il atace</​hidden>​+
  
 <​hidden>​ <​hidden>​
Line 36: Line 386:
 este animat (atac, miscare) este animat (atac, miscare)
  
-NPC +
-Agenti de la care se pot prelua/​rezolva quest-uri +
-Sunt animati +
-Pot purta o conversatie cu mai multe fire de dialog si mai multe variante de raspuns+
  
 </​hidden>​ </​hidden>​
pjv/laboratoare/07.1542226506.txt.gz · Last modified: 2018/11/14 22:15 by alexandru.gradinaru
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0