티스토리 뷰
여러분 반가워요!
저번 시간에 우리는 모션 확대범을 한번 만나봤는데요, 오늘은 이 친구가 프로젝트에서 실제로 어떤 역할을 하는지 보여드리려고 해요.

오늘의 목표는 왼쪽의 토끼를 따라하지만 움직임을 조금 과장해서 따라하는 오른쪽의 토끼를 만들어보는 거예요.
그럼, 시작합니다!


저저번 시간에서 알아봤듯이, 우리의 토끼는 다양한 객체들의 집합으로 이루어져 있어요.
게다가 이 객체들은 토끼라는 그룹에 무작위로 담겨 있는 것이 아니라, 오른쪽 사진처럼 서로 계층 구조를 형성하며 존재하고 있어요.
이 계층 구조는 머리는 몸에 붙어 있고, 귀는 머리에 붙어 있는 등 실제 토끼의 구조를 따르기 때문에 각 객체들의 Local 움직임을 정의하는 것만으로도 토끼의 움직임을 직관적으로 구성할 수 있답니다.
아무튼, 그러면 먼저 '모션 확대범'을 이용해서 A의 모션을 B에 똑같이 복제하는 것부터 시작할게요.
이 방식 역시 저저번 시간에 봤던 '주문서 - 요리사' 방식(커맨드 패턴)에 따라 구현되는데, 덕분에 당장 토끼가 없어도 연결을 미리 만들어둘 수도 있고, 중간에 토끼가 바껴도 주문서만 있다면 연결을 유지할 수 있다는 장점을 누릴 수 있답니다.
그럼 주문서부터 만들어 볼까요?


Transform Parent에서 봤던 것과 같이, 여기서도 각 객체들의 관계를 정의하는 데 'Part'라는 주문서가 사용돼요.
이 Part 역시 Transform Parent에 사용되는 Part와 같이 Target, Source, Pivot 객체를 적을 수 있는 칸과 모션 연결 방식을 적을 수 있어 우리가 예전에 했던 것과 비슷하게 연결 데이터를 정의할 수 있어요.
다만 이번 경우에는 토끼를 제대로 움직이게 하려면 토끼를 구성하는 모든 객체들에 대해 전부 Part 주문서를 만들어 긴 하겠어요.
아, 걱정하지 마세요! Part는 일일히 추가할 수도 있지만, 버튼 하나만 누르면 특정 객체에 속한 객체들에 대해 자동으로 Part 데이터를 만들어주는 기능도 있으니까, 우리는 버튼 하나만 누르면 돼요.
자, 그럼 주문서 제작이 끝났으니, 이걸 요리사에게 전달하러 가봅시다!


토끼마을에서는 이 주문서를 'Transform Syncer'라는 객체가 받아서 연결을 수행해요.
Transform Syncer는 주문서인 Part와 재료인 Rabbit을 가지고 있는데, Part를 받으면 가지고 있는 Rabbit에서 Part와 같은 이름의 객체를 선택한 뒤 이들을 Part에 써있는 정보대로 연결한답니다.
이건 Transform Parent와 정확히 같은 개념으로 작동하기 때문에 쉽게 이해하실 수 있을 거예요 :)

