멈추지 않고 끈질기게

[Unity] 클릭/터치 인터페이스(IPointerClickHandler, IPointerDownHandler, IPointerUpHandler) 본문

Unity

[Unity] 클릭/터치 인터페이스(IPointerClickHandler, IPointerDownHandler, IPointerUpHandler)

sam0308 2023. 9. 6. 16:29

※ 해당 포스팅은 개인의 공부 정리용 글입니다. 틀린 내용이 있다면 추후 수정될 수 있습니다.

※ 해당 포스팅은 Unity 2021.3.15f1 버전을 기준으로 작성되었습니다.

 

 

 

이번 포스팅에서는 유니티에서 지원하는 클릭/터치 관련 인터페이스 3종에 대해 알아보겠습니다.

1. 인터페이스 기능

 IPointerClickHandler, IPointerDownHandler, IPointerUpHandler는 모두 UnityEngine.EventSystems에 포함된 인터페이스 들로, 이벤트 시스템에 영향을 받는 UI 오브젝트에 적용할 수 있는 클릭/터치 관련 인터페이스입니다. 각 인터페이스는 PointerEventData 클래스 타입의 매개변수를 받는 함수(OnPointerClick(), OnPointerDown(), OnPointerUp())를 구현해주어야 합니다. PointerEventData 클래스는 클릭한 위치(position), 마지막으로 클릭한 시간(clickTime), 연속 클릭한 횟수(clickCount) 등의 정보를 포함하므로 필요하다면 활용할 수 있습니다.

 

using UnityEngine;
using UnityEngine.EventSystems;

public class InterfaceTest : MonoBehaviour, IPointerClickHandler, IPointerDownHandler, IPointerUpHandler
{
    public void OnPointerClick(PointerEventData eventData)
    {
        Debug.Log("OnPointerClick");
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        Debug.Log("OnPointerDown");
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        Debug.Log("OnPointerUp");
    }
}

사진 1. 인터페이스 함수 호출 순서

 

 OnPointerDown() 함수는 해당 오브젝트 위에서 마우스 버튼이 내려가는 순간, OnPointerUp() 함수는 해당 오브젝트 위에서  눌렀던 마우스 버튼을 떼는 순간, OnPointerClick()은 마우스 클릭이 완료되는(버튼을 눌렀다가 떼는) 순간 호출됩니다. 3가지 모두 구현한 오브젝트 위에서 클릭할 경우, OnPointerDown() -> OnPointerUp() -> OnPointerClick() 순서로 호출됩니다. 단, 다른 위치에서 마우스 버튼을 누른 상태로 드래그하여 해당 오브젝트 위에서 뗄 경우에는 OnPointerUp(), OnPointerClick()가 호출되지 않습니다.

 

 해당 오브젝트를 일반적인 버튼처럼 사용하려면 IPointerClickHandler를 상속받아 OnPointerClick()만 구현해도 되고, 꾹 누르는 조작에 대응하려면 IPointerDownHandler, IPointerUpHandler를 상속받아 사용하면 됩니다. 

 

 

2. 사용 예시

 다음은 IPointerClickHandler, IPointerDownHandler, IPointerUpHandler의 사용 예시입니다.

using System;
using UnityEngine;
using UnityEngine.EventSystems;

public class ButtonHandler : MonoBehaviour, IPointerClickHandler, IPointerDownHandler, IPointerUpHandler
{
    public Action OnClicked;
    public Action OnPressed;
    bool _isPressed = false;

    void Update()
    {
        if(_isPressed)
            OnPressed?.Invoke();
    }

    public void OnPointerClick(PointerEventData eventData)
    {
        OnClicked?.Invoke();
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        _isPressed = true;
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        _isPressed = false;
    }
}

 우선 다음과 같은 버튼 핸들러 클래스를 선언해줍니다. 3종 인터페이스를 상속받아 구현했고, 클릭 시 실행할 대리자 OnClicked누르고 있는 동안 실행할 대리자 OnPressed에 콜백함수를 외부에서 등록할 수 있도록 public으로 선언하였습니다. OnPointerClick()은 클릭 시 OnClicked를 실행하도록 구현했고, OnPointerDown() 및 onPointerUp()에서는 버튼을 누르고 있는지 여부(_isPressed)를 설정하도록 했습니다. 그리고 Update()에서 매 프레임마다 버튼을 누르고 있는 상태라면 OnPressed를 실행하도록 했습니다.

 

using UnityEngine;
using TMPro;

public class AutoCanvas : MonoBehaviour
{
    enum Buttons
    {
        StartButton,
        PointButton,
        ExitButton
    }

    enum Texts
    {
        ScoreText
    }
    TextMeshProUGUI _pointText;

    int _score = 0;

    void Awake()
    {
        // 텍스트 설정
        _pointText = transform.Find(Texts.ScoreText.ToString()).GetComponent<TextMeshProUGUI>();

        // 버튼 설정
        GameObject startBtn = transform.Find(Buttons.StartButton.ToString()).gameObject;
        GameObject pointBtn = transform.Find(Buttons.PointButton.ToString()).gameObject;
        GameObject exitBtn = transform.Find(Buttons.ExitButton.ToString()).gameObject;

        ButtonHandler startHandler = startBtn.AddComponent<ButtonHandler>();
        ButtonHandler pointHandler = pointBtn.AddComponent<ButtonHandler>();
        ButtonHandler exitHandler = exitBtn.AddComponent<ButtonHandler>();

        startHandler.OnClicked += () => { Debug.Log("GameStart"); };
        pointHandler.OnPressed += () =>
        {
            _score++;
            _pointText.text = string.Format("{0:000000}", _score);
        };
        exitHandler.OnClicked += () => {
            Debug.Log("Exit");
            UnityEditor.EditorApplication.isPlaying = false; 

        };
    }
}

 그리고 Canvas에 버튼 설정을 스크립트로 제어하는 클래스를 추가했습니다. 텍스트 및 버튼 이름들을 열거형으로 선언한 뒤, Transform.Find()의 매개변수로 사용하여 추적했습니다. 버튼 오브젝트에는 AddComponent()를 통해 위에 작성한 ButtonHandler를 추가하고, OnClicked 및 OnPressed에 람다식으로 콜백함수를 추가하였습니다. 위 예시에서는 Start 버튼은 클릭 시 로그 출력, Point버튼은 누르는 동안 스코어 증가 및 텍스트 갱신, Exit버튼은 클릭 시 로그 출력 및 에뮬레이터를 종료하도록 했습니다. 

 

 다음은 상기 예제 코드의 실행 모습입니다.

 

영상 1. IPointerClickHandler, IPointerDownHandler, IPointerUpHandler 사용 예시

 

 

3. 결론

 기존에는 버튼을 만들 때 인스펙터 창에서 OnClick 핸들러를 직접 등록해주는 식으로 사용했었는데, 해당 함수의 이름을 바꾸거나 연결한 오브젝트의 이름을 변경할 경우 연결이 해제되어 다시 일일히 등록해주어야 하는 경우가 많았습니다.

 

 현재 제작중인 프로젝트에서는 IPointerClickHandler와 같은 인터페이스를 사용함으로서 UI를 좀 더 스크립트에서 직접 제어함으로서 이러한 불편함이 없어졌습니다. 기존의 UI를 [SerializeField]로 직접 등록하여 사용하던 부분도 다른 사람의 코드를 참조하여 스크립트로 찾아서 자동으로 등록하는 방식으로 진행해보고 있습니다. 좀 더 공부하여 기존의 수동으로 해결하던 부분들을 스크립트로 제어할 수 있도록 해야겠습니다.