2017년 9월 22일

‘The Occupation’에서 사용한 AI 로코모션의 레이어별 접근

저자: James Burton


안녕하세요, 저는 화이트 페이퍼 게임즈(White Paper Games)의 테크니컬 아티스트 제임스 버튼(James Burton)입니다. 이번 포스트에서는 저희가 출시를 앞둔 게임 오큐페이션(The Occupation)에서 AI 캐릭터의 로코모션(locomotion) 시스템을 제작하는 데 사용한 프로세스를 보여드릴 것입니다. 저는 이 시스템을 저희 AI 디자이너 조니 픽튼(Jonny Pickton), 애니메이터 로버트 비어드(Robert Beard), 그리고 프로그래머 마틴 코센스(Martin Cosens)와 함께 작업했습니다. 우선 저희 팀은 이 시스템을 제작하기 전에는 그 어떤 캐릭터 애니메이션(이나 캐릭터 그 자체) 관련 경험이 전혀 없었다는 점을 먼저 짚고 넘어가야 하겠습니다. 이런 작업에서 선구자적인 역할을 했다고 주장하는 것이 아니라, 그저 저희 프로젝트에 맞는 솔루션을 제시한 것 뿐이며, 이것을 여러분 모두와 공유하는 것이 좋겠다고 생각했습니다. 이 구성을 개선할 수 있는 방법에 대한 피드백과 코멘트는 모두 환영합니다! 연락처 관련 정보는 이 기사 맨 아래에 있습니다.

이 로코모션(locomotion) 시스템에 필요했던 중요 요소들은 다음과 같습니다:

  • 모듈형의 비 파괴적 엘리먼트(레이어 추가 혹은 제거가 캐릭터의 작동을 멈추거나 다른 레이어에 영향을 주어서는 안됨).
  • 팀 내 프로그래머가 한 명 뿐이므로 적은 양의 코딩만을 요구해야 할 것.
  • 전투가 없는 게임의 분위기에 어울리도록 AI도 침착하고 관찰이 가능해야 할 것. 시선을 정면으로 고정한 상태의 옆걸음 애니메이션은 안 됨.
  • 언리얼 엔진 4의 기본 설정을 최대한 고수하는 시스템.
  • 가능한 한 변함없는 속도와 회전율을 고수하는 시스템.

이 작업을 하기 전에, 우리는 몇 가지 다른 방법들도 시험해 보았습니다:

완전한 루트 모션 구성(A Full Root Motion Setup): 보기에는 좋았지만 AI가 다른 임무까지 완료하는 데 필요한 정밀함은 제공하지 못했습니다.

루트 모션 / 캡슐 주도형 혼합(Root Motion/ Capsule Driven Hybrid): 회전에 루트 모션을 사용하고 나머지는 캡슐 위주 이동에 맡겼습니다. 하지만 루트 모션의 속도를 캡슐에 맞추는 것이 문제가 되어 이 방법도 포기했습니다.

결국 우리는 완전한 캡슐 주도형 시스템을 채택하고, 여기에 캡슐의 속도를 애니메이션의 재생에 맞출 수 있는 몇 가지 기술을 더하기로 했습니다. 그리고 후에 몇 가지를 더 추가했습니다.

다음 영상에서는 언리얼 엔진 4의 기본 설정에 아무런 편집을 가하지 않은 경우를 보여줍니다. 캐릭터가 보행 주기를 가지고 화면 안을 돌아다니는 것을 볼 수 있지만, 정확하게 자연스러워 보이지는 않습니다:

그래서 이것을 수정하기 위해 다음과 같은 시도를 했습니다.

레이어 1 – 제자리 회전(Turning on the Spot)

