일단 결론부터 말하자면 Scene을 관리하는 Scene 객체에 붙여서, Scene이 로드되면 SceneUI를 불러오는 코드이다.

자동화를 해놓았기 때문에, 코드 한 줄만 작성하면 된다.

 

GameScene.cs

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class GameScene : BaseScene
{
    protected override void Init()
    {
        base.Init();

        SceneType = Define.Scene.Game;

        Managers.UI.ShowSceneUI<UI_Inven>(); // 이 부분에서 UI가 자동으로 실행 !!

        Dictionary<int, Stat> dict = Managers.Data.StatDict;
    }

    public override void Clear()
    {

    }
}

 

UI가 실행되는 부분을 살펴 보았으니, 본격적으로 자동화 구조를 작성해보고자 한다.

보통 인벤토리를 생각하면 키보드의 키를 눌러서 Popup 형식으로 등장하곤 한다.

그러나 이번에 구현할 코드에서는 Scene에 할당되어 SceneUI로 관리한다.

즉, Scene을 실행하면 계속 띄워져 있다.

 

먼저, 작성된 클래스의 역할과 구조를 알아야 한다.

  • UI_Base
    • UI의 최상위 클래스이다. 모든 UI 클래스들의 부모가 된다.
  • UI_Scene
    • SceneUI의 최상위 클래스이다. 모든 SceneUI의 부모가 된다.
  • UI_Popup
    • PopupUI의 최상위 클래스이다. 모든 PopupUI의 부모가 된다.
    • 코드에 작성은 했으나 사용은 하지 않는다.
  • UI_Inven
    • UI_Inven이라는 이름의 prefab을 관리하기 위한 코드이다.
  • UI_Inven_Item
    • UI_Inven의 prefab 안에 자식으로 딸려 있는 아이템들을 위한 코드이다.
  • UI_EventHandler
    • UI의 이벤트를 관리하기 위한 코드이다.

 

오늘 구현해 볼 인벤토리의 구조이다.

  • @Root_UI
    • UI_Inven
      • GridPanel
        • UI_Inven_Item
        • UI_Inven_Item
        • ... 

 

UI를 생성할 때, 결국 UIManager를 실행하게 되는데, 이때 @Root_UI가 없다면 자동으로 생성하게 되고, 모든 UI를 자식으로 붙여서 관리를 해준다. 코드가 궁금하다면 아래 글을 참고해보자.

 

[UNITY] UIManager

UI를 관리하는 코드이다. SceneUI는 Scene이 열렸을 때, 한번만 열리면 되지만, PopupUI는 Scene 중간에 올라올 수 있기 때문에, 스택으로 관리한다. 평소 게임에서 팝업이 열리고, 끄는 과정을 생각해보

mainsdev.tistory.com

 

UI_Base.cs

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

public abstract class UI_Base : MonoBehaviour
{
    Dictionary<Type, UnityEngine.Object[]> _objects = new Dictionary<Type, UnityEngine.Object[]>(); // UI를 바인딩 할 딕셔너리 생성

    public abstract void Init(); // 자식 클래스에서 추가적으로 구현할 추상 클래스 함수

    protected void Bind<T>(Type type) where T : UnityEngine.Object // Object - Component - Behaviour - Monobehaviour 순서로 상속
    {

        string[] names = Enum.GetNames(type); // reflection을 사용해 선언한 Enum 타입의 이름을 가져온다.

        UnityEngine.Object[] objects = new UnityEngine.Object[names.Length]; // 딕셔너리에 넣을 배열을 선언
        _objects.Add(typeof(T), objects);

        for (int i = 0; i < names.Length; i++)
        {
            if (typeof(T) == typeof(GameObject))
                objects[i] = Util.FindChild(gameObject, names[i], true); // Bind 함수를 사용한 객체의 게임 오브젝트에서 인자로 받은 type의 자식 객체를 찾는다.
            else
                objects[i] = Util.FindChild<T>(gameObject, names[i], true); // 게임 오브젝트 타입이 아니라면 제너릭을 이용한 함수 호출

            if (objects[i] == null) // 호출 실패
                Debug.Log($"Failed to bind : {names[i]}");

        }
    }

