UDN
Search public documentation:

DevelopmentKitGemsCreatingAModularPawnKR
English Translation
日本語訳
中国翻译

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UDK 홈 > UDK 젬 > 모듈식 폰(ModularPawn) 만드는 법

모듈식 폰(ModularPawn) 만드는 법


문서 변경내역: James Tan 작성. 홍성진 번역.
UDK 2011년 6월 버전으로 최종 테스팅, PC 와 iOS 호환

개요


요즘 게임에서는 아바타 외형을 플레이어 마음대로 꾸밀 수 있도록 하는 추세입니다. 애니메이션이 없거나 있어도 별도의 시각 효과를 지닌 것에는 소켓이나 본을 통한 부착(attachment)으로 충분하지만, 단일 개체로 아바타를 움직이게 할 때는 잘 되지 않습니다. 예를 들어 아바타에 셔츠와 바지를 선택할 수 있게 해 놨다고 칩시다. 이러한 부분도 인간형 모양을 구성할 때 함께 포함되기 때문에, 애니메이션 싱크가 적절히 이루어 지지 않아 왠지 비현실적인 느낌이 나게 됩니다.

이 예제에서 로린(Lauryn) 에게 다양한 숄더 패드(어깨뽕), 부츠, 팔 보호대를 장착시켜 보겠습니다. 아주 약간만 수고해 줘도 그림자와 애니메이션이 자동으로 돌아갑니다.

ModularPawnTitle.jpg

방법


이 효과를 내는 방법은 두 가지 있습니다.
  • 액터당 여러 스켈레탈 메시 컴포넌트 - 런타임에 스켈레탈 메시를 변경할 수 있어 가장 유연한 방법입니다. 그러나 각 스켈레탈 메시당 드로 콜이 추가되므로 퍼포먼스에 영향을 끼칠 수 있습니다.
  • 메시 합성하기 (라이선시 전용) - 여러 메시를 합성하여 하나의 메시를 생성하는 방법입니다. 여기서는 이 방법을 다루지 않겠습니다.

관련 토픽

모듈식 폰 스크립팅


다수의 스켈레탈 메시 컴포넌트를 정의하는 모듈식 폰 클래스는 이와 같습니다. 각 스켈레탈 메시 컴포넌트는 다른 부분으로 맞바꿀 수 있는 폰의 일부를 나타냅니다. 딱 하나의 스켈레탈 메시 컴포넌트가 부모 스켈레탈 메시 컴포넌트이며, 이 경우에는 머리 스켈레탈 메시 컴포넌트 입니다.

부모 스켈레탈 메시 컴포넌트는 자식 스켈레탈 메시 컴포넌트 전부가 사용하는 스켈레톤 애니메이션 데이터 계산을 담당합니다. 자식 스켈레탈 메시 컴포넌트 전부도 역시 부모로부터 트랜슬레이션, 로테이션, 스케일 데이터를 받습니다.

최적의 결과를 내기 위해서라면 모든 스켈레탈 메시가 같은 스켈레톤을 사용하는지, 같은 애니메이션 용으로 디자인되었는지를 확인해야 합니다. 각 스켈레탈 메시를 익스포트하기 전에 다시 중심을 맞출 필요가 없기에, 모든 버텍스를 스켈레톤에 직접 매핑해도 됩니다.

마지막으로 모든 자식 스켈레탈 메시 컴포넌트는 부모 스켈레탈 메시 컴포넌트와 동일하게 그림자 부모를 설정해 주면 됩니다. 그래야 다이내믹 섀도우를 렌더할 때 퍼포먼스도 더 잘 나오며, 그림자가 겹치는 문제도 예방할 수 있습니다.

ModularPawn.uc
class ModularPawn extends UTPawn
  Placeable;

// 머리를 나타내는 스켈레탈 메시. 부모 스켈레탈 메시 컴포넌트.
var(ModularPawn) const SkeletalMeshComponent HeadSkeletalMesh;
// 몸통을 나타내는 스켈레탈 메시. 머리 스켈레탈 메시 컴포넌트의 자식 입니다.
var(ModularPawn) const SkeletalMeshComponent TorsoSkeletalMesh;
// 팔을 나타내는 스켈레탈 메시. 머리 스켈레탈 메시 컴포넌트의 자식 입니다.
var(ModularPawn) const SkeletalMeshComponent ArmsSkeletalMesh;
// 허벅지를 나타내는 스켈레탈 메시. 머리 스켈레탈 메시 컴포넌트의 자식 입니다.
var(ModularPawn) const SkeletalMeshComponent ThighsSkeletalMesh;
// 부츠를 나타내는 스켈레탈 메시. 머리 스켈레탈 메시 컴포넌트의 자식 입니다.
var(ModularPawn) const SkeletalMeshComponent BootsSkeletalMesh;
// 왼쪽 숄더 패드를 나타내는 스켈레탈 메시. 머리 스켈레탈 메시 컴포넌트의 자식 입니다.
var(ModularPawn) const SkeletalMeshComponent LeftShoulderPadSkeletalMesh;
// 오른쪽 숄더 패드를 나타내는 스켈레탈 메시. 머리 스켈레탈 메시 컴포넌트의 자식 입니다.
var(ModularPawn) const SkeletalMeshComponent RightShoulderPadSkeletalMesh;

