본문 바로가기

게임 프로그래밍 (Game Programming)/유니티 (Unity)

Let's move my character Chapter2. More accurate calculations! 유니티 일정한 속력으로 이동하기!

https://dnddkqja21.tistory.com/40

 

Let's move my character in the 3D world! 움직이는 캐릭터 구현

https://dnddkqja21.tistory.com/39 Let's decorate the Unity project to my taste! 내 취향에 맞게 프로젝트 꾸미기 도전! 지난 시간에 함께 배웠던 유니티 에디터 커스텀을 했다면 위 사진과 비슷하게 보일 겁니다.

dnddkqja21.tistory.com

 

지난 시간에는 3D월드에서 키보드 입력에 의해 플레이어를 이동시키는 것을 배워보았습니다.

 

아마 눈치를 채신 분들도 있겠지만 지난 프로젝트는 플레이어가 일정한 속력으로 움직일 수 없습니다.

오늘을 위해 정확하게 일정한 속력으로 이동하는 것은 다루지 않았습니다.

그러면 지난 시간에 했던 프로젝트에 속력을 볼 수 있는 UI를 만들어서 확인해봅시다!

 

 

UI - Text - TextMeshPro 를 생성해주세요.

 

 

씬탭과 게임탭을 보며 인스펙터에서 원하는 크기와 모양, 위치 등을 수정해주세요.

 

[SerializeField]
TextMeshProUGUI speedText;

Vector3 originPos;
float timer;

이번에 우리가 추가할 변수는 총 3가지입니다.

첫 번째로 우리가 생성한 UI인 TextMeshPro를 관리할 변수입니다.

Canvas 자식으로 관리가 되는 UI들은 스크립트에서 관리할 때 꼭 뒤에 UGUI가 붙은 TextMeshProUGUI를 골라주세요!

두 번째는 플레이어의 이전 프레임에서의 좌표를 담기위한 originPos변수입니다. 위치는 x, y, z 로 구성되어 있기 때문에

Vector3형 변수를 선언하셔야 합니다.

마지막으로 float 형 timer 변수를 선언해주었습니다.

 

오늘 배울 챕터의 내용은 일정한 속력이 키워드인데요. 

속력이란 무엇일까요?

속력은 크기를 가진 시간당 이동거리의 값을 이야기합니다.

이동거리를 걸린 시간으로 나누어 값을 구할 수 있습니다.

 

예를 들어 한국에서 미국까지 거리가 약 11,000km 일 때, 비행기로 15시간이 걸렸다면 비행기의 속력은

11,000 / 15 = 733.333... 이므로 약 733km/h가 되는 것입니다.

 

우리는 이 공식을 통해 플레이어의 속력을 구한 뒤 확인해볼 겁니다.

 

void Start()
{
    originPos = transform.position;
    timer = 0;
}

 

우선 Start() 함수에서 originPos의 변수를 플레이어의 포지션으로 초기화한 뒤 timer도 0으로 초기화 해줍시다.

 

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

    //Debug.Log("h : " + h + " / " + "v : " + v);

    transform.Translate(new Vector3(h, 0, v) * moveSpeed * Time.deltaTime);

    // 속도
    timer += Time.deltaTime;
    float dis = Vector3.Distance(originPos, transform.position);
    float speed = dis / timer;

    originPos = transform.position;
    timer = 0;

    speedText.text = "speed : " + string.Format("{0:F1}", speed);
}

지난 시간에 이어서 Update() 함수에 추가로 코드를 작성할 겁니다.

 

timer 변수에 매 프레임 Time.deltaTime을 누적시켜 이동하는데 걸린 소요 시간을 구할 겁니다.

 

다음으로는 한 프레임에 얼마나 플레이어가 이동했는지를 구할 겁니다. 

다행히도 유니티에서 제공하는 기능이 있습니다. Vector3.Distance인데요. 

위에서 선언했던 originPos 와 현재 나의 포지션인 transform.position을 파라미터로 전달하면 두 지점 사이의 거리를

flaot형 값으로 반환해 줍니다.

 

우리는 거리와 소요 시간을 알게되었으니 위에서 다루었던 속력을 구하는 공식을 그대로 사용해보겠습니다.

float형 speed 변수에 dis / timer를 해주어 속력을 구할 겁니다.

 

이제 속력이 구해졌으니 현재 프레임에서의 나의 위치를 다시 originPos에 담아주고 timer를 0으로 초기화 해줍니다.

 

마지막으로 UI에 표시를 해줘야겠죠.

speedText에 우리가 구한 속력을 적용시켜줍니다.

string.Format() 은 C#에서 제공하는 기능으로 개체의 값을 문자열로 변환합니다.

여기서 사용한 {0:F1} 중 0의 의미는 첫 번째 인자로 받은 speed의 값을 사용한다는 의미이며

F1은 소숫점 첫 째 자리까지만 출력하겠다는 의미입니다.

 

 

이제 스크립트를 저장한 뒤 speedText 변수에 TextMeshProUGUI 를 드래그 앤 드롭하여 할당해줍니다. 

 

 

 

에디터를 실행하면 위와 같이 속력이 출력되고 있습니다.

현재는 움직이지 않고 있기 때문에 0.0으로 표시되네요.

키보드로 좌, 우 또는 상, 하 입력을 하여 움직여봅시다.

 

 

 

키보드의 방향키를 누르고 있으면 0.0에서 3.0까지 속력이 증가하는 것을 확인할 수 있습니다.

 

