[유니티] 벡터를 활용한 플레이어 이동 구현, 오브젝트가 떨리는 버그 원인과 해결 방법

728x90

플레이어의 상/하/좌/우 움직임과 적이 플레이어를 따라다니도록 설정하는 코드 구현 시 몇몇 새로 알게된 사실이 있다. 쉽게 넘어갈 수도 있는 내용이지만 사실 굉장히 중요한 내용이고, 이를 바탕으로 응용할 수 있는 내용들이 많은 것 같아 정리하려고 한다. 

 

Vector3를 사용하여 오브젝트 이동 구현하기

float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");

// 방법 1
Vector3 dir = Vector3.right * h + Vector3.up * v;
transform.position += dir * speed * Time.deltaTime;

// 방법 2
Vector3 dir = new Vector3(h, v, 0);
transform.position += dir * speed * Time.deltaTime;

// 이동식 공통
// transform.Translate(dir * speed * Time.deltaTime);

Vector3.right는 벡터 좌표에서 x축 방향으로 (1, 0, 0)을 의미한다.

Vector3.up은 벡터 좌표에서 y축 방향으로 (0, 1, 0)을 의미한다.

즉, Vector3.right * h는 (h, 0, 0)을, Vector3.up * v은 (0, v, 0)을 의미하므로

두 좌표를 한 값은 (h, v, 0)이 나오게 된다.

따라서 방향을 구하는 두 코드는 같은 의미를 가진다.

Vector3 dir = Vector3.right * h + Vector.up * v;
Vector3 dir = new Vector3(h, v, 0);

Vector3.right = (1, 0, 0)        Vector3.left = (-1, 0, 0)

Vector3.up = (0, 1, 0)           Vector3.down = (0, -1, 0)

Vector3.forward = (0, 0, 1)   Vector3.back = (0, 0, -1)

※ 반대 방향으로 이동하고 싶으면 -1을 곱해 음수를 만들어 주면서 응용하면 된다

 

벡터의 정규화 Normalize

타겟이 플레이어를 따라다니게 하기 위한 방향은 target의 위치 - 플레이어의 위치 이므로 다음과 같이 만들 수 있다.

Vector3 dir = target.transform.position - transform.position;

하지만 이동 공식인 P = P0 + vt에 대입해보면 좌/우 이동 보다 대각선으로 이동할 때 속도가 더 빠른 것을 알 수 있다.

 

이는 피타고라스 정리에 의해 45도 대각선으로 움직일 때는 기본값 1 * h 가 아닌 1.414 * h 의 힘을 받기 때문에 Vector3의 크기에 따라 속도가 변해지는 문제가 생기는 것이다. 따라서 이동하는 각도에 따라 변하는 크기를 1로 정규화시켜주어야 한다. 이를 위해 유니티에서는 Normalize() 메서드를 제공해주고 있다.

dir.Normalize();

  

 

오브젝트가 떨리는 버그 발생

적 오브젝트가 플레이어 오브젝트를 따라 잡은 경우 자세히 보면 다음과 같이 계속 진동하는 버그가 발생하고 있다. 카메라 시점을 가까이 둔 경우 반드시 해결해야하는 문제이므로 문제 발생의 원인과 해결방법을 찾아보았다.

버그 발생 원인

두 오브젝트가 겹칠때 나타나는 진동은 적 오브젝트가 이동하는 힘의 크기가 다르기 때문이다. 플레이어가 위치한 정확한 포지션 (x, y, z) 값에 적 오브젝트가 정확하게 일치하면 상관 없지만 P = P0 + vt에서 V의 값(방향은 상관 없지만 크기가 문제)이 미세하게라도 다르게 된다. 즉, Update()가 수행되는 과정에서 매 프레임마다 이동한 후 최종 목적지에 도달해야하지만, 최종 프레임 실행 후 목적지를 통과할 가능성이 높기 때문에 통과한 경우, 다시 목적지를 향해 반대로 돌아가고, 또 다시 반대로 이동하는 행위를 반복하게 된다.(위 이미지 참고) 이로 인해 결국 화면 상에는 오브젝트가 계속 떨리는 것처럼 보이게 되는 것이다.

 

버그 해결 방법

그렇다면 어떻게 해결해야 할까?

가장 보편적인 방법은 어느 시점에 도착했을 때, 도착했다고 가정을 하고 이동을 멈추도록 만들면 된다. 두 오브젝트의 거리를 구해 그 사이 값이 특정 값 이하일 경우 움직임을 종료해주면 되겠다. 코드로 작성해 보면 다음과 같다.

float distance < dir.magnitude;
// magnitude : 두 좌표값 사이의 직선 거리

if(distance < 0.1f)
	transform.position = target.position;
else
	transform.position += dir * speed * Time.deltaTime;

플레이어와 적 오브젝트 사이의 거리(magnitude)를 구하고 그 거리가 특정 거리(0.1) 미만이면 플레이어의 포지션과 적의 에너미를 같게 만들어주고, 그렇지 않다면 계속 이동하도록 if문을 돌리면 된다.

 

※ 해당 함수를 활용해서 다음과 같이 파티클 효과처럼 확장해볼 수도 있다.

 

 

 

728x90