유니티 엔진/입력 처리 심화

유니티 6에서의 입력 처리 - (4) UI ToolKit과 Input System의 연결

monstro 2025. 11. 16. 19:46
728x90
반응형

- 개요

https://www.youtube.com/watch?v=GdjP5pggaHw&list=PLX2vGYjWbI0RpLvO3B7aH-ObfcOifMD20&index=4

 

유니티의 UI ToolKit은 언리얼 엔진의 Rich Text와 유사하게

HTML의 태그와 같은 마크업을 사용하여 UI를 구성할 수 있도록 지원한다

UI Toolkit의 핵심 요소인 UIDocument를 통해 조작할 수 있으며

UIDocument에서 기본 빌딩 블록인 VisualElement를 가져와 태그를 설정할 수 있다

 

 

UI의 구성은 위와 같다

프로젝트의 기본 구성은 다음과 같이 이루어져 있다

  • 하나의 UI 그룹에서는 WASD를 통해 이동 + UI를 클릭하면 이벤트 함수 호출
  • 왼쪽 UI 그룹을 활성화하기 위해서는 Q 키를 입력 + 오른쪽 UI 그룹을 활성화하기 위해서는 E 키를 입력

 

- UIScript 스크립트의 구성

using UnityEngine;
using UnityEngine.UIElements;
using UnityEngine.InputSystem;

public class UIScript : MonoBehaviour
{
    // Action Map에 따른 Input Action들을 저장한 Input Action Asset
    public InputActionAsset InputActions;

    // 왼쪽 UI 그룹에 속해있는 버튼 UI
    Button m_swordButton;
    Button m_axeButton;
    Button m_magicButton;
    Button m_stealthButton;
    
    // 오른쪽 UI 그룹에 속해있는 버튼 UI
    Button m_earthButton;
    Button m_monsterButton;
    Button m_healButton;
    Button m_rageButton;

    // 각 UI 그룹의 UI들을 저장할 배열
    Button[] m_buttonSet1 = new Button[4];
    Button[] m_buttonSet2 = new Button[4];

    private void Awake()
    {
        // UIDocument - VisualElement에 접근하여 마크업 태그를 통한 조작
        VisualElement root = GetComponent<UIDocument>().rootVisualElement;

        // 왼쪽 UI 그룹에서 Button에 해당하는 UI들을 가져오고 배열에 저장
        m_swordButton = root.Q<Button>("sword-icon__button");
        m_axeButton = root.Q<Button>("axe-icon__button");
        m_magicButton = root.Q<Button>("magic-icon__button");
        m_stealthButton = root.Q<Button>("stealth-icon__button");

        m_buttonSet1[0] = m_swordButton;
        m_buttonSet1[1] = m_axeButton;
        m_buttonSet1[2] = m_magicButton;
        m_buttonSet1[3] = m_stealthButton;

        // 오른쪽 UI 그룹에서 Button에 해당하는 UI들을 가져오고 배열에 저장
        m_earthButton = root.Q<Button>("earth-icon__button");
        m_monsterButton = root.Q<Button>("monster-icon__button");
        m_healButton = root.Q<Button>("heal-icon__button");
        m_rageButton = root.Q<Button>("rage-icon__button");

        m_buttonSet2[0] = m_earthButton;
        m_buttonSet2[1] = m_monsterButton;
        m_buttonSet2[2] = m_healButton;
        m_buttonSet2[3] = m_rageButton;
    }

    private void OnEnable()
    {
        ButtonActionSubscribe();
        ButtonSelection();
        InputActions.FindActionMap("UI").Enable();
        InputSwitch();
    }

    private void OnDisable()
    {
        ButtonActionUnsubscribe();
        InputActions.FindActionMap("UI").Disable();
        InputSwitchDisable();
    }

    // 각 UI 그룹의 버튼 UI들의 clicked 이벤트에 콜백 함수들을 연결
    void ButtonActionSubscribe()
    {
        m_swordButton.clicked += onSwordButtonClicked;
        m_axeButton.clicked += onAxeButtonClicked;
        m_magicButton.clicked += onMagicButtonClicked;
        m_stealthButton.clicked += onStealthButtonClicked;

        m_earthButton.clicked += onEarthButtonClicked;
        m_monsterButton.clicked += on_MonsterButtonClicked;
        m_healButton.clicked += onHealButtonClicked;
        m_rageButton.clicked += onRageButtonClicked;
    }

