Unity PATH GENERATOR
유니티에서 오브젝트를 곡선 및 직선 트랙을 따라 움직이는 기능구현
2019.11.01
유니티에서 오브젝트를 곡선 및 직선 경로를 따라 움직이게 만드는 방법은 무엇일까?
아마 가장 쉽고 직관적인 방법은 애니메이션을 사용하는 것일것이다.
하지만 애니메이션을 통한 구현 방법은 아래와 같은 문제점이 있다.
문제점
1 . 키프레임 사이를 원하는 방법대로 조절하기 어렵다.
빨간 네모에서 파란 네모로 움직이는 애니메이션,
즉 Position의 변화를 애니메이션으로 만든다고 하면
경로는 위 그림처럼 무수히 많이 존재 할 수 있는데,
애니메이션에서는 항상 가장 빨리 변할 수 있는 방향 (주황색 패스) 으로만 정해진다.
2 . 일정한 속도로 움직이기 어렵다
만약 위 그림처럼 빨간 네모에서 초록색 네모로, 그 후 파란색 네모로 움직이는 패쓰가 있다고 하자.
이 때 각 지점사이의 거리가 같지 않다고 할 때,
애니메이션 키프레임이 다음과 같이 분포한다면 (1:1 로)
두 부분에서 물체가 움직이는 속도는 다를것이다.
따라서 위 상황처럼 s1과 s2가 같지 않을 때 일정한 속도의 애니메이션을 만들고 싶다면,
S1 : S2 = ( t1 - t0 ) : ( t2 - t1 ) 이 되도록 키프레임을 제어해야 한다.
물론 시간을 많이 투자하면 가능하겠지만,
경로가 구부러지거나 그 수가 많아 비율을 계산하기 힘든 상황에선 불편 할 수 밖에 없다.
베지어 곡선
위에서 알 수 있는 문제점 중 공통된 사항은 ‘곡선’이다.
어떻게 하면 곡선을 나타 낼 수 있을까 찾아보다가, 베지어 곡선(Bézier curve)이라는 것을 찾아내었다.
이론
일단 직선을 움직이는 점을 생각해본다.
하나의 직선이 있고 그 위를 점 M이 일정 속도로 이동하고 있다.
이 점 M의 궤적은 당연하지만 단순한 직선으로 그려진다.
이때 t는 선분 위를 비율적으로 얼마나 나아갔는지를 나타내는 수치다.
여기에 선을 하나 더 추가하고 그 위에 M처럼 이동하는 점을 놓아본다.
그리고 원래의 점 M을 M0로, 새로운 점을 M1으로 한다.
M0와 M1이 움직이는 규칙은 이전과 같다.
M1이라는 점이 하나 더 늘었다 하더라도 특별히 복잡해질 것은 없다.
여기에서 M0와 M1을 잇는 선을 하나 더 그을 수 있다.
그 선은 M0와 M1이 이동하면 자연스럽게 함께 움직이게 된다.
그 선 위에 M0나 M1처럼 일정 속도로 이동하는 점을 놓을 수 있다. 그 점을 B라고 하자.
그리고 점 B가 그리는 궤적을 살펴보면, 곡선이 되는 모습을 볼 수 있다.
점 B가 그리는 궤적을 2차 베지어 곡선(Quadratic Bezier Curve)이라고 한다.
아래는 시간 t에 대한 베지어 곡선 식이다.
조절점을 늘릴수록 3차, 4차, 5차 .. 베지어 곡선을 만들 수 있다.
차수가 늘어날 수록 정교하거나 복잡한 곡선을 만들 수 있지만,
이번 프로젝트에서는 2차 베지어 곡선만으로도 충분하다고 생각했다.
아래는 3차 베지어 곡선이다.
스크립트
그래서…
이제는 애니메이션이 아닌 스크립트로 베지어 곡선을 만들어서
자동차가 달릴 트랙을 만들기로 하였다.
- PathGenerator.cs : 자동차를 지정된 Path대로 움직이는 스크립트
- PathGeneratorGUI.cs Play버튼을 누르기 전, 에디터 상에서 Path를 그리는 스크립트
쓸 수 있는 스크립트는 PathGenerator인데, Inspector Window에서 조정 가능한 파라미터는
아래와 같다.
이름 | 타입 | 설명 |
---|---|---|
Speed | float | 자동차의 스피드. 추천 : 1800f |
Flag | GameObject | 사용자가 패스를 만들때 쓰는, 자동차가 지나가게 되는 트랙의 연결부분 프리팹 |
Angle | GameObject | 베지어 곡선을 만들 때 쓰이는 조절점 프리팹 |
Target | GameObject | 자동차 오브젝트. Scene에서 드래그해서 넣으면 된다. |
Guide | GameObject | 디버그 시 자동차가 진행할 방향을 표시해줄 오브젝트의 프리팹 |
FlagRoot | GameObject | 트랙을 만들 때 사용되는 플래그들의 부모 오브젝트 |
AngleRoot | GameObject | 곡선을 만들 때 사용되는 조절점들의 부모 오브젝트 |
IsClosed | bool | 트랙이 닫힌회로 (무한루프)인지 체크 |
IsDebug | bool | 디버그를 하는건지 체크 |
FlagList | List<GameObject> | 실제로 트랙을 구성하는 플래그들의 집합 |
AngleList | List<GameObject> | 실제로 트랙을 구성하는 조절점들의 집합 |
PathDensity | int | 곡선의 밀한 정도를 나타내는 수치. 추천 50 (많아지면 계산량 증가) |
파라미터를 알맞게 조정하고,
Scene에서 오브젝트들을 잘 조절하면 주어진 트랙에 맞는 패스를 쉽게 그릴 수 있었다.
곡선 제작
아래는 에디터에서의 화면이다.
이처럼 Path를 쉽게 그릴 수 있다.
PathDensity를 조절하면 곡선이 부드러울지 뚝뚝 끊길지 정할 수 있다.
너무 적을 경우 곡선이 아니라 직선처럼움직이게 된다.
등속력 운동
아래처럼 만들어진 곡선을 따라서 같은 속력 ( offset * speed * Time.deltaTime )으로 움직이기 때문에
곡선이던 직선이던 상관없이 부드럽게 움직인다.
주행 테스트
확인해 본 결과 아주 잘 달리는 모습이다.
GIF로 업로드하던 도중 차가 살짝 중앙에서 치우쳐서 달리는 모습이 보이긴 하는데
애니메이션과 다르게 Path의 해당 앵글을 조금 손 보면 되기 때문에 금방 수정 가능하다.
남은 것
이제 핸들과 바퀴의 회전이 남았다.
해당 이슈에 대해서도 어느정도 생각이 있는다.
Quaternion의 Slerp를 이용하면 될 것 같은데,
자세한 사항은 다음에 업로드 하도록 하겠다.