AnyPortrait > 메뉴얼 > 래그돌 구현하기

래그돌 구현하기


1.6.2



"래그돌 (Ragdoll)"은 캐릭터가 봉제 인형처럼 힘없이 늘어지게끔 만드는 기법입니다.
캐릭터의 각 본에 물리 기능을 부가하여, 오직 물리 엔진에 의해서만 캐릭터가 움직이도록 만드는 것이 이 기법의 핵심입니다.


유니티에서 래그돌을 구현하기 위해서는, "Joint" 종류의 물리 컴포넌트를 이용해야합니다.
이 페이지는 "Hinge Joint 2D" 및 2D 물리 컴포넌트들을 본에 연동하여 래그돌을 구현하는 방법을 다룹니다.
먼저, 1개의 본에 래그돌 효과를 적용하는 방법을 설명합니다.
이어서 다수의 본에 래그돌 효과를 적용하기 위해서 저희팀이 작성한 스크립트를 적용하는 방법을 설명합니다.


안내
AnyPortrait에는 Rigidbody 등의 물리 컴포넌트와 본을 연동하기 위해서는 특별한 구현 방법을 알아야 합니다.
이 페이지의 설명을 보기 전에 "본과 물리 컴포넌트 연동하기" (링크) 메뉴얼을 먼저 읽을 것을 권장합니다.




예제 1. 한개의 본에 Hinge Joint 2D를 연동하기




첫번째 예제에서는 로봇의 한쪽 팔에 래그돌 효과를 적용해봅시다.
래그돌 효과를 구현하기 위한 가장 좋은 방법은 "더미 오브젝트"를 생성하고, 여기에 "Hinge Joint 2D" 컴포넌트를 추가하는 것입니다.
추가되는 Hinge Joint 2D는 부모 본의 "Rigidbody 2D"와 연결되어야 합니다.
부모 본이 없는 이 예제의 경우에는 루트가 되는 GameObject에 연결하면 됩니다.




캐릭터는 위와 같이 구성됩니다.
1. Character Group : 캐릭터 전체의 루트 오브젝트입니다. "Rigidbody 2D" 컴포넌트를 가집니다.
2. AnyPortrait : AnyPortrait로 제작된 캐릭터입니다.
3. Default Collider : 캐릭터 전체의 크기에 맞게 설정된 콜라이더 오브젝트입니다.
4. Dummy : 래그돌 효과가 시작될 때 임시로 생성되는 더미 오브젝트입니다. 로봇 팔에 해당하며 "Hinge Joint 2D"를 포함한 물리 컴포넌트들을 가집니다.


이전 페이지와 다르게, 로봇 팔이 캐릭터에 종속되어 움직이므로 "더미 오브젝트"가 캐릭터 그룹 내에 포함된다는 점에 유의하세요.




스크립트를 작성하기에 앞서 "소켓"을 활성화합니다.
(1) Bone 탭을 선택합니다.
(2) 대상이 되는 본은 "Bone Lower Arm L"이며, 본의 길이를 계산하기 위해 자식 본인 "Bone Hand L"도 같이 참조해야합니다.
(3) Ctrl 키를 눌러서 두개의 본들을 모두 선택합니다.
(4) "Socket" 버튼을 눌러서 소켓을 활성화합니다.


이제 Bake를 실행하고 다음의 스크립트를 작성합니다.



대부분의 코드들은 이전 메뉴얼에서 소개한 스크립트와 거의 동일합니다.
여기서는 래그돌 구현과 관련된 코드들을 살펴봅시다.



래그돌 시뮬레이션을 시작하는 요청을 하는 코드입니다.
후술할 "MakeDummyHinge"함수를 호출하고, 루트 오브젝트의 Rigidbody 2D의 Constraint를 모두 해제합니다.
이렇게 하면 캐릭터가 균형을 잃고 쓰러지는 연출을 만들 수 있습니다.
중요한 것은 "PauseAll" 함수를 호출하여 캐릭터의 애니메이션을 일시정지했다는 점입니다.
캐릭터의 애니메이션을 계속 재생하게 만든다면 물리 시뮬레이션이 매우 불안정하게 동작하므로, 꼭 애니메이션을 정지시켜주세요.



더미 힌지 오브젝트의 움직임을 매 프레임마다 캐릭터의 본에 적용하는 코드입니다.
Hinge Joint 2D의 경우 독립되어 이동되지 않고 부모에 종속되어 회전만 하기 때문에 SetBoneRotation만 호출합니다.



