본문 바로가기

C#,Unity

Unity - 오브젝트 클릭 이벤트 만들기(IPointer,RayCast)

유니티에는 마우스 클릭으로 이벤트를 발생시켜야 할 상황이 많습니다.

기본적으로 UI에 Button 컴포넌트가 있지만 이는 UI에만 적용 가능하고 오브젝트에서 사용 가능한 버튼 컴포넌트가 따로 존재하지는 않습니다.

 

이번에는 UI가 아닌 오브젝트를 클릭해 이벤트를 발생시키는 스크립트와,

해당 스크립트를 이용해 Button 컴포넌트 없이 발동하는 UI 버튼을 만들어 보겠습니다.

 

using UnityEngine.EventSystems;

public class test : MonoBehaviour, IPointerClickHandler
{
  [SerializeField] private TextMeshProUGUI MyTMp = null;
  public void OnPointerClick(PointerEventData eventData)
  {
    MyTMp.text = "Lehu " + Count++.ToString();
  }
  private int Count = 0;
}

우선 IPointerClickHander를 사용한 클릭 이벤트입니다.

해당 오브젝트에는 Button 컴포넌트가 존재하지 않습니다.

 

Button 컴포넌트가 없는 Image를 클릭하니 클릭 이벤트가 발생하는 것을 볼 수 있습니다.

물론 그 위에 다른 이미지가 올라가 있거나, RayCast(혹은 CanvasGroup의 BlockRayCast)가 False로 설정되어 있으면 IPointerClick은 반응하지 않습니다.

 

이것이 첫 번째 방법입니다.

 

두 번째 방법은 좀 더 단순무식한 방법으로, 마우스 클릭 시 해당 위치의 오브젝트들을 감지하는 방식입니다.

요건 UI뿐만 아니라 오브젝트도 감지하기 때문에 일단 오브젝트를 버튼처럼 써먹을 수도 있습니다.

 

public class mouse : MonoBehaviour
{
  private Dictionary<string, Action<GameObject>> ClickCheckEvent = new Dictionary<string, Action<GameObject>>();
  //클릭마다 오브젝트들의 태그를 확인해 
  private void Awake()
  {
    ClickCheckEvent.Add("UITag", (obj) => MyTMP.text = obj.name);
  }
  private void Update()
  {
  	//대충 마우스 커서 움직이게 하는 수식들(생략)
    
    if (Input.GetMouseButtonDown(0))
    {
      Click();
    }
  }
  [SerializeField] private TextMeshProUGUI MyTMP = null;
  [SerializeField] private EventSystem MyEventSystem = null;
  [SerializeField] private GraphicRaycaster GraphicRaycaster = null;
  private PointerEventData MyPointerEventData = null;

  private void Click()
  {
    MyPointerEventData = new PointerEventData(MyEventSystem);

    MyPointerEventData.position = Input.mousePosition;

    List<RaycastResult> _rayresults = new List<RaycastResult>();

    GraphicRaycaster.Raycast(MyPointerEventData, _rayresults);

	//태그를 인식해 해당 태그에 할당된 메소드를 실행
    foreach (var _target in _rayresults)
    {
      if (ClickCheckEvent.ContainsKey(_target.gameObject.tag)) ClickCheckEvent[_target.gameObject.tag](_target.gameObject);
    }
  }
}

Awake에서 클릭 시 인식한 태그에 따라 무슨 메소드를 실행할 지 미리 입력해 두었습니다.

위 스크립트에서는 마우스 클릭 시 "UITag"를 감지하면 화면에 해당 오브젝트의 이름을 출력하도록 설정했습니다.

Graphic Raycast는 기본 캔버스에 들어가 있으니 해당 컴포넌트를 넣어주면 됩니다.

 

Tag가 UITag이며 아무런 스크립트도 없는 Image 오브젝트를 클릭해보겠습니다.

 

버튼도, IPointer 스크립트도 없는 이미지 UI를 클릭해 이벤트를 발생시켰습니다.

 

 

본인은 타일맵을 활용하는 과정에서 두번째 기능(RayCast)를 활용했습니다.

원래 각 타일마다 버튼 컴포넌트를 할당해 이동 경로를 선택하는 방식으로 썼는데 지도가 커질수록 쓰지 않으면서도 활성화되있는 오브젝트들이 너무 많아

차라리 각 버튼들을 없애고 마우스에서 총체적으로 관리하는게 훨씬 나을 것 같았습니다.

        if (CheckObjTag("TilePanel") != null)
        {
          MouseState = MouseStateEnum.DragMap;
          LastPos = Camera.main.ScreenToViewportPoint(Input.mousePosition);

          if (CheckObjTag("Tile") != null)
          {
            string _coordinate = CheckObjTag("Tile").name;
            TileData _tile = GameManager.Instance.MyGameData.MyMapData.TileDatas[
              int.Parse(_coordinate.Split(',')[0]), int.Parse(_coordinate.Split(',')[1])];
            SelectingTile = !_tile.Interactable || _tile.Fogstate != 2 ? null : _tile;

            ClickPosition = Input.mousePosition;
          }
        }

그래서 타일 하나하나에 버튼을 넣지 않고 마우스를 클릭할 때마다 타일 여부를 감지한 뒤 타일이 존재할 시 기존 타일 클릭 메소드를 호출하게 하였습니다.

해당 움짤로 설명하기는 그렇지만 아무튼 기존에 비해서 렉이 훨씬 나아지기는 했었습니다.

 

역시 뭐든 쓰기 나름입니다.