Game/Unity

[유니티] 오브젝트 이동 (2)

nowkoes 2023. 1. 23. 17:03

목표

- 오브젝트가 특정 경로를 따라 이동하게 구현

- 좌표와 애니메이션을 이용해 오브젝트가 움직이며 이동하게 구현


특정 경로를 따라 이동

 이전 [유니티] 오브젝트 이동(1)에서는 기본적인 이동방법 2가지인 transform.Translate와 transform.position에 대해 간략하게 알아보았다. 이번 파트에서는 이 두 가지 방법 중 하나를 이용해서 경로를 따라 이동하는 것을 구현해보자. 

 

 다음과 같은 타일을 맵으로 해서 노란색 경로를 따라 지나가다 End를 만나면 멈추는 것을 구현해보자. Start부터 시작해서 꺾이는 분기점을 지나게 하려면 어떻게 해야할까?

 

- Vector3.moveTowards

 유니티 공식 문서에 따르면 Vector3의 프로퍼티중 MoveTowards는 다음과 같다.

 

 

 current에서 target까지 maxDistanceDelta의 속도로 이동한다고 생각하면 된다. 이때 예제를 살펴보았을 때 maxDistanceDelta의 속도에 Time.deltaTime을 곱해주는 경우가 많은데, 여기서 Time.deltaTime1/초당프레임을 의미한다. 즉, 컴퓨터 사양에 구애받지 않고 시간을 다루기 위해 사용된다.

 

Time.deltaTime의 의미

 

 따라서 start에서 출발해서 end로 가기 위한 코드를 짜면 다음과 같다. 

public float speed = 3f;
public Transform target;

void Update()
{
    transform.position = Vector3.MoveTowards(transform.position, target.position, speed * Time.deltaTime);
    // 매 프레임마다 speed의 속도만큼 이동
}

 

1. 경로(Path) 설정

 다시 우리가 이동하고자 하는 맵을 확인해보자. 

 

 

 노란색 경로 위에 있는 저 빨간색 동그라미를 지나쳐야 올바르게 이동하는 것이므로, 해당 경로 지점을 설정해주는 작업이 필요할 것 같다. 따라서 Hieararchy창에 빈 오브젝트를 생성해 경로를 담아두는 용도로 지정해놓고, 그 안에 경로 포인트를 담도록 해보자.

 

검은색 큐브가 있는 지점을 Start로 해서 Curve (1), Curve (2)를 지나 End에 도달하게 오브젝트를 생성해놨다.

 

2. 경로 이동

 모든 준비가 끝났으니, 이제 코드로 구현할 차례만 남았다. 저기서 이동을 하려면 어떤 알고리즘이 필요할지 한 번 생각해보면, Scene에서 경로 포인트를 찾아야 하고, 그 경로를 정해진 순서에 따라 이동을 해야 한다는 걸 알 수 있다. 

 

 먼저 경로 포인트들을 담을 스크립트 PathPoints를 하나 작성하자.

// PathPoints.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PathPoints : MonoBehaviour
{
    public Transform[] points;
}

 

 이 스크립트는 단순히 이동 경로들을 저장할 용도이므로 Transform을 배열 형식으로 초기화 해놓으면 된다. 그 다음은 Path Object에 이 스크립트를 추가해 경로들을 지정해주면 된다.

 

0번부터 3번까지 순서대로 지정해야 한다.

 

 이제 경로를 지정했으니, 경로를 찾아 끝지점에 도달할 때까지 이동할 코드를 작성해야한다. 스크립트를 다음과 같이 작성해보자.

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

public class PathMove : MonoBehaviour
{
    [SerializeField] PathPoints thePath;
    private int currentPoint; //
    private bool endPoint; 
    private float speed = 3f; 

    void Start()
    {
        thePath = FindObjectOfType<PathPoints>();
    }

    void Update()
    {
        if(!endPoint)
        {
            this.transform.position 
                = Vector3.MoveTowards(this.transform.position, thePath.points[currentPoint].position, 
                	speed * Time.deltaTime);
            
            if(Vector3.Distance(this.transform.position, thePath.points[currentPoint].position) < 0.1f)
            {
                currentPoint++;

                if(currentPoint >= thePath.points.Length)
                    endPoint = true;
            }
        }
    }
}

 

 코드가 작동되는 과정을 살펴보자. 

[SerializeField] PathPoints thePath;
private int currentPoint; 
private bool endPoint; 
private float speed = 3f;

 먼저 아까 작성한 지점들을 받아와야 하므로 PathPoints 타입의 변수 thePath를 지정해주고, 배열의 인덱스에 따라 이동할 지점이 결정되므로 정수형 변수 currentPoint를 초기화해준다. 그후 끝지점 도달 여부를 체크할 bool 변수 endPoint와 큐브의 이동속도를 담당할 변수 speed를 초기화해준다.

 