이전 메뉴얼과 가장 크게 달라지는 코드입니다.
물리 연산이 실행되는 "FixedUpdate"에서 더미 오브젝트의 위치를 캐릭터의 본에 맞게 보정해야합니다.
정상적인 경우라면 AnyPortrait의 본과 더미 오브젝트는 부모에 종속된다는 공통점이 있으므로 굳이 위치를 보정하지 않아도 됩니다.
그렇지만 Hinge Joint 2D가 부모에 강력하게 연결되도록 설정을 하더라도, 너무 강한 힘을 가하거나 충돌 에러가 발생한다면 더미 오브젝트가 부모(여기에서는 Character Group)와 분리되는 문제가 발생합니다.
그래서 본의 위치와 더미 오브젝트의 위치를 계속 비교하면서 오류가 발생한다면 더미 오브젝트를 강제로 보정할 필요가 있습니다.
여기서는 "0.1f"의 위치 차이를 감지하도록 했는데, 만약 이 값을 너무 작게 만든다면 "MovePosition" 함수가 너무 빈번하게 호출될 수 있습니다.
"MovePosition"이 지나치게 많이 발생하면 더미 오브젝트의 속도에 영향을 주므로 움직임이 자연스럽지 않게 됩니다.
따라서 테스트를 해보고 적절한 값을 입력해야합니다.



더미 오브젝트를 생성할 때, 이전 메뉴얼과 달리, 캐릭터의 루트에 해당하는 "characterTransform"의 자식이 되도록 설정해주세요.



더미 오브젝트에 "Hinge Joint 2D" 컴포넌트를 추가하는 코드입니다.
여기서는 부모 본이 존재하지 않으므로 캐릭터의 루트 오브젝트인 "rootRigidbody"에 연결합니다.
만약 래그돌의 부모 본이 있다면, 부모 본에 해당하는 Rigidbody를 연결해야합니다.
추가적으로, 더미 오브젝트의 회전 각도를 제한해야할 필요가 있다면 위와 같이 코드를 작성하면 됩니다.
"JointAngleLimits2D"를 적용하기 전에 "Physics2D.SyncTransforms()"를 먼저 호출해야한다는 점에 주의해주세요.




작성한 스크립트를 씬에 적용해봅시다.
(1) GameObject를 새로 생성합니다.
(2) 생성된 GameObject에 스크립트를 추가하고, 멤버 변수에 해당하는 오브젝트들을 적절하게 할당합니다.




게임을 실행하고 스크립트에 맞게 A 키를 눌러봅시다.
위와 같이 로봇 캐릭터가 애니메이션을 멈추고 옆으로 쓰러지는 것을 볼 수 있습니다.
로봇이 넘어지면서 팔이 자연스럽게 꺾이는 것도 볼 수 있습니다.




이 상태에서 게임을 일시정지하고 어떤 상황이 발생했는지 살펴봅시다.
(1) 래그돌이 활성화되면 "DummyHinge"라는 이름의 더미 오브젝트가 캐릭터 그룹 내에 생성됩니다.
(2) 이 더미 오브젝트에는 "Rigidbody 2D", "Capsule Collider 2D" 및 "Hinge Joint 2D" 컴포넌트들이 추가되어 있습니다.
(3) 씬에서 확인하면, 더미 오브젝트와 물리 컴포넌트들이 대상 본에 맞게 생성되었고, 본이 더미 오브젝트와 동기화되어 움직이는 것을 볼 수 있습니다.




예제 2. 래그돌 구현하기


한개의 본을 Hinge Joint 2D와 연동하는 방식을 캐릭터의 대부분의 본들에 적용하면 래그돌을 구현할 수 있을 것입니다.
모든 본에 대해서 더미 오브젝트를 만들고 물리 시뮬레이션을 수행하도록 위의 스크립트를 수정하면 됩니다.
다만 수정된 스크립트의 분량이 너무 많아지므로 이 페이지에서 소개하기는 어려워서, 저희가 미리 작성한 스크립트를 제공해드립니다.


- 래그돌 시뮬레이터 스크립트 다운로드


Unitypackage 파일을 다운로드받고 유니티 프로젝트에 임포트하면 1개의 스크립트가 추가될 것입니다.
이 스크립트를 이용하여 래그돌을 구현해봅시다.


안내
제공되는 스크립트는 AnyPortrait v1.6.2와 Unity 6.2를 기반으로 작성되었습니다.




로봇 캐릭터의 대부분의 본에 더미 오브젝트들을 생성하기 위한 준비를 해야합니다.
(1) Bone 탭을 선택합니다.
(2)  Shift  키나 Ctrl 키를 눌러서 대부분의 본을 선택합니다. 또한 대상 본의 자식 본들도 선택해야합니다.
(3) 거의 모든 본들이 선택된 상태입니다.
(4) 선택한 본들의 "Socket" 옵션을 일괄적으로 활성화합니다.


참고
이 예제에서 저희는 루트 본인 "Bone Pelvis"를 선택에서 제외했습니다.
루트 본과 메시가 연결되지 않았으며, 그 역할을 루트 오브젝트("Character Group")가 대신 했기 때문입니다.
작업자마다 캐릭터의 구조가 다를 것이므로, 캐릭터의 구조에 맞게 적절히 설정하고 구현하시면 되겠습니다.