defaultproperties
{
  // UTPawn 에 정의된 스켈레탈 메시 제거
  Components.Remove(WPawnSkeletalMeshComponent)

  // 애니메이션 시퀸스 생성
  Begin Object class=AnimNodeSequence Name=AnimNodeSequence
  End Object

  // 머리 스켈레탈 메시 컴포넌트 생성
  Begin Object Class=SkeletalMeshComponent Name=HeadSkeletalMeshComponent
    bCacheAnimSequenceNodes=false
    AlwaysLoadOnClient=true
    AlwaysLoadOnServer=true
    bOwnerNoSee=true
    CastShadow=true
    BlockRigidBody=true
    bUpdateSkelWhenNotRendered=false
    bIgnoreControllersWhenNotRendered=true
    bUpdateKinematicBonesFromAnimation=true
    bCastDynamicShadow=true
    RBChannel=RBCC_Untitled3
    RBCollideWithChannels=(Untitled3=true)
    LightEnvironment=MyLightEnvironment
    bOverrideAttachmentOwnerVisibility=true
    bAcceptsDynamicDecals=false
    AnimTreeTemplate=AnimTree'CH_AnimHuman_Tree.AT_CH_Human'
    bHasPhysicsAssetInstance=true
    TickGroup=TG_PreAsyncWork
    MinDistFactorForKinematicUpdate=0.2
    bChartDistanceFactor=true
    RBDominanceGroup=20
    MotionBlurScale=0.0
    bUseOnePassLightingOnTranslucency=true
    bPerBoneMotionBlur=true
    // 애니메이션을 테스트할 수 있도록 애니메이션 노드 시퀸스 설정
    Animations=AnimNodeSequence
  End Object
  HeadSkeletalMesh=HeadSkeletalMeshComponent
  Components.Add(HeadSkeletalMeshComponent)

  // 몸통 스켈레탈 메시 컴포넌트 생성
  Begin Object Class=SkeletalMeshComponent Name=TorsoSkeletalMeshComponent
    bCacheAnimSequenceNodes=false
    AlwaysLoadOnClient=true
    AlwaysLoadOnServer=true
    bOwnerNoSee=true
    CastShadow=true
    BlockRigidBody=true
    bUpdateSkelWhenNotRendered=false
    bIgnoreControllersWhenNotRendered=true
    bUpdateKinematicBonesFromAnimation=true
    bCastDynamicShadow=true
    RBChannel=RBCC_Untitled3
    RBCollideWithChannels=(Untitled3=true)
    LightEnvironment=MyLightEnvironment
    bOverrideAttachmentOwnerVisibility=true
    bAcceptsDynamicDecals=false
    bHasPhysicsAssetInstance=true
    TickGroup=TG_PreAsyncWork
    MinDistFactorForKinematicUpdate=0.2
    bChartDistanceFactor=true
    RBDominanceGroup=20
    MotionBlurScale=0.0
    bUseOnePassLightingOnTranslucency=true
    bPerBoneMotionBlur=true
    // 부모 애니메이션 컴포넌트를 머리 스켈레탈 메시 컴포넌트에 할당.
    // 스켈레탈 메시 컴포넌트가 하나인 것처럼 폰이 애니메이팅할 수 있도록 해 줍니다.
    ParentAnimComponent=HeadSkeletalMeshComponent
    // 그림자 부모 컴포넌트를 머리 스켈레탈 메시 컴포넌트에 할당. 그렇게 하는 이유는
    // 이 폰의 그림자 렌더링 속도를 높이고 그림자가 겹치지 않도록 하기 위해서 입니다.
    ShadowParent=HeadSkeletalMeshComponent
  End Object
  TorsoSkeletalMesh=TorsoSkeletalMeshComponent
  Components.Add(TorsoSkeletalMeshComponent)

  // 팔 스켈레탈 메시 컴포넌트 생성
  Begin Object Class=SkeletalMeshComponent Name=ArmsSkeletalMeshComponent
    bCacheAnimSequenceNodes=false
    AlwaysLoadOnClient=true
    AlwaysLoadOnServer=true
    bOwnerNoSee=true
    CastShadow=true
    BlockRigidBody=true
    bUpdateSkelWhenNotRendered=false
    bIgnoreControllersWhenNotRendered=true
    bUpdateKinematicBonesFromAnimation=true
    bCastDynamicShadow=true
    RBChannel=RBCC_Untitled3
    RBCollideWithChannels=(Untitled3=true)
    LightEnvironment=MyLightEnvironment
    bOverrideAttachmentOwnerVisibility=true
    bAcceptsDynamicDecals=false
    bHasPhysicsAssetInstance=true
    TickGroup=TG_PreAsyncWork
    MinDistFactorForKinematicUpdate=0.2
    bChartDistanceFactor=true
    RBDominanceGroup=20
    MotionBlurScale=0.0
    bUseOnePassLightingOnTranslucency=true
    bPerBoneMotionBlur=true
    // 부모 애니메이션 컴포넌트를 머리 스켈레탈 메시 컴포넌트에 할당.
    // 스켈레탈 메시 컴포넌트가 하나인 것처럼 폰이 애니메이팅할 수 있도록 해 줍니다.
    ParentAnimComponent=HeadSkeletalMeshComponent
    // 그림자 부모 컴포넌트를 머리 스켈레탈 메시 컴포넌트에 할당. 그렇게 하는 이유는
    // 이 폰의 그림자 렌더링 속도를 높이고 그림자가 겹치지 않도록 하기 위해서 입니다.
    ShadowParent=HeadSkeletalMeshComponent
  End Object
  ArmsSkeletalMesh=ArmsSkeletalMeshComponent
  Components.Add(ArmsSkeletalMeshComponent)

  // 허벅지 스켈레탈 메시 컴포넌트 생성
  Begin Object Class=SkeletalMeshComponent Name=ThighsSkeletalMeshComponent
    bCacheAnimSequenceNodes=false
    AlwaysLoadOnClient=true
    AlwaysLoadOnServer=true
    bOwnerNoSee=true
    CastShadow=true
    BlockRigidBody=true
    bUpdateSkelWhenNotRendered=false
    bIgnoreControllersWhenNotRendered=true
    bUpdateKinematicBonesFromAnimation=true
    bCastDynamicShadow=true
    RBChannel=RBCC_Untitled3
    RBCollideWithChannels=(Untitled3=true)
    LightEnvironment=MyLightEnvironment
    bOverrideAttachmentOwnerVisibility=true
    bAcceptsDynamicDecals=false
    bHasPhysicsAssetInstance=true
    TickGroup=TG_PreAsyncWork
    MinDistFactorForKinematicUpdate=0.2
    bChartDistanceFactor=true
    RBDominanceGroup=20
    MotionBlurScale=0.0
    bUseOnePassLightingOnTranslucency=true
    bPerBoneMotionBlur=true
    // 부모 애니메이션 컴포넌트를 머리 스켈레탈 메시 컴포넌트에 할당.
    // 스켈레탈 메시 컴포넌트가 하나인 것처럼 폰이 애니메이팅할 수 있도록 해 줍니다.
    ParentAnimComponent=HeadSkeletalMeshComponent
    // 그림자 부모 컴포넌트를 머리 스켈레탈 메시 컴포넌트에 할당. 그렇게 하는 이유는
    // 이 폰의 그림자 렌더링 속도를 높이고 그림자가 겹치지 않도록 하기 위해서 입니다.
    ShadowParent=HeadSkeletalMeshComponent
  End Object
  ThighsSkeletalMesh=ThighsSkeletalMeshComponent
  Components.Add(ThighsSkeletalMeshComponent)

  // 부츠 스켈레탈 메시 컴포넌트 생성
  Begin Object Class=SkeletalMeshComponent Name=BootsSkeletalMeshComponent
    bCacheAnimSequenceNodes=false
    AlwaysLoadOnClient=true
    AlwaysLoadOnServer=true
    bOwnerNoSee=true
    CastShadow=true
    BlockRigidBody=true
    bUpdateSkelWhenNotRendered=false
    bIgnoreControllersWhenNotRendered=true
    bUpdateKinematicBonesFromAnimation=true
    bCastDynamicShadow=true
    RBChannel=RBCC_Untitled3
    RBCollideWithChannels=(Untitled3=true)
    LightEnvironment=MyLightEnvironment
    bOverrideAttachmentOwnerVisibility=true
    bAcceptsDynamicDecals=false
    bHasPhysicsAssetInstance=true
    TickGroup=TG_PreAsyncWork
    MinDistFactorForKinematicUpdate=0.2
    bChartDistanceFactor=true
    RBDominanceGroup=20
    MotionBlurScale=0.0
    bUseOnePassLightingOnTranslucency=true
    bPerBoneMotionBlur=true
    // 부모 애니메이션 컴포넌트를 머리 스켈레탈 메시 컴포넌트에 할당.
    // 스켈레탈 메시 컴포넌트가 하나인 것처럼 폰이 애니메이팅할 수 있도록 해 줍니다.
    ParentAnimComponent=HeadSkeletalMeshComponent
    // 그림자 부모 컴포넌트를 머리 스켈레탈 메시 컴포넌트에 할당. 그렇게 하는 이유는
    // 이 폰의 그림자 렌더링 속도를 높이고 그림자가 겹치지 않도록 하기 위해서 입니다.
    ShadowParent=HeadSkeletalMeshComponent
  End Object
  BootsSkeletalMesh=BootsSkeletalMeshComponent
  Components.Add(BootsSkeletalMeshComponent)

  // 왼쪽 숄더 패드 스켈레탈 메시 컴포넌트 생성
  Begin Object Class=SkeletalMeshComponent Name=LeftShouldPadSkeletalMeshComponent
    bCacheAnimSequenceNodes=false
    AlwaysLoadOnClient=true
    AlwaysLoadOnServer=true
    bOwnerNoSee=true
    CastShadow=true
    BlockRigidBody=true
    bUpdateSkelWhenNotRendered=false
    bIgnoreControllersWhenNotRendered=true
    bUpdateKinematicBonesFromAnimation=true
    bCastDynamicShadow=true
    RBChannel=RBCC_Untitled3
    RBCollideWithChannels=(Untitled3=true)
    LightEnvironment=MyLightEnvironment
    bOverrideAttachmentOwnerVisibility=true
    bAcceptsDynamicDecals=false
    bHasPhysicsAssetInstance=true
    TickGroup=TG_PreAsyncWork
    MinDistFactorForKinematicUpdate=0.2
    bChartDistanceFactor=true
    RBDominanceGroup=20
    MotionBlurScale=0.0
    bUseOnePassLightingOnTranslucency=true
    bPerBoneMotionBlur=true
    // 부모 애니메이션 컴포넌트를 머리 스켈레탈 메시 컴포넌트에 할당.
    // 스켈레탈 메시 컴포넌트가 하나인 것처럼 폰이 애니메이팅할 수 있도록 해 줍니다.
    ParentAnimComponent=HeadSkeletalMeshComponent
    // 그림자 부모 컴포넌트를 머리 스켈레탈 메시 컴포넌트에 할당. 그렇게 하는 이유는
    // 이 폰의 그림자 렌더링 속도를 높이고 그림자가 겹치지 않도록 하기 위해서 입니다.
    ShadowParent=HeadSkeletalMeshComponent
  End Object
  LeftShoulderPadSkeletalMesh=LeftShouldPadSkeletalMeshComponent
  Components.Add(LeftShouldPadSkeletalMeshComponent)

  // 오른쪽 숄더 패드 스켈레탈 메시 컴포넌트 생성
  Begin Object Class=SkeletalMeshComponent Name=RightShoulderPadSkeletalMeshComponent
    bCacheAnimSequenceNodes=false
    AlwaysLoadOnClient=true
    AlwaysLoadOnServer=true
    bOwnerNoSee=true
    CastShadow=true
    BlockRigidBody=true
    bUpdateSkelWhenNotRendered=false
    bIgnoreControllersWhenNotRendered=true
    bUpdateKinematicBonesFromAnimation=true
    bCastDynamicShadow=true
    RBChannel=RBCC_Untitled3
    RBCollideWithChannels=(Untitled3=true)
    LightEnvironment=MyLightEnvironment
    bOverrideAttachmentOwnerVisibility=true
    bAcceptsDynamicDecals=false
    bHasPhysicsAssetInstance=true
    TickGroup=TG_PreAsyncWork
    MinDistFactorForKinematicUpdate=0.2
    bChartDistanceFactor=true
    RBDominanceGroup=20
    MotionBlurScale=0.0
    bUseOnePassLightingOnTranslucency=true
    bPerBoneMotionBlur=true
    // 부모 애니메이션 컴포넌트를 머리 스켈레탈 메시 컴포넌트에 할당.
    // 스켈레탈 메시 컴포넌트가 하나인 것처럼 폰이 애니메이팅할 수 있도록 해 줍니다.
    ParentAnimComponent=HeadSkeletalMeshComponent
    // 그림자 부모 컴포넌트를 머리 스켈레탈 메시 컴포넌트에 할당. 그렇게 하는 이유는
    // 이 폰의 그림자 렌더링 속도를 높이고 그림자가 겹치지 않도록 하기 위해서 입니다.
    ShadowParent=HeadSkeletalMeshComponent
  End Object
  RightShoulderPadSkeletalMesh=RightShoulderPadSkeletalMeshComponent
  Components.Add(RightShoulderPadSkeletalMeshComponent)
}

