This is an old revision of the document!
Realizati o scena care sa contina:
todo: raycast pentru selectia obiectelor - point and click mers la ele
Interactiunea cu obiectele in spatiul 3D poate fi extrem de complexa, intrucat exista foarte multe forme de interactiuni: inamici, obiecte de pickup, deschidere de usi, activare de manivele etc. Fiecare dintre aceste interactiuni are specificul ei, dar abstractizand, putem deduce ca fiecare interactiune se intampla intr-o anumita raza si cu un anumit punct de interactiune. Pentru a defini usor aceste lucruri, putem crea o clasa generica denumita InteractionObject cu o metoda abstracta (virtuala) ce defineste interactiunea in detaliu.
public class InteractionObject : MonoBehaviour {
  public float radius = 1f;
  public Transform interactionPoint;
  Transform interactionObject;
  bool done = false;
  //metoda abstracta, speficica fiecarui tip de interactiuni
  public virtual void Interaction ()
  {
		
  }
  void Update ()
  {
      float distance = Vector3.Distance(interactionObject.position, interactionPoint.position);
      if (distance <= radius && !done) // avem interactiune cu obiectul, pot sa afisez informatii: de ex "Press E to use"
      {
        done = true;
        Interaction();
      }
    }
  }
}
Astfel, toate obiectele ce vor avea interactiuni, vor mosteni aceasta clasa. Spre exemplu pentru un obiect de pickup putem avea urmatoarea secventa:
public class PickupObject : InteractionObject {
  public override void Interaction()
  {
    base.Interaction(); // se apeleaza metoda parinte, in caz ca avem ceva generic
    
    //mecanica
    ...
    
    //distrugem obiectul
    Destroy(gameObject);
    
  }
}
Pentru a controla mai bine zona de actiune (radius) si punctul de interes pentru un obiect de interactiune (InteractionObject), se poate defini o functie de editor, atunci cand obiectul este selectat. In exemplul de mai jos, la selectarea obiectului se va afisa o sfera wireframe de culoare alba.
void OnDrawGizmosSelected ()
{
  Gizmos.color = Color.white;
  Gizmos.DrawWireSphere(interactionPoint.position, radius);
}	
In ceea ce priveste quest-urile, sunt foarte multe posibilitati de abordare, dar in general implica urmatoarele elemente:
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:
Exemple de tipuri de Recompense:
Exemplu de clasa generica de tip Quest
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.
  }
  
  
}
Apoi, un exemplu de un quest concret.
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( ...));    
  
  }
}
Si un exemplu de Obiectiv
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;
    }
  }
  
  
}
In ceea ce priveste cine gestioneaza questul, acesta este de obicei un NPC, deci putem extinde clasa NPC cu cateva lucuri specifice:
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();
      }
    }
  }
}
Astfel, pentru inamici putem defini un controller cu un radius de actiune, si un gizmos pentru vizualizare usoara a acestuia in editor.
public class EnemyController : MonoBehaviour {
  public float radius = 2;
  
  void OnDrawGizmosSelected() {
    Gizmos.color = Color.red;
    Gizmos.DrawWireSphere(transform.position, radius);
  }
}
Diferenta este ca acesti agenti vor raspunde automat la anumite evenimente: