기존 카드 뒤집기 게임에서 피버타임 때 닷지 게임을 모바일로도 조작하기 위해서는 흔히 사용하는 "가상 조이스틱" 기능이 필요하여 구현했습니다.
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가지로 각각은 아래를 담당합니다.
여기서 가장 중요한 함수는 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로, 가상 조이패드에서 들어온 입력을 가지고, 이를 정규화 시켜서 플레이어의 이동값으로 변환시킵니다.

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