알림

위의 클래스에서 애니메이션 테스트를 위해 Anim Node Sequence 가 정의되었습니다. 이때문에 애님 트리가 정상 작동하지 않을 것인데, 다시 작동하게 하려면 (아키타입 내의) 오브젝트나 클래스 스크립트에서 애님 노드 시퀸스를 제거하시기 바랍니다.

관련 토픽

ModularPawn 프로퍼티


월드에 ModularPawn 을 놓고난 후 프로퍼티 창을 열면 모든 스켈레탈 메시가 드러날 것입니다.

ModularPawnProperties.jpg

각 스켈레탈 메시 오브젝트를 확장한 다음 스켈레탈 메시를 적절히 설정해 줍니다. 부모 스켈레탈 메시 컴포넌트에는 Anim Tree Template, Physics Asset, AnimSets 세트가 있어야 합니다. 이 경우 Head Skeletal Mesh 오브젝트에 전부 담겨 있을 것입니다.

ModularPawnSettingProperties_A.jpg

각 스켈레탈 메시를 설정하면 에디터에 나타날 것입니다.

ModularPawnSetSkeletalMeshes.jpg

부모 스켈레탈 메시 컴포넌트 (Heal Skeletal Mesh) 에는 애니메이션 오브젝트에 설정된 Anim Node Sequence 가 있습니다. Animations 탭을 확장해 보면 Anim Node Sequence 가 펼쳐집니다. Anim Seq Name 을 Anim Sets 배열에서 찾을 수 있는 애니메이션에 할당합니다. 그러면 에디터 안의 스켈레탈 메시 컴포넌트가 업데이트될 것입니다. 애니메이션을 직접 확인해 보려면, PIE 로 돌려 보면 됩니다.