지난 시간에 키보드 방향키 입력은 방향에 따라 0에서 시작하여  -1 또는 1의 값을 반환한다고 배웠죠.

추가로 우리가 인스펙터에서 지정해주었던 3이라는 moveSpeed를 곱하여 나온 값입니다.

속력은 크기만 가지고 있기 때문에 방향은 존재하지 않습니다. 따라서 -(음수) 값이 나올 수가 없죠.

 

아직까지는 아무 이상이 없는 것 처럼 보이지만 일정한 속력으로 이동시키기 위해서는 아직 한 단계가 더 남아 있습니다.

자, 여기서 대각선 이동을 한 번 해볼까요???  어떠한 결과가 나타날까요???

 

 

 

직선으로 이동할 때 3.0의 속력이 나왔었는데 대각선 이동을 하니 4.2라는 값이 나왔습니다.

실제로도 여러분이 대각선 이동을 할 때 직선 이동보다 빠르다는 것을 몸으로 느끼실 수 있을 거예요.

이상태로 게임을 출시한다면 아마 유저들은 이 점을 금방 눈치채고 대각선으로만 이동을 하여 직선 이동보다 빠르게 원하는 목표 지점까지 이동하겠죠. 

이는 제작자의 의도가 아니므로 원인을 알아볼게요!

 

 

 

직각 삼각형을 보니 어떠한 공식이 하나 떠오르지 않나요?

바로 학창시절에 배웠던 피타고라스의 정리입니다.

 

밑변 a와 높이 b의 길이를 알 때 빗변 c 의 길이를 구하는 공식은 

a² + b² = c²입니다.아주 잘 알려진 공식이죠! 아마 수학시간에 배웠는데 사용하지 않아서 기억이 잘 나지 않으셨을 거예요.

 

우리의 유니티 프로젝트에서 키보드 입력에 의한 이동 값은 1을 반환한다고 했었죠?

직선 이동의 경우 horizontal 값을 담았던 h 변수와 vertical 값을 담았던 v 변수가 기억나시죠???

우리는h와 v 값을 이용해 플레이어 이동을 하였는데요.

대각선 이동 즉, h와 v가 해당하는 키보드 방향키를 동시에 눌렀다면 h의 값도 1, v의 값도 역시 1이 될 겁니다.

밑변 a에 해당하는 변수는 수평 값인 h, 높이 b에 해당하는 변수는 수직 값인 v가 되는 겁니다.

 

우리는 대각선 이동을 위해 수평, 수직을 의미하는 두 개의 키를 동시에 눌렀기 때문에 결국 c의 값으로 이동하는 것이나 다름없습니다. 

 

h와 v는 1의 값을 가지므로 a² + b² = c² 공식에 의해 c의 값은 √2가 되는 것입니다.

√2 는 약 1.41421356...이라고 하네요.

우리가 지정했던 moveSpeed 는 3, √2 는 약 1.4이므로 두 값을 곱해주면 4.2라는 값이 나오게 됩니다.

그리하여 우리가 대각선으로 이동했을 때에는 4.2라는 값이 UI에 출력되는 것이었습니다.

 

우리는 이제 정확한 원인을 알았으므로 수정해야겠죠.

우리가 원하는 결과는 직선으로 이동할 때나 대각선으로 이동할 때 모두 동일한 속력으로 이동하는 것입니다.

 

이럴 때에 필요한 기능 또한 친절하게도 유니티에서 제공을 하고 있습니다.바로 normalized를 사용하여 벡터를 정규화 시키는 방법입니다.유니티에서는 Returns this vector with a magnitude of 1 로 설명하고 있습니다.무조건 크기가 1인 벡터로 반환한다는 의미입니다. 이렇게 하면 두 키를 동시에 입력하여도 √2가 아닌 1로 고정된 값을 반환합니다.직접 코드에서 작성해봅시다.

 

transform.Translate(new Vector3(h, 0, v) * moveSpeed * Time.deltaTime);

 

Update() 함수 내의 이 부분을 수정할 겁니다.

하는 김에 코드도 더욱 간결하게 수정해보겠습니다.

 

Vector3 newDir = new Vector3(h, 0, v).normalized * moveSpeed * Time.deltaTime;
transform.Translate(newDir);

 

Vector3 형 newDir 변수를 만들고 앞서 구했던 new Vector3(h, 0, v)에 .normalized를 사용하여 정규화한 뒤 moveSpeed와 Time.deltaTime을 곱해주었습니다.

마지막으로 이렇게 계산된 newDir를 Translate()함수에 전달하였습니다.

스크립트를 저장 후 에디터를 실행하여 확인해봅시다!

 

 

 

이제 직선이동 또는 대각선 이동을 할 때 모두 3.0의 속력으로 이동할 수 있게 되었습니다.

또 한 가지 달라진 점이 있다면 키입력을 하면 매 프레임 0 ~ 1 까지의 값을 반환하기 때문에 우리가 눈으로 느끼기는 매우 어렵지만 사실 점차 속력이 증가하는 셈이지요. 다만 너무 찰나의 순간이라 누르자마자 1에 도달하기 마련입니다.

벡터를 정규화하면 어떤 값이 들어오든 무조건 1의 크기로 고정되기 때문에 점차 속력이 증가하지 않고 방향키를 누르는 즉시 1이 반환되어 최종적으로 3의 속력이 나오게 되는 겁니다.

 

오늘은 피타고라스의 정리와 벡터의 정규화를 이용하여 일정한 속력으로 이동하는 플레이어를 구현하였습니다.

오늘도 따라오시느라 고생하셨습니다 :D