Inventář. Část druhá - toggle panel. Log #14 (Unity, C#)
25.02.2018

    V minulém díle jsem popisoval inventář a jeho části. Dneska se vrhnu na zprovoznění toggle panelu pro zapínání a vypínání panelů předmětů. Řešení je na můj vkus celkem komplikované, niceméně funguje a to mi prozatím stačí. Pokud je hráč v inventáři a klikne levým na tlačítko (toggle), zobrazí nebo skryje odpovídající panel. Volba je ale jen dočasná dokud je hráč v inventáři, jakmile je inventář otevřen znova, automaticky se všechny panely skryjí. Proto jsem přidal možnost zvolit si oblíbené panely. To lze aktivovat prostředním tlačítkem myši (obyvkle kolečkem). Výběr zůstane uložen i po opětovném otevření inventáře. Tak bude mít hráč naprostou kontrolu nad zobrazenými prvky v inventáři.

    Prvním krokem je nastavení zobrazení tlačítek - rozpoznat mezi aktivovaným a deaktivovaným. V zásadě se o to toggle stará téměř sám, já jen měním grafiku. Togglu nastavím Target graphic na sprity které jsem postahoval na game-icons.net, změním barvu na odstín žluté a do panelu OnValueChanged(Boolean) přidám nový event, přetáhnu odpovídající objekt panelu a vyberu GameObject>SetActive. Tohle aktivuje nebo deaktivuje celý panel jako objekt. Jako vypnuté tlačítko přidám pod objekt toggle nový Image (ImageIsOn), stejný sprite jako aktivní toggle, jen změním barvu a přethnu jej do komponenty Toggle na mém tlačítku do políčka Graphic. Pod objekt tlačítka přidám ještě další Image, tvaru čtverce, jako typ nastavím "Filled", Method - Radial 90, Origin - Top Right, hodnotu 0.5, zaškrtnu clockwise  a zachování poměru stran. Tím mi vznikne malý trojúhelníček na pravém spodním okraji tlačitka. Teď se budou panely vypínat a zapínat po stisknutí tlačítka, ale to není ani zdaleka vše.

    Pro samotnou funkci vytvořím skript InventoryToggle, který bude uchovávat informaci o tom jestli je vybraný toggle v oblíbených nebo ne a starat se o aktivaci nebo deaktivaci ikonky označující oblíbený toggle. Tuhle informaci mi nese property inFavorites. Jak jsem zmínil výše, oblíbené nastavuji prostěředním tlačítkem, v kódu k tomu využiji funkci OnPointerClick(PointerEventData) která, potřebuje vyžaduje rozhraní IPointerClickHandler a předá mi v eventData.button tlačítko které na objekt s tímto skriptem kliklo, prostřední tlačítko je pak PointerEventData.InputButton.Middle. Zapínám nebo vypínám ikonku na tlačítku pomocí GetComponent<Image>().enabled. Také zároveň nastavuji tlačítko (a odpovíjadící panel) jako aktivní funkcí SetActive()

 

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class InventoryToggle : MonoBehaviour, IPointerClickHandler
{
    public bool inFavorites { get; private set; }

    void Awake()
    {
        inFavorites = false;
        ResetFavorite(false);
    }

    public void SetActive()
    {
        GetComponent<Toggle>().isOn = true;
    }

    public void SetInactive()
    {
        GetComponent<Toggle>().isOn = false;
    }

    public void ResetFavorite(bool favorie)
    {
        transform.GetChild(2).GetComponent<Image>().enabled = favorie;
        if(favorie) SetActive();
    }

    public void OnPointerClick(PointerEventData eventData)
    {
        if (eventData.button == PointerEventData.InputButton.Middle)
        {
            inFavorites = !inFavorites;
            if (inFavorites)
            {
                SetActive();
                transform.GetChild(2).GetComponent<Image>().enabled = true;
            }
            else
            {
                transform.GetChild(2).GetComponent<Image>().enabled = false;
            }
        }
    }
}

 

    K funkci ResetFavorite(bool) se dostanu později. V metodě Awake() (volá se jen při zapnutí hry) nastavím inFavorites na false. Později se budou volby hráče ukládat do paměti, to zatím nemám naimplementované. O celé chování všech togglů se mi bude starat skript Toggles na nadřazeném objektu panelu tlačítek. V metodě Start() si uložím všechny tlačítka do listu, k tomu využívám foreach, který dokáže iterovat skrz objekt typu Transform a vrací podřazené objekty opět typu Transform, které jsou vložené v inspektoru pod objektem který iteruji, v mém případě všechny tlačítka se skriptem InventoryToggle. Dále funkce které po zavolání resetují veškeré nastavení (jako oblíbené i aktivní tlačítka) - ResetFavoriteToggles() a ResetActive() případně zpětně aktivují oblíbené nebo aktivní - SetFavoriteToggles() a SetActiveToggle(string). Proč resetovat tlačítka? Pokaždé když do inventáře objekt přidám, nebo jej odeberu se inventář bude aktualizovat. A hlavně protože budu používat okno inventáře taky pro přístup k truhlám a skladištím. To budu implementovat ale jindy. Tady je skript Toggle.cs

 

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Toggles : MonoBehaviour
{

    List<Toggle> _toggles { get; set; }
    public Transform toggles;

    void Start()
    {
        _toggles = new List<Toggle>();
        foreach (Transform toggle in toggles)
        {
            _toggles.Add(toggle.GetComponent<Toggle>());
        }
    }

    public void SetActiveToggle(string toggleName)
    {
        foreach (Toggle _toggle in _toggles)
        {
            if (_toggle.transform.name == toggleName)
            {
                _toggle.GetComponent<InventoryToggle>().SetActive();
            }
        }
    }

    public void ResetFavoriteToggles()
    {
        foreach (Toggle _toggle in _toggles)
        {
            _toggle.GetComponent<InventoryToggle>().ResetFavorite(false);
        }
    }

    public void SetFavoriteToggles()
    {
        foreach (Toggle _toggle in _toggles)
        {
            _toggle.GetComponent<InventoryToggle>().ResetFavorite(_toggle.GetComponent<InventoryToggle>().inFavorites);
        }
    }
    public void ResetActive()
    {
        foreach (Toggle toggle in _toggles)
        {
            toggle.GetComponent<InventoryToggle>().SetInactive();
        }
    }

}

 

    Kde ale všechny tyhle funkce využiju? Pro řízení inventáře přidám na objekt Inventory nový skript InventoryManager. Tady definuji veškerou logiku inventáře. Aktuálně je to jen reset tlačítek a jejich aktualizace, později se bude třída rozšiřovat. Jako u player menu i inventář bude "jedináček", tzv. singelton. Vytvořím veřejnou statickou proměnnou InventoryManager Instance a v Awake() si ji zabezpečím stejným způsobem jako u menu. Zatím si jen uchovám referenci na objekt Toggles, který do proměnné toggles přiřadím v inspektoru. Vytvořím dvě funkce, SetToggles() a UpdateList(). Ve funkci pro aktualizaci budu volat SetToggles(), ve které jen resetuji tlačitka a následně budu nastavovat oblíbené, pomocí funkcí toggles.ResetToggles() a toggles.SetFavoriteToggles(). Protože mám grafiku a logiku oddělelnou, ale logika hodně s grafikou pracuje, vytvořím si proměnnou InventoryGUIManager inventoryGUIManager, do které přiřadím komponentu starající se o grafiku - vytvořenou v předešlém díle. Ve funkci Awake() jen přidám inventoryGUIManager.HideInventory(), pro skrytí inventáře po zapnutí hry. Tahle třída se mi taky bude starat o zapínání inventáře, vytvořím tedy funkci OpenInventory() a do ní zavolám funkci UpdateList() a hned potom inventoryGUIManager.ShowInventory(). Pochopitelně teď budu muset změnit event v panelu OnClick() pro komponentu Button tlačítka Inventory_button player menu. Tam změním event na InventoryManager>OpenInventory(). A také smazat HideInventory() z Awake() metody v InventoryGUIManager.

 

using UnityEngine;

public class InventoryManager : MonoBehaviour
{
    public static InventoryManager Instance { get; private set; }

    InventoryGUIManager inventoryGUIManager;
    public Toggles toggles;

    void Awake()
    {
        if (Instance == null)
            Instance = this;
        else if (Instance != this)
            Destroy(gameObject);

        inventoryGUIManager = GetComponent<InventoryGUIManager>();
        inventoryGUIManager.HideInventory();
    }

    public void OpenInventory()
    {
        UpdateList();
        inventoryGUIManager.ShowInventory();
    }

    void SetToggles(Inventory inventory)
    {
        toggles.ResetActive();
        toggles.SetFavoriteToggles();
    }

    void UpdateList()
    {
        SetToggles();
    }
}

 

    V téhle fázi se mi zobrazí inventář bez panelů, můžu si kterýkoliv aktivovat, nebo přidat do oblíbených, ten co v oblíbených nebude se mi při dalším otevření inventáře nezobrazí.

 

 

    Následující díl budu věnovat inventáři v případě, kdy otvírám inventář který nepatří hráči - to je truhla, obchodník, nebo skladiště.