ModularPawnSettingAnimationProperties.jpg

부모 스켈레탈 메시 컴포넌트 (Heal Skeletal Mesh) 트랜슬레이션, 로테이션, 스케일은 자식 스켈레탈 메시 컴포넌트에 영향을 끼칩니다. 스켈레탈 메시 컴포넌트가 콜리전 실린더에 들어맞지 않는다거나, 스켈레탈 메시 컴포넌트의 방향이 폰 로테이션에 잘 맞지 않는다던가, 스켈레탈 메시 컴포넌트가 너무 작다던가 할 경우에 부모의 것을 고쳐주면 됩니다. 자식 스켈레탈 메시 컴포넌트의 것을 고쳐줄 수도 있지만, 부모 세팅에 상대적으로 작동된다는 점 기억하시기 바랍니다.

ModularPawnSettingPrimitiveComponentProperties.jpg

관련 토픽

언리얼 애님세트 에디터


PIE 말고 모듈식 폰을 확인해 보는 방법엔 언리얼 애님세트 에디터가 있습니다. "Extra Mesh #" (빨강 반전 필드 부분)을 설정해 주면 동일한 스켈레톤과 애니메이션에 여러 메시를 할당해줄 수 있습니다. 이 예제에서는 로린의 메시 대부분이 할당되었습니다. 그러나 부츠를 할당해 줄 슬롯이 충분치 않았습니다.

ModularPawnSettingTheExtraMeshInAnimSet.jpg

여분의 메시를 설정해 준 다음, Anim 탭을 클릭하면 애니메이션 목록이 표시됩니다. 아무 애니메이션을 선택하여 재생합니다. 모든 스켈레탈 메시의 애니메이션을 볼 수 있을 것입니다.

ModularPawnPlayAnAnimationToTest.jpg

관련 토픽

이 기능을 언제 사용하면 좋을지...


스켈레탈 메시에 뭔가를 부착하는 방법은 더 있는데, 소켓 부착을 사용하는 것입니다. 이 방법과 소켓 방법중에 어느 것이 나은가가 문제입니다. 답은 애니메이션을 동기화(synchronize)시킬 필요가 있을 때입니다. 이번 경우를 예로 들자면, 메시 전부가 동기화되어야 폰같아 보일 것입니다. 그래도 숄더 패드는 따로 돌아다녀도 상관 없을테니 소켓 부착 식으로도 가능합니다.

관련 토픽