    protected T Get<T>(int idx) where T : UnityEngine.Object
    {
        UnityEngine.Object[] objects = null;
        if (_objects.TryGetValue(typeof(T), out objects) == false) // 바인딩 딕셔너리에서 입력받은 타입을 Key 값으로 검색하여 objects에 Value를 저장한다
            return null;
        return objects[idx] as T; // 존재할 시 T 타입으로 저장한 값을 반환한다.
    }

    protected GameObject GetObject(int idx) { return Get<GameObject>(idx); } // 타입에 따라 GET 함수를 사용할 수 있게 만들어 놓음
    protected Text GetText(int idx) { return Get<Text>(idx); }
    protected Button GetButton(int idx) { return Get<Button>(idx); }
    protected Image GetImage(int idx) { return Get<Image>(idx); }

    public static void BindEvent(GameObject go, Action<PointerEventData> action, Define.UIEvent type = Define.UIEvent.Click)
    { // UI에 이벤트를 바인드 하는 코드

        UI_EventHandler evt = Util.GetOrAddComponent<UI_EventHandler>(go); // 함수를 호출한 오브젝트에서 UI_EventHandler 컴포넌트가 있는지 확인하고, 없으면 붙여준다.

        switch (type) // Enum 타입으로 선언된 이벤트 타입에 따라 이벤트 설정을 다르게 한다.
        {
            case Define.UIEvent.Click:
                evt.OnClickHandler -= action; // 델리게이트로 받은 함수를 클릭 핸들러에 지정
                evt.OnClickHandler += action;
                break;
            case Define.UIEvent.Drag:
                evt.OnDragHandler -= action; // 델리게이트로 받은 함수를 드래그 핸틀러에 지정
                evt.OnDragHandler += action;
                break;
        }

        
        evt.OnDragHandler += ((PointerEventData data) => { evt.gameObject.transform.position = data.position; });
        // 예제코드 : 드래그 핸들러에 게임 오브젝트를 잡고 드래그 하면 그 위치로 이동하는 함수
    }
}

 

UI_Base에서는 Bind와 Get, BindEvent 함수가 선언되어 있다. 

 

Bind는 UI의 이름을 enum 타입으로 선언된 값을 인자로 받는다. 인자로 받아온 값을 변환하여 모든 자식들을 찾아서 _objects 딕셔너리에 저장한다. 

Get은 _objects 딕셔너리에서 받은 Type 을 Key로 하여 해당하는 게임 오브젝트를 Value로 받는다.

BindEvent는 인자로 받은 게임 오브젝트에 UI_EventHandler를 컴포넌트로 붙여주고, 인자로 받은 이벤트 함수를 지정해준다. switch 문을 통해 이벤트 타입에 따라 이벤트를 설정할 수 있다.

 

나머지 부분은 다음 글에 작성.

 

 

[UNITY] UI 자동화(2) - 인벤토리 구현

[UNITY] UI 자동화(1) - 인벤토리 구현 일단 결론부터 말하자면 Scene을 관리하는 Scene 객체에 붙여서, Scene이 로드되면 SceneUI를 불러오는 코드이다. 자동화를 해놓았기 때문에, 코드 한 줄만 작성하면

mainsdev.tistory.com

 

'UNITY' 카테고리의 다른 글

UNITY - ResourceManager  (0) 2024.01.12
UNITY - UI 자동화(2) - 인벤토리 구현  (0) 2024.01.11
UNITY - UIManager  (2) 2024.01.11
UNITY - Camera - 3인칭 시점(2)  (4) 2024.01.08
UNITY - Camera - 3인칭 시점(1)  (3) 2024.01.08

+ Recent posts