    // 각 UI 그룹의 버튼 UI들의 clicked 이벤트에 연결된 콜백 함수들을 제거
    void ButtonActionUnsubscribe()
    {
        m_swordButton.clicked -= onSwordButtonClicked;
        m_axeButton.clicked -= onAxeButtonClicked;
        m_magicButton.clicked -= onMagicButtonClicked;
        m_stealthButton.clicked -= onStealthButtonClicked;

        m_earthButton.clicked -= onEarthButtonClicked;
        m_monsterButton.clicked -= on_MonsterButtonClicked;
        m_healButton.clicked -= onHealButtonClicked;
        m_rageButton.clicked -= onRageButtonClicked;
    }

    // 콜백함수들의 구성
    void onSwordButtonClicked() => InventoryButtonClicked("Sword");
    void onAxeButtonClicked() => InventoryButtonClicked("Axe");
    void onMagicButtonClicked() => InventoryButtonClicked("Magic");
    void onStealthButtonClicked() => InventoryButtonClicked("Stealth");
    void onEarthButtonClicked() => InventoryButtonClicked("Earth");
    void on_MonsterButtonClicked() => InventoryButtonClicked("Monster");
    void onHealButtonClicked() => InventoryButtonClicked("Heatlh");
    void onRageButtonClicked() => InventoryButtonClicked("Rage");

    // 인자로 전달받은 message + clicked !!!의 문자열을 출력
    void InventoryButtonClicked(string message)
    {
        Debug.Log(message + " clicked !!!");
    }

    // OnEnable 함수에서 최초로 한번 호출되며, 기본적으로 왼쪽 UI 그룹을 활성화하고 첫 번째 인덱스의 버튼을 선택
    void ButtonSelection()
    {
        for (int i = 0; i < m_buttonSet2.Length; i++)
        {
            m_buttonSet2[i].SetEnabled(false);
            // m_ButtonSet2[i].style.display = DisplayStyle.None;
        }

        m_buttonSet1[0].Focus();
    }

    // UI 액션 맵의 RightButtons 액션과 LeftButtons 액션의 performed 이벤트에 콜백 함수들을 연결
    void InputSwitch()
    {
        InputActions.FindAction("UI/ShowRightButtons").performed += RightButtons;
        InputActions.FindAction("UI/ShowLeftButtons").performed += LeftButtons;
    }

    // UI 액션 맵의 RightButtons 액션과 LeftButtons 액션의 performed 이벤트에 연결된 콜백 함수들을 제거
    void InputSwitchDisable()
    {
        InputActions.FindAction("UI/ShowRightButtons").performed -= RightButtons;
        InputActions.FindAction("UI/ShowLeftButtons").performed -= LeftButtons;
    }

    // InputAction.CallbackContext : InputAction의 상태가 변경될 때 발생하는 이벤트에 대한 모든 정보를 담고 있는 구조체
    // 현재 performed 이벤트에 콜백함수를 바인드하였으므로 입력 이벤트 단계를 확인하기 위해 해당 인자 사용

    // 왼쪽 UI 그룹을 활성화하는 함수
    void LeftButtons(InputAction.CallbackContext context)
    {
        for (int i = 0; i < m_buttonSet1.Length; i++)
        {
            m_buttonSet1[i].SetEnabled(true);
            // m_ButtonSet1[i].style.display = DisplayStyle.Flex;
        }
        for (int i = 0; i < m_buttonSet2.Length; i++)
        {
            m_buttonSet2[i].SetEnabled(false);
            // m_ButtonSet2[i].style.display = DisplayStyle.None;
        }
        m_buttonSet1[0].Focus();
    }

    // 오른쪽 UI 그룹을 활성화하는 함수
    void RightButtons(InputAction.CallbackContext context)
    {
        for (int i = 0; i < m_buttonSet1.Length; i++)
        {
            m_buttonSet1[i].SetEnabled(false);
            // m_ButtonSet1[i].style.display = DisplayStyle.None;
        }
        for (int i = 0; i < m_buttonSet2.Length; i++)
        {
            m_buttonSet2[i].SetEnabled(true);
            // m_ButtonSet2[i].style.display = DisplayStyle.Flex;
        }
        m_buttonSet2[0].Focus();
    }
}

 

코드의 구성은 위와 같다

중요하게 알아볼 점은 다음과 같다

  • UIToolKit을 사용하여 구성한 UI에서는 UIDocument - VisualElement를 통해 접근하여 조작
  • Input Action의 입력 단계에 따라 콜백 함수를 다르게 호출해야 하는 경우
    • 콜백 함수의 인자로 InputAction.CallbackContext 사용 

 

- 최종 실행 결과

동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.

 

728x90
반응형