반응형

Paint War는 격자 타일 위를 이동하며 영토를 확보하는 게임입니다. 모바일 조이스틱의 입력 방향이 조금만 흔들려도 캐릭터가 인접 타일의 모서리 쪽으로 움직일 수 있기 때문에, 조작 방향을 안정적으로 만드는 작업이 필요했습니다.

1. 왜 조이스틱 방향이 흔들릴까?

조이스틱은 화면에서 손가락이 움직인 위치를 Vector2로 전달합니다. 이 값은 정확히 상·하·좌·우·대각선 8방향으로만 들어오는 것이 아닙니다. 예를 들어 오른쪽으로 움직이려고 해도 입력이 (0.98, 0.12)처럼 조금 기울어질 수 있습니다.

이를 매 프레임 가장 가까운 방향으로 반올림하면 8방향 이동은 가능하지만, 두 방향 사이의 경계에 손가락이 걸렸을 때 방향이 계속 바뀔 수 있습니다. 그래서 입력을 방향으로 바꾸는 과정과 방향을 유지하는 과정을 구분했습니다.

Joystick에서는 배경의 반지름을 기준으로 터치 위치를 정규화하고, 필요하면 45도 단위로 스냅하도록 했습니다. 그러나 실제 이동에 사용할 때는 PlayerMovement에서 방사형 데드존과 방향 유지 보정도 처리합니다.

2. 8방향으로 변환하되 경계에 여유 주기

PlayerMovement_Locomotion.cs의 핵심은 FilterMovementToEightDirections()입니다. 현재 입력 각도를 구해 45도 단위 구역으로 나누되, 이전 구역을 곧바로 버리지 않습니다.

const int sectorCount = 8;
const float sectorSize = 360f / sectorCount;
float angle = Mathf.Repeat(
    Mathf.Atan2(input.y, input.x) * Mathf.Rad2Deg, 360f);
int nearestSector = Mathf.RoundToInt(angle / sectorSize) % sectorCount;

float retainedAngle = retainedMovementSector * sectorSize;
float distanceFromRetained = Mathf.Abs(
    Mathf.DeltaAngle(retainedAngle, angle));
if (distanceFromRetained >
    sectorSize * 0.5f + movementDirectionHysteresisDegrees)
{
    retainedMovementSector = nearestSector;
}

여기서 Atan2()는 입력 벡터가 가리키는 각도를 구합니다. Mathf.RoundToInt()는 그 각도와 가장 가까운 방향을 선택합니다. 중요한 것은 마지막 조건식입니다. 방향 경계에 추가 여유각(movementDirectionHysteresisDegrees)을 두었기 때문에, 아주 작은 입력 변화만으로 직전 방향을 잃지 않습니다. 이런 처리를 히스테리시스(hysteresis)라고 부릅니다.

물론 입력이 충분히 다른 방향으로 움직이면 새 구역으로 변경됩니다. 방향을 고정하는 것이 아니라, 작은 흔들림만 무시하는 방식입니다.

3. 방향 안정화와 격자 코너 보정은 별개

입력을 8방향으로 만드는 것만으로 코너 문제가 모두 해결되는 것은 아닙니다. 자유 이동 방식에서는 격자 중심을 조금씩 벗어나다가 다음 타일 모서리에 걸릴 수 있기 때문입니다.

그래서 이동 방향이 명확한 가로·세로일 때만 GetSoftGridLaneAssistMovement()를 적용했습니다. 현재 타일 중심과 캐릭터의 위치 차이를 구한 뒤, 진행 방향과 수직인 축을 조금씩 보정합니다.

if (Mathf.Min(absX, absZ) * 1.5f >= Mathf.Max(absX, absZ))
    return Vector3.zero;

float maxCorrection = currentSpeed *
    softGridLaneAssistStrength * Time.deltaTime;

첫 번째 조건은 대각선 이동을 보정 대상에서 제외하기 위한 것입니다. 대각선을 누르고 있는데 강제로 타일 중앙으로 끌어당기면 오히려 입력과 다른 방향으로 움직이기 때문입니다. 보정된 위치 역시 CanMoveToWorldPosition()으로 검사한 후 이동에 적용합니다.

조이스틱을 놓았을 때는 HandleSoftGridSnap()이 가까운 타일 중심으로 천천히 이동시키되, 허용 반경 밖이거나 장애물이 있으면 스냅하지 않습니다.


정리

입력의 작은 각도 변화는 방향 유지 보정, 타일 사이의 위치 오차는 격자 레인 보정으로 나누어 처리했습니다. 두 문제를 하나의 스냅 기능으로 해결하려고 하면 대각선 조작까지 뻣뻣해질 수 있습니다. 이번 구현에서는 대각선 이동을 보존하면서 가로·세로 이동의 안정성을 높이는 방향으로 구성했습니다.

다만 모든 코너 충돌이 사라졌는지는 실제 모바일 기기에서 추가 확인이 필요합니다.


참고한 구현 코드

반응형