[25.04.08] TIL( 프로젝트 개발 - 가상 조이스틱 )

설민우·2025년 4월 8일

내일배움캠프 - Unity

목록 보기
17/85

가상 조이스틱 만들기

기존 카드 뒤집기 게임에서 피버타임 때 닷지 게임을 모바일로도 조작하기 위해서는 흔히 사용하는 "가상 조이스틱" 기능이 필요하여 구현했습니다.

    public void OnDrag(PointerEventData eventData)
    {
        // 스크린 좌표 -> ui의 로컬 좌표
        // out은 드래그 대상 내부 좌표계에서의 위치, 캔버스=overlay 이기 때문에 null 이어도 괜찮음
        if (RectTransformUtility.ScreenPointToLocalPointInRectangle(transfrom, eventData.position, null, out Vector2 localPoint))
        {
            if (localPoint.magnitude < this.stickRange)
            {
                this.joystickHandle.transform.localPosition = localPoint;
            }
            else   // handleRange의 값 만큼 고정합니다.
            {
                this.joystickHandle.transform.localPosition = localPoint.normalized * this.stickRange;
            }

            this.input = localPoint;
        }
    }

    // 포인터 누르면 드래그 시작
    public void OnPointerDown(PointerEventData eventData)
    {
        OnDrag(eventData);
    }

    // 포인터 놓기 -> 인풋 초기화 및 커서 중앙 이동
    public void OnPointerUp(PointerEventData eventData)
    {
        input = Vector2.zero;
        joystickHandle.anchoredPosition = Vector2.zero;
    }

가상 조이스택을 구현하는데 있어 추가해야할 인터페이스는 Drag, PointerUp, PointerDown 3가지로 각각은 아래를 담당합니다.

  • PointerDown : 마우스를 클릭했을 때 가상 조이스틱의 조작을 시작합니다
  • Drag : 마우스 위치를 ScreenPointToLocalPointInRectangle 로 로컬 좌표로 변환해 PlayerController에게 전달합니다.
  • PointerUp : 마우스 클릭을 해제했을때, 가상 조이스틱의 Handle 부분을 영점으로 초기화 합니다.

여기서 가장 중요한 함수는 RectTransformUtility의 ScreenPointToLocalPointInRectangle 함수 입니다
이의 매개변수 구조는 아래와 같습니다

public static bool ScreenPointToLocalPointInRectangle(
    RectTransform rect,  // 로컬 좌표로 구하기를 원하는 rectTransform, 여기서는 가상 조이스틱(상위)
    Vector2 screenPoint, //  스크린 에서의 마우스 포인트 위치
    Camera cam,  // 현재 사용하고 있는 카메라
    out Vector2 localPoint // 반환되는 로컬 좌표 위치
);

이는 마우스의 위치를 원하는 카메라 기준으로 가져와, rectTransfrom에서의 로컬 좌표값으로 변환 시킵니다.
또한 이 값을 Range로 조절함으로써 조이스틱이 뻗어나갈 수 있는 한계선을 결정지어 놓습니다.

좌표값이 구해졌으면 그 다음은 더 쉽습니다

    void Update()
    {
        Vector2 input = new Vector2(joystick.horizontal, joystick.vertical);

        // magnitude 비율 계산 (0 ~ 1), 최대값은 1로 제한한다.
        float magnitude = Mathf.Min(input.magnitude / joystick.stickRange, 1f);

        // 데드존 처리
        if (magnitude < deadZone)
            magnitude = 0f;

        // 방향과 크기 결합
        Vector2 ratioInput = input.normalized * magnitude;

        // 이동
        transform.position += (Vector3)(ratioInput * speed * Time.deltaTime);
    }

위의 코드는 PlayerController로, 가상 조이패드에서 들어온 입력을 가지고, 이를 정규화 시켜서 플레이어의 이동값으로 변환시킵니다.

  • 데드존은 가상 조이스틱의 핸들이 너무 작게 움직였을때의 이동을 막아줍니다.
  • magnitude 값의 최대는 1로 고정시켜 속도가 원하는값 이상으로 올라가지 않게 조정합니다.
  • 조정된 크기와 방향을 다시 재 결합하여 이동 방향으로 설정합니다.

결론

  • 이번 프로젝트 말고 과거의 다른 프로젝트에서도 가상 조이스틱을 구현해 본 적이 있습니다. 해당 조이스틱의 경우, 우물 정 자 처럼 구획을 나누어 해당 구획에 넘어오면 그 방향으로 이동하도록(8 방향) 조이스틱을 구현 했었는데, 이렇게 기본적인 이동에 관련된 가상 조이스틱을 만들어 본 것은 새로운 경험이었습니다.
  • 특히, 조작감에 있어서 최초에는 방향과 크기를 한꺼번에 결정짓는 방법을 사용했는데, clamp 기능이 없으니 속도값이 이상하게 변화하고, 미끄러지는 듯한 느낌이 들었었습니다. 이를 해결하는 방향으로 다시 인풋 처리를 구현하여 좀 더 원활하게 닷지 게임의 총알을 피할 수 있도록 하였습니다.
profile
클라이언트 개발자를 지망하고 있습니다.

0개의 댓글