프로젝트/건즈앤 레이첼스

[유니티] 2D 아날로그 터치패드 구현 & 애니메이션

Bueong_E 2023. 2. 26. 01:55
반응형
SMALL

 

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

public class UIJoystick : MonoBehaviour , IBeginDragHandler, IEndDragHandler,IDragHandler
{
    public RectTransform lever;    //레버의(조이스틱) 의 렉트 트랜스폼
    private RectTransform rectTransform; //레버 bg의 렉트 트랜스폼

    [SerializeField, Range(10f, 150f)] //SerializeField 뒤에 "," 를 부팅고 Rage 클래스를 이용해 범위 지정 가능
    private float leverRange; //레버의(조이스틱) 의 렉트 트랜스폼 과 레버 bg의 렉트 트랜스폼 사이의 거리

    private Vector2 inputVector;    //플레이어를 이동시킬 Vector2 형식의 최종값
    private bool isInput;           //터치패드 아날로그가 눌렸는지 아닌지를 알기위한 부울값

    public GameObject player; // 플레이어 움직임 제어를 위한 플레이어 오브젝트 변수

    public float clampedSpeed = 500.0f; // 레버 범위에 따른 플레이어 스피드 제어

    public enum state
    {
        Idle = 0,
        Up = 1,
        Right = 2,
        Down = 3,
        Left = 4

    }

    public void Awake()
    {
        //레버 bg의 렉트 트랜스폼 변수 할당
        this.rectTransform = GetComponent<RectTransform>();
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        //이벤트 데이터 형식 값을 ControlJoystickLever 함수에 할당
        ControlJoystickLever(eventData);   
        //아날로그 패드가 눌렸을떄 부울값 true
        this.isInput = true;
    }

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

    public void OnEndDrag(PointerEventData eventData)
    {
        //아날로그 에서 손을 떼면 레버의 엥커 포지션 초기화(0,0)
        this.lever.anchoredPosition = Vector2.zero;
        //인풋 부울값 false
        this.isInput = false;
    }

    public void ControlJoystickLever(PointerEventData eventData)
    {
        //인풋 방향은 이벤트 데이터의 포지션에서 레버 bg의 렉트 트랜스폼 앵커드포지션 값을 뺀 값을 방향으로 변수에 담기
        Vector2 inputDir = eventData.position - rectTransform.anchoredPosition;
        //조건연산자 ? 는  조건을 기준으로 true라면 ":"의 왼쪽 false라면 오른쪽의 값을 변수에 대입할수 있는 연산자이다.
        //magnitude는 백터의 길이 즉 bg의 앵커 중앙 값부터 터치한 부분까지의 거리를 말함
        //magnitude가 leverRange의 범위를 벗어난다면 normalized한 값(1) 에 레버의 레인지를 곱해 캐릭터의속도가 너무 빨라지는걸 방지
        Vector2 clampedDir = inputDir.magnitude < leverRange ? inputDir          
            : inputDir.normalized * leverRange;
        //레버의 앵커드포지션에 vector2 clampedDir 값 할당 (레버의 범위가 벗어나지 않게)
        this.lever.anchoredPosition = clampedDir;
        //플레이어를 이동시킬 Vector2 형식의 최종값은 앵커드 포지션 / leverRange
        this.inputVector = clampedDir / this.clampedSpeed;
        Debug.Log(this.inputVector);
    }

    private void InputControlVector()
    {
      
            //캐릭터 컨트롤러를 이용하여 플레이어 오브젝트 이동
            this.player.GetComponent<CharacterController>().Move(this.inputVector);  
        
    }

    void Update()
    {
        //인풋이 들어왔다면
        if (isInput)
        {
            //플레이어 이동 함수 실행
            InputControlVector();
        }

        if (this.lever.anchoredPosition.y > 0) this.player.GetComponent<Animator>().SetInteger("state",(int)state.Up);
        if (this.lever.anchoredPosition.x > 0) this.player.GetComponent<Animator>().SetInteger("state", (int)state.Right);
        if (this.lever.anchoredPosition.y < 0) this.player.GetComponent<Animator>().SetInteger("state", (int)state.Down);
        if (this.lever.anchoredPosition.x < 0) this.player.GetComponent<Animator>().SetInteger("state", (int)state.Left);
        if (this.lever.anchoredPosition.x == 0 && this.lever.anchoredPosition.y == 0) this.player.GetComponent<Animator>().SetInteger("state", (int)state.Idle);
    }
}

우선 조건 연산자에 대해 좀더 찾아봐야 할듯하고 magnitude 와 anchoredPosition 의 경우 처음 사용해보는 프로퍼티기에 좀더 익숙해질 필요가 있어보인다.

 

또 애니메이션을 추가해보았는데 아날로그 인풋 기준으로 애니메이션을 정해주니 뭔가 자연스럽지 않은 느낌이 강해 이걸 좀더 자연스럽게 구현하는 방향으로 애니메이션을 연구해 보아야겠다.

 

또 조작감의 경우 아날로그 inputVector의 값을 그대로 사용하니 좀 가속도가 붙는 느낌이 있어 노말라이즈 된 (1로 고정된) 값을 받게 할지 여부도 고민을 해보아야겠다 ( 아케이드 적인 느낌을 살리려면 노말라이즈된 조작감이 좀더 좋을듯하다)

반응형
LIST