우선 기본 레이어 구성에 초점을 맞추었습니다. 이런 방법으로 디자이너들은 캐릭터 작업을 할 수 있었고 우리는 기본적인 수준의 충실도를 얻을 수 있었습니다. 즉 캐릭터들이 제자리 회전을 해 진행 경로의 다음 목표 지점을 향할 수 있게 되었다는 뜻입니다:

  • 애니메이션은 일정한 회전 속도로 제작되었습니다.
  • 캐릭터가 회전해야 하는 각도에 따라 애니메이션의 재생 속도를 조절했습니다. 이 작업을 위해, 회전해야 하는 각도를 캐릭터의 회전 속도로 나누었습니다. 이를 통해 원하는 회전 시간을 구했습니다. 그런 다음 원본 애니메이션의 길이(초 단위)를 원하는 회전 시간으로 나누어 회전 애니메이션의 재생 속도를 구했습니다.
  • 이것은 굉장히 기본적인 구성으로, 일부 다른 게임들에도 적용이 가능할 것입니다.

Unreal+Engine%2FblogAssets%2F2017%2FSEPTEMBER+2017%2FThe+Occupation+Animation+Tech+Blog%2FIMG1_PlayRateFormula-770x132-40cf6c29f58fbaa0033e828f464617ccc7d473e3
Unreal+Engine%2FblogAssets%2F2017%2FSEPTEMBER+2017%2FThe+Occupation+Animation+Tech+Blog%2FIMG2_ScalePlayrateforSpotTurns-770x175-98be8e33b4c57bc70b85d6a9b95c2c021713904a

아래는 결과 영상과 이 레이어에 제작된 애니메이션을 포함한 GIF 파일입니다:

Unreal+Engine%2FblogAssets%2F2017%2FSEPTEMBER+2017%2FThe+Occupation+Animation+Tech+Blog%2FGIF1_-TurnonSpot45-6b7174660396fbf8fd3f40b479cd6b6ec61d4752

   Unreal+Engine%2FblogAssets%2F2017%2FSEPTEMBER+2017%2FThe+Occupation+Animation+Tech+Blog%2FGIF2_TurnonSpot90-073c83b7a4226c2fc49d361f7e961acb6f1e0a51Unreal+Engine%2FblogAssets%2F2017%2FSEPTEMBER+2017%2FThe+Occupation+Animation+Tech+Blog%2FGIF3_TurnonSpot180-e4e18cc4f6b2d412ec8dfa69909f45e3b26d9a21 







  [제자리에서 45도 회전]             [제자리에서 90도 회전]         [제자리에서 180도 회전]

레이어 2 – 보행 시작(Walk Starts)

다음 레이어는 보행의 시작을 구성합니다. 캐릭터는 이 방식을 통해 기본 보행 속도로 갑자기 툭 튀어나오는 것이 아니라 서서히 가속할 수 있습니다. 여기서는 속도의 상승을 다루며 모든 것을 캡슐 주도형으로 유지하고 싶었기 때문에, 우리는 작은 툴 하나를 제작했습니다. 이 툴은 매우 간단하고 제 코딩 경험이 매우, 매우 일천했기 때문에 MEL(Maya Embedded Language - 마야 임베디드 랭귀지)로 만들었습니다. 이 툴은 매 프레임마다 캐릭터의 속도를 설명하는 커브를 작성해 가속을 매핑합니다. 이것의 활용 방법은 우리 애니메이터가 애니메이션을 하나 만들게 한 다음, 이 애니메이션이 다음과 같은 간단한 연산을 거치게 합니다:

Unreal+Engine%2FblogAssets%2F2017%2FSEPTEMBER+2017%2FThe+Occupation+Animation+Tech+Blog%2FIMG3_Speed-Formula-770x132-88308d0bf30bd9c23e0257059d7cb20906cad618

TranslateX.F2 – TranslateX.F1는 애니메이션의 현재 프레임에서 이전 프레임을 빼서, 각 프레임 사이에 얼마나 많은 이동이 있었는지 알아내는 것입니다. 그런 다음 이 값을 원하는 FPS 설정(어떤 애니메이터들은 24FPS로 작업했고, 다른 애니메이터들은 30FPS으로 작업하는 등 다양했습니다)에서 1프레임에 소요되는 시간으로 나눕니다..