Transform Parent에서는 Bind라는 작업을 통해 두 객체간의 연결을 수행할 수 있었는데, Transform Syncer는 이와는 조금 다른 과정을 통해 연결을 수행해요.
Transform Parent는 연결을 할 때 Source 객체의 '현재 위치'만 있으면 됐지만, Transform Syncer는 연결을 위해 Source의 '현재 위치'뿐만 아니라 Source의 '처음 위치', 그리고 Target의 '처음 위치'도 필요해요. 이는 객체가 움직일 때 각 객체의 '위치 델타'를 계산하기 위함인데, 이 계산은 객체의 '현재 위치'에서 '처음 위치'를 빼는 방식으로 작동하기 때문이에요.
또한 Target 객체는 '처음 위치'에 '위치 델타'를 더한 값으로 '현재 위치'를 정하는데, 이때 '처음 위치'를 알지 못한다면 '현재 위치'도 제대로 구할 수 없다는 문제점도 있어요.
이 때문에, Transform Syncer는 주문서를 받으면 가지고 있는 두 Rabbit에서 Part와 같은 이름의 객체를 선택한 뒤, 각각의 위치를 '처음 위치'로 간주하여 저장하고, 이들을 Part에 써있는 정보대로 연결해요.
이 과정을 Capture라고 부르며, Transform Syncer의 상태는 Capture가 일어나기 전인 Idle과 Capture가 일어난 후인 Binded로 나뉘어요.
Capture를 수행하면 각 객체의 초기 위치 정보가 해당하는 연결 방법에 맞는 형식으로 xml 형식으로 저장되며, 이는 프로그램이 재실행되더라도 설정 데이터를 유지하여 객체가 항상 동일한 초기 위치에서 동작을 수행하도록 도와줍니다.
이는 Target 객체가 처음 위치'에 '위치 델타'를 더한 값으로 '현재 위치'를 정한다는 성질 때문인데, 시작할 때마다 '현재 위치'가 달라진다면 객체가 의도하지 않은 움직임을 보일 수 있겠죠?
또한 Transform Syncer는 객체의 처음 위치들을 알고 있기 때문에, 객체가 변형되었을 때 Reset Position 기능을 수행하면 각 객체들을 Capture 당시의 위치로 초기화시켜줄 수도 있답니다.
그럼 실제로 객체를 Capture한 후, Rabbit A를 움직여볼게요!

짠! 이렇게 연결이 잘 된 걸 볼 수 있답니다.
연결을 확인하기 위해 토끼가 두 번 점프를 할 때마다 Transform Parent를 꺼 봤는데, 잘 작동하는 게 보이죠?
하지만 우리는 토끼의 움직임을 과장하는 게 목표였어요.
과장도 한번 해볼게요!


토끼의 좌우 움직임을 담당하는 가장 큰 객페는 _body_b라는 객체에요.
Local로 연결된 이 객체의 Position Scale을 4로 하고 다시 플레이를 하면?

짠! _body_b 객체의 '델타 위치'가 4배로 커지면서 의도대로 모션이 확대된 걸 볼 수 있습니다.
이 기세를 몰아서 몸도 양옆으로 확대를 해볼게요.

이런 식으로 Transform Syncer는 원본의 모션을 다양하게 변경하는 역할을 수행한답니다.
덕분에 모션을 하나만 만들어도 여러 체형의 토끼에게 자유롭게 적용할 수 있는 것이죠.
참, 저번 시간에 World 표현법이 모션 확대에서 어떻게 쓰이는지 보여준다고 했었죠?
지금 한번 보여드릴게요.

먼저, 저는 토끼의 머리를 조금 높인 상태로 두고 싶다고 해볼게요.
그러기 위해 Target의 '처음 위치'의 y 값을 조정해봤는데, 머리의 연결 방식이 Local이기 때문에 머리의 Parent의 객체를 기준으로 좌표계가 설정되는 것을 볼 수 있어요.

하지만 이렇게 Control Type을 World로 하고 설정하면 Transform Syncer가 입력한 좌표를 Local 좌표계로 변환하여 적용해주기 때문에 이렇게 직관적인 높이 조절이 가능해진답니다.

물론 이렇게 설정한 높이는 애니메이션이 재생 중일 때도 적용된답니다.
오늘은 이렇게 실제 토끼에 모션 확대를 적용해보는 시간을 가졌어요.
다음 시간에는 다양한 토끼들의 모션을 실제로 어떻게 구현하는지 보여드릴게요.
그럼 그때 다시 만나요!
'토끼마을 개발 백서 > Shape Control' 카테고리의 다른 글
| 다음 일지는 11월에 올라와요 (0) | 2024.08.03 |
|---|---|
| 모션 확대범 (3) | 2024.07.23 |
| Transform Parent (2) | 2024.07.16 |
| 세 가지 위치 표현법 (1) | 2024.07.13 |
| Shape Control 마을 (0) | 2024.07.11 |