Realizati o scena care sa contina:
Inregistrare pe teams
Pentru a crea un sistem de inventar avem nevoie in primul rand de date atasate fiecarui obiect, cum ar fi nume, icon, atribute etc.
Putem realiza acest lucru usor prin obiecte scriptabile (Scriptable Objects). Obiectele Scriptable sunt containere de date ce nu trebuie sa fie atasate la un GameObject intr-o scena. Ele pot fi salvate ca asset-uri in bibliteca proiectului ca mai apoi sa poata fi utilizate.
ScriptableObject este o clasă Unity serializabilă care vă permite să stocați cantități mari de date partajate independent de instanțe de script. Utilizarea ScriptableObjects facilitează gestionarea modificărilor și depanarea. Puteți construi un nivel de comunicare flexibilă între diferitele sisteme din jocul dvs., astfel încât să fie mai ușor de gestionat să le schimbați și să le adaptați pe parcursul producției, precum și reutilizarea componentelor.
Obiectele scriptabile se definesc prin crearea unui script ce mosteneste clasa ScriptableObject.
Pentru a instantia un obiect scriptabil avem doua variante:
[CreateAssetMenu(fileName = "New Item", menuName = "Inventory/Item", order = 1)] public class Item : ScriptableObject { new public string name = "New MyScriptableObject"; //suprascrie atributul name public string objectName = "New MyScriptableObject"; public bool colorIsRandom = false; public Color thisColor = Color.white; public Sprite icon; public Vector3[] spawnPoints; // Called when the item is pressed in the inventory public virtual void Use () { // Use the item // Something may happen } // Call this method to remove the item from inventory public void RemoveFromInventory () { Inventory.instance.Remove(this); } }
Intrucat exista atribute implicite pentru un obiect scriptabil (e.g. name), putem folosi variabile diferite (e.g. objectName) sau putem suprascrie definirea acestui atribut prin folosirea metodei new
(new public string name).
Mai mult, putem crea obiecte cu actiuni specifice: de ex obiect consumabil sau echipabil.
using UnityEngine; /* An Item that can be consumed. So far that just means regaining health */ [CreateAssetMenu(fileName = "New Item", menuName = "Inventory/Consumable")] public class Consumable : Item { public int healthGain; // How much health? // This is called when pressed in the inventory public override void Use() { // Heal the player PlayerStats playerStats = Player.instance.playerStats; playerStats.Heal(healthGain); Debug.Log(name + " consumed!"); RemoveFromInventory(); // Remove the item after use } }
Mai departe, pentru un inventar vom avea nevoie de o lista de obiecte gestionabile. Pentru acest lucru vom face un script de gestiune pentru inventar (e.g InventoryManager) care gestioneaza adaugarea, eliminarea si interogarea inventarului. Pentru o accesare mai usoara si mai facila, ideal ar fi ca acest inventorymanager sa fie un Singleton. Pentru a avea un singleton trebuie sa ne asiguram ca avem o singura instanta creata pentru acest script atunci cand e accesat.
public class InventoryManager : MonoBehaviour { // singleton public static InventoryManager instance; void Awake() { instance = this; } //lista de obiecte public List<Item> items = new List<Item>(); //metode pentru gestionare public void Add(Item item) { items.Add(item); } public void Remove(Item item) { items.Remove(item); } }
Fiind definit ca un singleton, putem accesa acum foarte usor gestionarea inventarului:
Inventory.instance.Add(item); Inventory.instance.Remove(item);
Inca un element util in gestionarea inventarului este definirea unei metode de a notifica atunci cand s-a produs o modificare in inventar. Pentru acest lucru putem folosi Delegates
. Un Delegate este un pointer la o metode. Aceasta ne permite sa tratam metoda ca o variabila și sa o folosim pentru un callback. Cand este apelata, acesta notifica toate metodele care fac referire la delegate. Astfel putem definit o variabila pentru evenimentul de schimbare.
public delegate void OnInventoryChanged(); public OnInventoryChanged onInventoryChangedCallback; //metode pentru gestionare public void Add(Item item) { ... onInventoryChangedCallback.Invoke(); //notifica despre modificare } public void Remove(Item item) { ... onInventoryChangedCallback.Invoke(); //notifica despre modificare }
Urmatorul pas este crearea unei interfete grafice si legarea interfetei de functionalitatea InventoryManger-ului. Pentru interfata grafica, putem folosi, ca si pana acum, canvas-ul oferit de Unity, structurat astfel incat sa avem un panou general pentru inventar, si mai multe slot-uri pentru obiectele din acesta. Pe fiecare slot putem defini urmatoarele aspecte:
Pentru sloturile de inventar este indicat sa folositi un prefab sau un template.
public class InventorySlot : MonoBehaviour { public Image icon; public Button removeButton; Item item; // Current item in the slot // Add item to the slot public void AddItem (Item newItem) { item = newItem; icon.sprite = item.icon; icon.enabled = true; removeButton.interactable = true; } // Clear the slot public void ClearSlot () { item = null; icon.sprite = null; icon.enabled = false; removeButton.interactable = false; } // If the remove button is pressed, this function will be called. public void RemoveItemFromInventory () { Inventory.instance.Remove(item); } // Use the item public void UseItem () { if (item != null) { item.Use(); } } }
Interfata grafica a inventarului are nevoie si de un script de gestionare. Astfel vom aveam un script care asculta (subscribe) evenimentul definit (delegate
) la actualizarea inventarului, si actualizeaza fiecare slot din interfata grafica:
void Start() { inventory = Inventory.instance; inventory.onInventoryChangedCallback += UpdateUI; //definesc o metoda ca se apeleaza la aparitia unui eveniment delegat slots = GetComponentsInChildren<InventorySlot>(); //fiecare slot din inventar } void UpdateUI() { //actualizare fiecare slot for(i=0; i < slots.Length; i++) { if(i<iventory.items.Count) slots[i].AddItem(..) else slots[i].RemoveItem(..) } }
Bineinteles, in inventar se pot pune diferse restrictii si interactiuni (cum ar fi dimensiunea maxima a inventarului)
bool AddItem() { if(items.Count >= space) //no more room return false else return true; } if(Inventory.instance.Add(item)) Destroy(gameObject);
Similar se poate face si gestiunea altor interfet: de quest pentru player, de echipament / arma, skilltree etc.
Pentru crearea unui sistem de dialog, se folosesc elemente de UI Canvas: Panel, Button, Image etc.
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.
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(); } }
Apoi, in clasa NPC-ului, putem instantia un dialog personalizat pentru NPC-ul respectiv:
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); }