Unreal 공부

[Unreal] Kawaii Physics 플러그인 분석 및 수정을 통한 캐릭터 애니메이션 커스터마이징

겨울꿈개발자 2021. 10. 20. 21:42

이 글은 이전 글에서 이어집니다.

https://dream-winter.tistory.com/22

 

[Unreal] Custom Anim Node 만들기

현재 Unreal을 시작한지 약 2주일 정도 된것 같습니다. Unity를 추석동안 잠깐 만져보다가 Unreal을 배우면 많은 공부가 될 것 같아서 본격적으로 진행해보고자 합니다. 목표는 간단한 3D 게임을 만들

dream-winter.tistory.com

이전 글에서 진행한 플러그인 생성 튜토리얼을 통해 어느 정도 언리얼 플러그인에 대한 느낌을 잡았습니다.

 

이번에는 캐릭터의 옷, 머리카락 등의 뼈 제어에 사용될 수 있는 Kawaii Physics 플러그인을 분석하고 

해당 플러그인을 약간 수정해서 원하는 캐릭터 애니메이션을 만들도록 해보았습니다.


Kawaii Physics 플러그인은 적절한 파라메터를 통해 캐릭터의 뼈 애니메이션을 쉽게 커스터마이징할 수 있는 언리얼 플러그인입니다. 언리얼에서 기본으로 제공하고 있는 AnimDynaimcs 기능과 비슷하며, 해당 기능보다 기본적으로 쉽게 사용이 가능하며, 충돌체를 임의로 추가하여 해당 충돌체를 피하는 애니메이션을 만들 수 있습니다. 이는 캐릭터의 몸이 옷을 뚫고 지나가는 것을 방지하는 목적으로 사용될 수 있습니다.

그 외 움직임에 따라 머리카락이 흔들리는 등, 캐릭터를 좀더 입체감 있게 표현할 수 있도록 합니다.

 

https://github.com/pafuhana1213/KawaiiPhysics

 

GitHub - pafuhana1213/KawaiiPhysics: KawaiiPhysics : Simple fake Physics for UnrealEngine4

KawaiiPhysics : Simple fake Physics for UnrealEngine4 - GitHub - pafuhana1213/KawaiiPhysics: KawaiiPhysics : Simple fake Physics for UnrealEngine4

github.com

https://docs.unrealengine.com/4.26/ko/AnimatingObjects/SkeletalMeshAnimation/NodeReference/SkeletalControls/AnimDynamics/

 

AnimDynamics

AnimDynamics 를 사용하여 물리 기반 이차 애니메이션을 내는 법을 설명합니다.

docs.unrealengine.com


Kawaii Physics 플러그인의 폴더 구조는 아래와 같습니다.

Kawaii Physics 플러그인 구조

해당 플러그인을 분석한 내용은 아래와 같습니다.

더보기

• KawaiiPhysics.uplugin

    ○ 두개의 모듈을 사용함을 알 수 있음. (아마도 Runtime 및 Editor)

        § KawaiiPhysics (Runtime)

        § KawaiiPhysicsEd (UncookedOnly)

       

• *.Build.cs => 두 모듈 모두 가지고 있는 파일이며, 해당 모듈의 종속성을 표현하는 것으로 보임. (특별히 눈여겨볼거는 없는 듯함.)

 

• 클래스 호출 관계 정리

    ○ KawaiiPhysicsEd

        § IModuleInterface 인터페이스를 사용하는 FKawaiiPhysicEdModule 클래스 (-> StartupModule 함수때, FKawaiiPhysicEditMode 클래스를 호출)

        § FKawaiiPhysicEditMode 클래스는 FKawaiiPhysicEditModeBase 클래스를 상속받는다.

            ® EnterMode일때 FAnimNode_KawaiiPhysics, UAnimGraphNode_KawaiiPhysics 클래스를 각각 RuntimeNode, GraphNode 변수로써 저장함.

        § FKawaiiPhysicsEditModeBase 클래스는 IAnimNodeEditMode 클래스를 상속받는다.

    ○ KawaiiPhysics

        § FAnimNode_KawaiiPhysics 클래스 정의 (Editor 클래스에서 Runtime 변수로 호출)

            ® FAnimNode_SkeletalControlBase 클래스를 상속받는다.

 

• KawaiiPhysicsEd

    ○ KawaiiPhysicsEd.(h|cpp)

        § IModuleInterface 인터페이스 사용하는 FKawaiiPhysicsEdModule 클래스 선언

        § StartupModule, ShutdownModule 이름의 virtual void 함수 선언

        § LOCTEXT_NAMESPACE "FKawaiiPhysicsModuleEd"

        § AnimGraph.SkeletalControl.KawaiiPhysics 이름의 FKawaiiPhysicsEditMode 클래스를 각각 Register 및 Unregister하는 StartupModule, Shutdown 함수 정의

            ® 추정하건데, AnimGraph.SkeletalControl은 아마 Editor 상에서 해당 노드가 속해있는 그룹명을 지정하는 것으로 보임.

            ® LOCTEXT() 매크로의 2번째 인자인 Kawaii Physics는 실제 에디터 상에서 표시되는 노드의 이름으로 보인다.

            ® 실제 해당 노드의 에디터 역할은 FKawaiiPhysicsEditMode 클래스가 담당하는 것으로 보인다.

    ○ KawaiiPhysicsEd.(h|cpp)

        § FKawaiiPhysicsEditModeBase 클래스를 상속받는 FKawaiiPhysicEditMode 클래스 선언

        § 클래스 생성자 -> RuntimeNode, GraphNode 는 널포인터로 초기화 + CurWidgetMode는 WM_Translate로 초기화

        § 언리얼 기본 제공 클래스 IAnimNodeEditMode 클래스(FKawaiiPhysicsEditModeBase 클래스의 부모 클래스)의 virtual 함수들을 정의

            ® 추가적으로 다른 클래스들의 virtual 함수들도 포함되어있는것으로 보임.

            ® 주로 에디터에서 해당 노드를 클릭하거나 위치를 옮기거나 할때의 실행되는 기능들을 정의

            ® EnterMode일때 RuntimeNode 변수에는 FAnimNode_KawaiiPhysics 클래스, GraphNode 변수에는 UAnimGraphNode_KawaiiPhysics 클래스를 저장함.

    ○ (KawaiiPhysicsEditModeBase|AnimGraphNode_KawaiiPhysics).(h|cpp)

        § 기타 추가적인 그래픽 처리 쪽을 담당하는 기능들이 정의되어있음.

        § 우선 추가 기능 개발에 대해서는 그래픽 처리쪽은 아직 관심이 없기에 pass

       

• KawaiiPhysics

    ○ KawaiiPhysics.(h|cpp) -> StartupModule 및 ShutdownModule 함수 정의 (함수 내용은 비어있음)

    ○ KawaiiPhysicsLimitsDataAsset.(h|cpp) -> Sphere, Capsule, Planar 형태의 Collision Limit 데이터 형식 저장

    ○ AnimNode_KawaiiPhysics.(h|cpp)

        § FAnimNode_KawaiiPhysics 클래스를 정의하고 있다.

            ® FAnimNode_SkeletalControlBase 클래스를 상속받고 있다.

            ® 실제 에디터 상에서 볼 수 있는 파라메터들이 public 멤버 변수로 정의되어 있다.

            ® 결과적으로 실제 런타임내에서 핵심 기능은 EvaluateSkeletalControl_AnyThread() 함수에서 담당하는 것으로 보인다.

                ® Update(Spherical|Capsule|Planer)Limits - 충돌체들의 위치/회전 정보를 업데이트 (관련되어있는 뼈 위치/회전을 그대로 반영)

                ® 충돌에 의한 뼈의 움직임은 SimulateModifyBones 함수에서 수행하는 것으로 보인다.

                    ◊ 그 내에서도 AdjustBy(Sphere|Capsule|Planer)Collision 함수

                        } 근데 왜 (Sphere|Capsule|Planer)Limits 랑 (Sphere|Capsule|Planer)lLimitsData 가 동시에 존재하지?

                            – LimitsData는 Experimental 주석이 달려있음. (실제로 에디터에서는 사용 못하는 것으로 보임)

                                w VisibleAnyWhere 태그가 달려있으며, 이는 ReadOnly의 의미와 같다고 한다.

                        } AdjustBySphereCollision

                            – Bone.Location += (LimitDistance - (Bone.Location - Sphere.Location).Size()) * (Bone.Location - Sphere.Location).GetSafeNormal();

                            – 간단히 해석해보면, 구 충돌체에 의해 영향받는 뼈는 구 충돌체와의 방향벡터에 Limit를 벗어날 정도만큼 이동한다.

                        } AdjustByCapsuleCollision

                            – 구보다는 복잡한 형태이지만, 결과적으로는 구와 비슷하게 처리될 것으로 보인다.

                        } AdjustByPlanarCollision

                            – Bone.Location = PointOnPlane + Planar.Rotation.GetUpVector() * Bone.PhysicsSettings.Radius;

                            – 평면 회전의 +Z축 벡터방향으로 움직인다. (Z축 방향으로만 움직이는 것으로 보니, 뼈의 움직이는 방향을 평면의 Z축 방향으로 결정할 수 있는것으로 보인다.)

                            – 충돌 조건이 단지 평면과 구체와의 거리이고, 특정 평면 위에 있어야 한다는 조건은 없는 것으로 보임.

                                w 해당 부분에 대한 조건 추가 필요

해당 플러그인은 언리얼 4.26 버전에서 사용이 가능하나, 코드를 수정하고 빌드하는 것은 지원을 안하는 것으로 보입니다. 따라서 해당 코드를 다 가져와서 언리얼 4.27 버전에서 플러그인을 새로 만들었습니다. 만드는 김에 노드의 이름은 Kawaii Physics 에서 Customized Kawaii Physics 로 바꿨습니다.

언리얼 4.27에서 새로 추가한 Customized Kawaii Physics 플러그인

아래 그림은 해당 플러그인에서 수정한 부분입니다.

기존 함수 AdjustByPlanerCollision은 사용자가 추가한 평면 충돌체에 따라 뼈들의 위치를 제어하는 함수입니다.

기존 코드에서 681~684번줄을 추가했는데, 이는 현재 뼈가 평면 충돌체의 윗 부분 (법선벡터 방향) 에만 존재하도록 하였습니다. 

 

682번줄: 현재 뼈의 위치 벡터 - 현재 뼈를 평면 충돌체에 정사영한 위치 벡터 => 평면으로부터 현재 뼈의 위치 벡터

683번줄: 평면 충돌체의 법선 벡터

684번줄: 평면으로부터 현재 뼈의 위치 벡터와 평면 충돌체의 법선 벡터의 내적 곱

 

684번줄의 내적 곱이 양수이면, 현재 뼈는 평면 충돌체의 법선 벡터 방향에 있다는 것이고, 음수면 법선 벡터 반대 방향에 있다는 의미입니다. 법선 벡터 반대 방향에 있다는 것은 평면 아래에 있다는 것이고, 이럴 경우에는 기존의 위치 변환 코드(691번줄)가 적용되도록 하였습니다.

평면 충돌체 코드 수정한 부분

참고로 기존 코드는 아래 경우에만 위치 변환 코드가 적용됩니다.

1. 현재 뼈가 평면에 충돌할때 (CollisionDetected, 687번줄)

2. 이전 뼈의 위치랑 현재 뼈가 현재 평면을 지나갈때 (IntersectionDetected, 688번줄)

 

IntersectionDetected 변수가 있더라도, 만약 평면 충돌체의 위치가 크게 변해서 이전 뼈와 현재 뼈가 전부 평면 충돌체 아래에 있으면, 그대로 적용됩니다. 그 이유 때문에 기존 플러그인은 아래 그림과 같이 일부 치마가 다리 밑으로 내려가는 일이 있었습니다.

기존 Kawaii Physics 플러그인을 사용할때 캐릭터 옷이 다리 밑으로 내려가는 경우 발생


결과적으로 플러그인을 수정해서 캐릭터의 치마가 다리 밑으로 내려가는 것을 방지하였으며,

캐릭터의 달리기 뿐만 아니라 점프 모션에 대해서도 해당 문제가 발생하지 않는 것을 확인하였습니다.

 

이를 통해 기존 언리얼에서 제공하는 3인칭 게임에 제가 만든 캐릭터 애니메이션을 입힐 수 있었습니다.

 

https://www.youtube.com/watch?v=J9dhWaBoRac 

언리얼 3인칭 게임에 캐릭터 애니메이션을 입힌 결과

걷기모션에서 다리가 치마를 뚫는 부분이 가끔 보이는데,

1. 대기/걷기 모션에는 이번에 수정한 평면 충돌체를 사용하지 않았었고

2. Speed 변수에 따라 대기/걷기/달리기 모션이 블렌딩되는데 이 과정에서 발생하는 것으로 보입니다.

 

해당 문제는 추후에 수정할 계획입니다.