본문 바로가기

Unity

[Unity3D]JoyStick(D pad)캐릭터 이동 컨트롤러 제작(마무리)

1. 서론

없다. 바로 본론을 보자.

이전 글은 아래에...

https://s-sense.tistory.com/16

 

[Unity3D]JoyStick(D pad)캐릭터 이동 컨트롤러 제작(1)

1. 서론 오랜만에 유니티 작업으로 찾아왔다. 거의 세 달 만 인 것 같은데... 블로그에 어떤 자료를 올릴까 고민이 많았다. 필자는 전부 다 테스트하고 정말 쓸만 한것들만 올리자는 명목 하에 블로그를 개설한 것..

s-sense.tistory.com

2. 본론

코드가 너무 길다. 중간중간 주석처리를 해놓았고, 자세한 설명은 아래를 확인하길 바란다.

Controller.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;//이벤트 핸들러 사용 using

public class Controller : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IDragHandler, IEndDragHandler, IPointerClickHandler
{
	//이벤트 핸들러 enum값
    public enum eEventHandle{Click, Drag }
    public eEventHandle m_ePrevEvent;
	
    //이번 글에서 사용하진 않았다. 자세한 설명은 블로그 아래
    private RectTransform m_BackGround;

	//조이스틱과 조이스틱 백그라운드 오브젝트
    public GameObject m_JoyStickBackGround;
    public GameObject m_JoyStick;

	//겟컴포넌트 하기 귀찮아서 따로 빼놓았다.
    private RectTransform m_TransJoyStickBackGround;
    private RectTransform m_TransJoyStick;

	//포지션값을 따로 저장 하기 위함.
    public Vector2 m_VecJoystickValue { get; private set; }
    public Vector3 m_VecJoyRotValue { get; private set; }
    
    //조이스틱의 범위계산을 위한 반지름 값.
    private float m_fRadius;
    
    //예시 상태 값.
    public enum ePlayerState { Idle, Attack, Move, End }
    public ePlayerState m_ePlayerState { get; private set; }

	
	private void Awake()
    {
        Init();//초기화
    }
    
    //이벤트들
    #region event
    public void OnPointerClick(PointerEventData eventData)
    {
        SetPlayerState(ePlayerState.Idle);
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        CallJoyStick(eventData);
        SetHandleState(eEventHandle.Click);
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        m_JoyStickBackGround.SetActive(false);

        if (m_ePrevEvent == eEventHandle.Drag)
            return;

        SetPlayerState(ePlayerState.Attack);
        SetHandleState(eEventHandle.Click);
    }

    public void OnDrag(PointerEventData eventData)
    {
        JoyStickMove(eventData);
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        JoyStickMoveEnd(eventData);
    }
    #endregion

    private void Init()
    {
        m_TransJoyStickBackGround = m_JoyStickBackGround.GetComponent<RectTransform>();
        m_TransJoyStick = m_JoyStick.GetComponent<RectTransform>();
        m_fRadius = m_TransJoyStickBackGround.rect.width * 0.5f; //조이스틱의 행동반경 계산

        m_JoyStick.SetActive(true);
        m_JoyStickBackGround.SetActive(false);
    }
    
    private void JoyStickMoveEnd(PointerEventData eventData)
    {
        m_TransJoyStick.position = eventData.position;
        m_JoyStickBackGround.SetActive(false);

        SetHandleState(eEventHandle.Click);
        SetPlayerState(ePlayerState.Idle);
    }
    
    private void CallJoyStick(PointerEventData eventData)
    {
        m_JoyStickBackGround.transform.position = eventData.position;
        m_JoyStick.transform.position = eventData.position;
        m_JoyStickBackGround.SetActive(true);
    }
    
    private void JoyStickMove(PointerEventData eventData)
    {
        m_VecJoystickValue = eventData.position - (Vector2)m_TransJoyStickBackGround.position;

        m_VecJoystickValue = Vector2.ClampMagnitude(m_VecJoystickValue, m_fRadius);
        m_TransJoyStick.localPosition = m_VecJoystickValue;

        m_VecJoyRotValue = new Vector3(m_TransJoyStick.localPosition.x, 0f, m_TransJoyStick.localPosition.y);

        SetHandleState(eEventHandle.Drag);
        SetPlayerState(ePlayerState.Move);
    }
    
    private void SetHandleState(eEventHandle _handle)
    {
        m_ePrevEvent = _handle;
    }
    
    private void SetPlayerState(ePlayerState _state)
    {
        m_ePlayerState = _state;
    }

}

여기서 이벤트를 사용해야 하는 중의 가장 중요한 것은 호출 순서인 것이다.

필자는 이것 때문에 개 빡 치는 일이 발생하였으나 여러분들은 발생하지 않길 바란다.

Down -> BeginDrag(있을 시) -> Drag(있을 시) -> DragEnd(있을 시) ->Up -> Click 순으로 호출된다.

어쩌면 당연한 얘기겠만... 원하는 상황이 나오질 않아 별에 별 방법을 다 썼다는 후문이다.

 

코드의 흐름은 단순하다.

제일 처음 Awake() 안에 Init() 함수에서 초기화를 한다. RectTransform, 반지름, 조이스틱이 켜고 꺼질 상태 등등을 초기 값을 세팅한다.

 

그다음 OnPointerDownOnPointerDown(PointerEventData eventData)에서 조이스틱, 조이스틱 백그라운드 위치를 이벤트가 발생한 지점으로 맞춰준다. 그와 동시에 SetActive(true)를 통해 보여준다.

 

 

OnPointerUp(PointerEventData eventData)에서는 다시 조이스틱 백그라운드를 SetActive(false)를 해준다.

마우스 클릭이 떼 졌기 때문에 처음 상태로 되돌려 주는 것이다.

 

드래그가 시작되는 부분은 필요 없기 때문에 OnDrag(PointerEventData eventData)로 바로 들어온다.

JoyStickMove()에서 이벤트 포지션과 조이스틱 백그라운드 위치를 빼주어 조이스틱의 벨류 값을 얻어온다.

 

그 후 ClampMagnitude 메서드에서 조이스틱의 위치와 반지름 사이를 클램핑 하여 조이스틱 벨류의 범위를 정해준다.

조이스틱 벨류를 조이스틱의 로컬 로케이션 값에 넣어주면 조이스틱 이미지가 백그라운드 범위 안에 갇히게 되는 것이다.

 

그냥 쉽게 말해서 이벤트 발생지점으로부터 조이스틱 백그라운드까지 얼마나 떨어져 있는가 계산 후에 조이스틱에 넣어주는 것이다. 어차피 범위 안에 있을 테니 조이스틱은 백그라운드 끝에 걸리겠다.

 

m_VecJoyRotValue 회전 값까지 구해놓는 센스.

 

OnEndDrag(PointerEventData eventData) 이것과 OnPointerUp(PointerEventData eventData) 사실 다를 바 없다. 다시 돌려놓는 이벤트

 

이러면 클릭하면 생기고 떼면 사라지는 컨트롤러가 완성되었다.

 

3. 결론

https://youtu.be/08 XHsblFYj0

동영상1. 유니티 컨트롤러 시범영상

 

질문은 댓글로 남겨주길 바란다.