다행히 이 데이터를 우리 스켈레톤 루트 본의 특성에 입력할 수 있으며 언리얼 엔진 4에 임포트 할 때는 “커스텀 특성 임포트(Import Custom Attributes)”가 켜져 있는지 확인해야 합니다. 이것은 이 데이터가 반드시 애니메이션 애셋의 커브로 들어오게 해 줍니다. 이것은 수많은 멋진 구성들에 대한 가능성을 열어준다는 점에서, 언리얼 엔진의 정말 멋진 기능이라는 점을 꼭 언급해주고 싶습니다.

Unreal+Engine%2FblogAssets%2F2017%2FSEPTEMBER+2017%2FThe+Occupation+Animation+Tech+Blog%2FIMG4_CustomSpeedAttribute-770x136-807d4ff0104f8ea1c74066bf17f4dbdc1777ca83

그런 다음 이 커브를 사용해 캐릭터의 최대 속도를 구성하면, 준비는 끝났습니다!

다음은 위 작업의 결과물과 제작된 애니메이션의 GIF 파일입니다:

Unreal+Engine%2FblogAssets%2F2017%2FSEPTEMBER+2017%2FThe+Occupation+Animation+Tech+Blog%2FGIF4_StartWalkFWD-825c8ec73e8e1cb5fa734fb78a7286463a763b0aUnreal+Engine%2FblogAssets%2F2017%2FSEPTEMBER+2017%2FThe+Occupation+Animation+Tech+Blog%2FGIF5_StartWalk90-a82bd63e1a2266b368bc4147c1e2fad50705c5a4 Unreal+Engine%2FblogAssets%2F2017%2FSEPTEMBER+2017%2FThe+Occupation+Animation+Tech+Blog%2FGIF6_StartWalk-90-aa415b13dc1ba39be1fcdcf4a87b6aef44304537             







 [전진 보행 시작]              [90도 회전 후 보행 시작]                  [-90도 회전 후 보행 시작]

레이어 3 – 보행 정지

이 작업은 보행 시작과 정확히 똑같은 구성이었기 때문에 상당히 간단했지만, 캐릭터에게 “정지 애니메이션”을 언제 시작해야 할지 알려주기 위해서는 프로그래머가 언리얼 엔진 내에 약간의 코딩을 해야 했습니다.

이 코드는 경로의 마지막 지점을 확인한 다음, 마지막 지점에서 필요한 만큼의 거리(이것은 애니메이터가 애니메이션을 제작할 때 결정하는 것이지만, 되도록 짧게 하는 것이 좋습니다)가 떨어진 지점에서 “정지 애니메이션 재생(play stop animation)” 이벤트를 발동합니다.

마지막 지점에서는 허용범위 이내의 발 미끄러짐 현상이 아주 약간 발생합니다. 아래의 사례에서는 뚜렷하게 보이지만, 약간의 조정만 거친다면 알맞게 고칠 수 있습니다. 최종 지점에서는 다른 애니메이션이 또 재생되기 때문에, 이 최종 지점에 완벽하게 도달하는 것이 아주 중요했습니다. 그래서 다른 애니메이션은 최종 지점에 스냅해 오류가 발생해도 나머지는 제대로 작동하게 했습니다.

다음은 결과 영상과 제작된 애니메이션의 GIF 이미지입니다:

Unreal+Engine%2FblogAssets%2F2017%2FSEPTEMBER+2017%2FThe+Occupation+Animation+Tech+Blog%2FGIF7_WalkStopFWD-2362243ea8baeb8abf63c7e2146448b9f023f6ea

[전진 보행 정지]

레이어 4 – 완만한 커브 경로

이것은 가장 고치기 어려웠던 문제로 약간의 코딩이 필요했지만, 또한 전체 로코모션 시스템의 외형에서 가장 큰 차이점을 만들어낸 레이어이기도 합니다.