void Start()
{
    thePath = FindObjectOfType<PathPoints>();
}

 경로를 지정해논 thePath를 찾아줘야 하는데, 이때 FindObjectOfType<>() 함수를 써주면 편리하다. 공식 문서를 살펴보면 다음과 같다.

 

출처:&nbsp;https://docs.unity3d.com/ScriptReference/Object.FindObjectOfType.html

 

 간단하게 꺾새<> 안에 적혀있는 타입의 오브젝트를 찾아준다고 생각하면 된다. 굉장히 간편하지만 성능적인 측면에서 느리다는 단점이 존재한다. 하지만 우리는 프로젝트 규모가 크지 않고, Start()문에서 한 번만 사용할 것이므로 크게 개의치 않아도 된다. 

 

void Update()
{
    if(!endPoint)
    {
        this.transform.position 
            = Vector3.MoveTowards(this.transform.position, thePath.points[currentPoint].position, 
            	speed * Time.deltaTime);

        if(Vector3.Distance(this.transform.position, thePath.points[currentPoint].position) < 0.1f)
        {
            currentPoint++;

            if(currentPoint >= thePath.points.Length)
                endPoint = true;
        }
    }
}

 경로를 찾은 후에는, 내가 이동할 포인트를 찾아야 한다. 우리의 목표는 지정된 경로를 따라 이동하되, 끝지점에 도달하면 멈춰야하므로 이동과 관련된 코드들을 endPoint에 도달하지 않았을 때만 업데이트 되도록 조건을 걸어놓자.

 

 만약 끝지점이 아니라면, 앞서 배운 MoveToWards를 통해 이동을 하도록 구현했다. 이때 위치는 자신의 위치에서 인덱싱한 배열을 따라 움직이게 설계했는데, 만약 자신의 거리와 목적지의 거리가 일정 수준으로 좁혀지면 다음 경로로 이동할 수 있도록 currentPoint를 증가시켰다.

 

 그렇게 이동하다보면 현재의 위치 currentPoint와 thePath의 길이와 같거나 커지는 순간이 오는데, 이때가 끝지점에 도달했다는 것이므로 endPoint를 true로 바꾸면 된다.


애니매이션을 이용해 오브젝트의 이동을 생동감 있게 표현

 위의 과정을 잘 따라왔다면 큐브가 빨간 포인트를 잘 지나갈 것이다. 하지만 뭔가 밋밋하다. 좀더 생동감있게 표현하기 위해 애니메이션을 활용해 점프하며 지나가는 듯한 느낌을 줘보자. 애니메이션을 구현하기 위해 빈오브젝트를 생성한 후 이를 Model로 이름 짓고 그 안에 Cube를 넣자(이렇게 하지 않으면 필자가 구현하려는 방식으론 애니메이션을 구현할 때 본체의 좌표가 고정되어서 구현하기 번거롭다). 또한 Model과 Cube의 위치가 꼬일 수 있으므로 반드시 각 오브젝트의 위치를 초기화를 한 번 진행하고 작업해야 한다.

 

 

 우리는 큐브가 점프하는 것을 구현할 것이므로 Ctrl + 6번(혹은 Windows -> Animation -> Animation)을 눌러 애니메이션을 생성하자.

 

 

 그러면 이런 창이 뜰 것인데, 반드시 Hieararchy창에서 Cube를 선택한 후 Create 버튼을 눌러 적당한 이름을 짓고 애니메이션 파일을 생성하자. 

 

 

 해당 창이 활성화되었다면 성공적으로 작업을 할 준비가 되었다는 것이다. 이번 시간에서는 애니메이션에 대해 깊게 파고들 것은 아니므로, 간단하게 큐브의 위치를 연속적으로 변화시켜 마치 점프하며 이동하는 듯한 모션을 구현해보자. 

 

 우측에 시간이 표시된 쪽에 마우스를 클릭하면 흰 선이 이동할 텐데, 이를 이용하면 해당 시간대에 Cube의 상태를 표현할 수가 있다. 예를 들어 0:00에 이벤트를 하나 만들고, 0:10에 흰 선을 옮겨놓고 Cube의 y축 position을 0.5로 해놓으면, y축이 0에서 0.5로 계속해서 변화하므로 점프하는 듯한 모션을 줄 수가 있다.

 

 

 좌측에 Cube의 Position 프로퍼티를 추가하면 다음과 같은 화면이 될 것이다. 점 표시가 활성화된 것이 이벤트인데, 이벤트를 활성화하여 y를 0부터 1까지 변화시킬 것이다.

 

 이제 좌측에 플레이 버튼을 누른 후 원하는 시간대에 클릭을 하여 y의 값을 변경해보자.

 

 

 이렇게 하면 큐브가 마치 점프하며 움직이는 듯한 모션을 간단하게 구현할 수 있다. 


요약

- 경로를 transform 배열로 지정해, 배열을 순회하며 경로를 이동할 수 있다.
- 애니메이션을 이용하여 특정 행동을 구현할 수 있다.
반응형