Bake를 하고 유니티 씬으로 돌아옵니다.
(1) GameObject를 새로 생성합니다.
(2) 생성한 GameObject에 다운로드한 "Portrait Ragdoll Simulator"스크립트를 추가합니다.
(3) "Character Objects" 항목의 멤버 변수에 오브젝트들을 적절히 할당합니다.




이 스크립트는 "Bone Infos"라는 항목에 본 정보를 입력하면, 이 정보를 바탕으로 더미 오브젝트를 생성합니다.
캐릭터의 모든 본들의 이름을 외워서 하나씩 입력해도 되지만, 이러면 너무 번거롭습니다.
그래서 "소켓 옵션이 활성화된 모든 본을 찾아서 자동으로 정보를 입력하는 기능"을 구현해두었습니다.
(1) "Make Hinge Infos" 버튼을 누릅니다.
(2) 안내 메시지를 확인한 후 Yes 버튼을 누릅니다.




소켓이 활성화된 본들을 대상으로 정보가 자동으로 채워집니다.
다만, 본의 구조상 일부 정보가 잘못 입력될 수도 있으니 하나씩 확인해봐야 합니다.
(1) 예를 들어 이 캐릭터의 경우, "Bone Spine"의 일부 항목이 잘못 입력된 상태입니다.
(2) "Child Bone Name" 항목에 "Bone Upper Arm R"이 입력된 상태인데, 이렇게 입력되면 몸통에 해당하는 더미 오브젝트가 오른쪽 어깨 방향으로 기울어져서 생성될 것입니다.




(1) "Bone Spine"의 끝점에 해당하는 본인 "Bone Head"의 이름을 "Child Bone Name"에 직접 입력해줍시다.






다른 생성 정보들을 확인해가면서 적절히 속성을 수정합니다.
위와 같이 회전 범위를 변경하면 더 자연스러운 래그돌을 만들 수 있습니다.


시뮬레이터 스크립트는 그 자체로 동작하지 않습니다.
별도의 스크립트를 작성하여 시뮬레이션을 실행시켜봅시다.



"ragdollSimulator.Initialize()"를 호출하기 전에 "portrait.Initialize()"를 먼저 호출해서 정상적으로 초기화를 진행해야 합니다.
이후에 "ragdollSimulator.StartSimulate()" 함수와 "ragdollSimulator.StopSimulate()"를 이용해서 시뮬레이터를 켜거나 종료합니다.
래그돌 시뮬레이터를 실행할 때는 애니메이션을 일시정지해주세요.




래그돌 시뮬레이터를 실행할 스크립트까지 작성했으니, 이것을 씬에 적용해봅시다.
(1) 새로운 GameObject를 생성합니다.
(2) 작성한 스크립트를 추가하고 멤버 변수에 적절히 할당합니다.




이제 게임을 실행해봅시다.
A 키를 누르면 래그돌이 활성화되어 캐릭터가 힘없이 바닥에 쓰러지는 것을 볼 수 있습니다.


여기서 약간의 수정을 더 해봅시다.
현재 생성된 더미 오브젝트들간의 충돌이 발생하여 관절이 덜 회전되는 것 같습니다.
구현 의도에 따라서 캐릭터의 관절들은 서로 충돌을 하지 않도록 만들어야 할 때도 있을 것입니다.




(1) Project Settings를 엽니다.
(2) Tags and Layers를 선택합니다.
(3) Layers에 캐릭터 전용의 새로운 레이어를 추가합니다. 여기서는 "Character"라는 이름의 레이어를 추가했습니다.




(1) Project SettingsPhysics 2D를 선택합니다.
(2) Layer Collision Matrix 탭을 선택합니다.
(3) 레이어 간의 충돌 여부에서 "Character - Character"의 체크를 해제합니다.




(1) 캐릭터의 루트 오브젝트를 선택합니다.
(2) Layer를 방금 추가한 "Character"로 변경하고 적용합니다.


래그돌 시뮬레이터 스크립트에는 다음과 같은 코드가 있어서, 생성된 더미 오브젝트들이 루트 오브젝트의 레이어와 동일한 값을 가집니다.





이제 게임을 실행하고 다시 래그돌를 켜봅시다.
캐릭터의 각 관절이 다른 관절과 충돌하지 않으므로 더 많이 구부러지는 것을 볼 수 있습니다.




마지막으로, 래그돌 시뮬레이터가 동작할 때 씬에서 어떤 상황이 발생하는지 확인해봅니다.
(1) 래그돌 시뮬레이터가 켜지면 더미 오브젝트들이 본의 구조와 유사하게 생성됩니다.
(2) 더미 오브젝트 중 "Ragdoll Root"를 제외한 각 더미 오브젝트에는 Rigidbody 2D, Capsule Collider 2DHinge Joint 2D 컴포넌트가 추가되어 있습니다.
(3) 캐릭터와 더미 오브젝트들이 동기화되어서 움직이는 것을 볼 수 있습니다.