여기서 우리는 기본적으로 캐릭터를 진행 경로에서 이탈시킨 다음, 캐릭터가 다음 지점을 향해 회전하는 동안 캐릭터의 전방 벡터가 캐릭터의 이동을 조종하도록 만듭니다. 이미지와 같이 설명하면 이해가 더 잘 될 것입니다:

Unreal+Engine%2FblogAssets%2F2017%2FSEPTEMBER+2017%2FThe+Occupation+Animation+Tech+Blog%2FIMG5_TurnExplanationImage-770x770-e93ebd25ac4ef7413b9c28c7a9e8f2015a8e60bd

중요한 점은 일정한 회전 및 캐릭터 캡슐 속도를 이용하는 것입니다. 그러면 계산이 쉬워집니다.

다음은 결과 영상입니다:

다행히 여기서는 추가 애니메이션 없이도 괜찮아 보였기 때문에, 애니메이션을 더 제작할 필요가 없었습니다.

레이어 5 – 회전 예측(미끼 활용)

이것은 캐릭터가 마치 회전을 예측하는 것 같은 느낌을 주도록 만드는 좋은 추가요소입니다. 캐릭터가 실제로 회전을 시작하기 전에 회전할 방향으로 자신의 머리와 몸을 살짝 돌리게 만들도록 구성했습니다.

여기서는 단순히 캐릭터의 다음 지점만 사용하는 것이 아니라 그 방향을 향한 회전을 선형보간(Lerp)해야 했으므로, 약간의 독창성이 필요했습니다.

결국 우리는 “미끼”를 사용해 캐릭터 전방의 진행 지점 사이를 선형보간(Lerp)하는 시스템을 제작했으며, 애님 오프셋을 사용해 캐릭터의 몸과 머리가 언제나 이 미끼를 향하도록 만들었습니다. 아래 예제에서는 캐릭터가 계속해서 쫓아다니는 노란색 공이 보이실 것입니다(불행하게도 캐릭터는 절대 이 공을 잡을 수 없습니다).

다음은 결과물과 블루프린트 구성, 그리고 에임 오프셋의 GIF입니다:

Unreal+Engine%2FblogAssets%2F2017%2FSEPTEMBER+2017%2FThe+Occupation+Animation+Tech+Blog%2FIMG6_PieceOfCandySet-770x223-be0a1732766d1d33c4c9e3ab2882745a0351a344

Unreal+Engine%2FblogAssets%2F2017%2FSEPTEMBER+2017%2FThe+Occupation+Animation+Tech+Blog%2FIMG7_PieceOfCandySet_2-770x211-e0d79bd178843175c1f2cd5a130df762bcc13341

Unreal+Engine%2FblogAssets%2F2017%2FSEPTEMBER+2017%2FThe+Occupation+Animation+Tech+Blog%2FGIF8_WalkLeansAimOffset-ec813037004b2ac7b6e013c0ad0a6fc92253f623

[에임 오프셋을 활용한 보행]

이제 완성되었습니다. 이것이 바로 오큐페이션에서 사용하는 로코모션 시스템입니다. 이 구성의 최종 애님그래프(AnimGraph)는 언리얼 엔진 4 기본 설정과 우리가 제작한 로코모션 시스템의 경로 탐색을 비교한 영상과 함께 아래에 게시해 두었습니다.

Unreal+Engine%2FblogAssets%2F2017%2FSEPTEMBER+2017%2FThe+Occupation+Animation+Tech+Blog%2FIMG8_AnimGraph-770x390-e9a679c94be1f88c5a2c671f1bd5edef3bc387cd

Unreal+Engine%2FblogAssets%2F2017%2FSEPTEMBER+2017%2FThe+Occupation+Animation+Tech+Blog%2FIMG9_AnimEventGraph-770x297-2e54738a785f965abcb9e89e97150f681b663480

이 포스팅이 여러분께 유용했길 바라며, 질문이나 답변이 있으시다면 아래에 게시해 주시거나 트위터(Twitter) (@whitepapergames)나 contact@whitepapergames.com로 자유롭게 연락해 주시기 바랍니다.