UDN
Search public documentation:

DevelopmentKitGemsCreatingAModularPawn
日本語訳
中国翻译
한국어

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

Unreal Development Kit Home > Unreal Development Kit Gems > How to create a modular pawn

How to create a modular pawn


Last tested against UDK June, 2011
PC and iOS compatible

Overview


Many games these days allow players to customize the appearance of their virtual avatar. While attachments via sockets or bones work great for non animating or separate animating visuals; they don't work so well when you want the virtual avatar to move as a single entity. For example, imagine if you wanted to allow the player to create an avatar from a selection of shirts and pants. Because these parts when composed together form a humanoid, it will look very unrealistic when the animations do not synchronize appropriately.

In this example, we have Lauryn with a different shoulder pad arrangement, different boots and different arms. Shadows and animations work automatically with very little fuss.

ModularPawnTitle.jpg

Methods


There are two methods to achieving this effect.
  • Multiple skeletal mesh components per actor - This method is the most flexible as it allows run time changes to the skeletal mesh. However it can affect performance as each skeletal mesh actor will be an additional draw call.
  • Compositing meshes (Licensees only) - This method composites the mesh to generate a single mesh from multiple meshes. This method won't be described in this gem.

Related topics

Scripting the modular pawn


This is the modular pawn class which defines multiple skeletal mesh components. Each skeletal mesh component represents a part of the pawn which can be swapped out for another part. Only one skeletal mesh component is the parent skeletal mesh component, which is the head skeletal mesh component in this case.

The parent skeletal mesh component is responsible for calculating the skeleton animation data which is used by all of its child skeletal mesh components. All child skeletal mesh components also receive all translation, rotation and scale data from their parent too.

For best results, ensure that all skeletal meshes use the same skeleton and are designed for the same animations. All the vertices should be mapped directly to the skeleton as there is no need to recenter each skeletal mesh before exporting.

Lastly, all child skeletal mesh components have their shadow parent set to the same parent skeletal mesh component. This produces better performance when rendering dynamic shadows and prevents shadow overlapping problems.

ModularPawn.uc
class ModularPawn extends UTPawn
  Placeable;

// Skeletal mesh which represents the head. Parent skeletal mesh component.
var(ModularPawn) const SkeletalMeshComponent HeadSkeletalMesh;
// Skeletal mesh which represents the torso. Child to the head skeletal mesh component.
var(ModularPawn) const SkeletalMeshComponent TorsoSkeletalMesh;
// Skeletal mesh which represents the arms. Child to the head skeletal mesh component.
var(ModularPawn) const SkeletalMeshComponent ArmsSkeletalMesh;
// Skeletal mesh which represents the thighs. Child to the head skeletal mesh component.
var(ModularPawn) const SkeletalMeshComponent ThighsSkeletalMesh;
// Skeletal mesh which represents the boots. Child to the head skeletal mesh component.
var(ModularPawn) const SkeletalMeshComponent BootsSkeletalMesh;
// Skeletal mesh which represents the left shoulder pad. Child to the head skeletal mesh component.
var(ModularPawn) const SkeletalMeshComponent LeftShoulderPadSkeletalMesh;
// Skeletal mesh which represents the right shoulder pad. Child to the head skeletal mesh component.
var(ModularPawn) const SkeletalMeshComponent RightShoulderPadSkeletalMesh;

defaultproperties
{
  // Remove UTPawn's defined skeletal mesh
  Components.Remove(WPawnSkeletalMeshComponent)

  // Create the animation sequence
  Begin Object class=AnimNodeSequence Name=AnimNodeSequence
  End Object

  // Create the head skeletal mesh component
  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
    // Set the animation node sequence so we can test the animation
    Animations=AnimNodeSequence
  End Object
  HeadSkeletalMesh=HeadSkeletalMeshComponent
  Components.Add(HeadSkeletalMeshComponent)

  // Create the torso skeletal mesh component
  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
    // Assign the parent animation component to the head skeletal mesh component. This ensures that
    // the pawn animates as if it was one skeletal mesh component.
    ParentAnimComponent=HeadSkeletalMeshComponent
    // Assign the shadow parent component to the head skeletal mesh component. This is used to speed up
    // the rendering of the shadow for this pawn and to prevent shadow overlaps from occur.
    ShadowParent=HeadSkeletalMeshComponent
  End Object
  TorsoSkeletalMesh=TorsoSkeletalMeshComponent
  Components.Add(TorsoSkeletalMeshComponent)

  // Create the arms skeletal mesh component
  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
    // Assign the parent animation component to the head skeletal mesh component. This ensures that
    // the pawn animates as if it was one skeletal mesh component.
    ParentAnimComponent=HeadSkeletalMeshComponent
    // Assign the shadow parent component to the head skeletal mesh component. This is used to speed up
    // the rendering of the shadow for this pawn and to prevent shadow overlaps from occur.
    ShadowParent=HeadSkeletalMeshComponent
  End Object
  ArmsSkeletalMesh=ArmsSkeletalMeshComponent
  Components.Add(ArmsSkeletalMeshComponent)

  // Create the thighs skeletal mesh component
  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
    // Assign the parent animation component to the head skeletal mesh component. This ensures that
    // the pawn animates as if it was one skeletal mesh component.
    ParentAnimComponent=HeadSkeletalMeshComponent
    // Assign the shadow parent component to the head skeletal mesh component. This is used to speed up
    // the rendering of the shadow for this pawn and to prevent shadow overlaps from occur.
    ShadowParent=HeadSkeletalMeshComponent
  End Object
  ThighsSkeletalMesh=ThighsSkeletalMeshComponent
  Components.Add(ThighsSkeletalMeshComponent)

  // Create the boots skeletal mesh component
  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
    // Assign the parent animation component to the head skeletal mesh component. This ensures that
    // the pawn animates as if it was one skeletal mesh component.
    ParentAnimComponent=HeadSkeletalMeshComponent
    // Assign the shadow parent component to the head skeletal mesh component. This is used to speed up
    // the rendering of the shadow for this pawn and to prevent shadow overlaps from occur.
    ShadowParent=HeadSkeletalMeshComponent
  End Object
  BootsSkeletalMesh=BootsSkeletalMeshComponent
  Components.Add(BootsSkeletalMeshComponent)

  // Create the left shoulder pad skeletal mesh component
  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
    // Assign the parent animation component to the head skeletal mesh component. This ensures that
    // the pawn animates as if it was one skeletal mesh component.
    ParentAnimComponent=HeadSkeletalMeshComponent
    // Assign the shadow parent component to the head skeletal mesh component. This is used to speed up
    // the rendering of the shadow for this pawn and to prevent shadow overlaps from occur.
    ShadowParent=HeadSkeletalMeshComponent
  End Object
  LeftShoulderPadSkeletalMesh=LeftShouldPadSkeletalMeshComponent
  Components.Add(LeftShouldPadSkeletalMeshComponent)

  // Create the right shoulder pad skeletal mesh component
  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
    // Assign the parent animation component to the head skeletal mesh component. This ensures that
    // the pawn animates as if it was one skeletal mesh component.
    ParentAnimComponent=HeadSkeletalMeshComponent
    // Assign the shadow parent component to the head skeletal mesh component. This is used to speed up
    // the rendering of the shadow for this pawn and to prevent shadow overlaps from occur.
    ShadowParent=HeadSkeletalMeshComponent
  End Object
  RightShoulderPadSkeletalMesh=RightShoulderPadSkeletalMeshComponent
  Components.Add(RightShoulderPadSkeletalMeshComponent)
}

Notes

In the class above, an Anim Node Sequence was defined to allow you to test animations. This will stop the anim tree from working normally, thus remove the Anim Node Sequence from the class script or object (within an archetype) to get the anim tree working again.

Related topics

Modular Pawn Properties


After placing a ModularPawn into the world, opening up its properties window will reveal all of the skeletal meshes.

ModularPawnProperties.jpg

Expand each skeletal mesh object and set the skeletal mesh appropriately. The parent skeletal mesh component should have the Anim Tree Template, Physics Asset, AnimSets set. In this case, the Head Skeletal Mesh object would have all of these.

ModularPawnSettingProperties_A.jpg

As you set each skeletal mesh, they will appear in the editor.

ModularPawnSetSkeletalMeshes.jpg

The parent skeletal mesh component (Head Skeletal Mesh) has an Anim Node Sequence set to the Animations object. Expanding the Animations tab to reveal the Anim Node Sequence. Assign the Anim Seq Name to an animation that can found within the Anim Sets array. This will update the skeletal mesh components within the editor. To visualize the animations, run PIE.

ModularPawnSettingAnimationProperties.jpg

The parent skeletal mesh component (Head Skeletal Mesh) translation, rotation and scale affect the child skeletal mesh components. Adjust these if you find that the skeletal mesh components don't fit within the collision cylinder, or the skeletal mesh components aren't in the right orientation relative to the pawn rotation, or if the skeletal mesh components are too small. You can also adjust the child skeletal mesh component versions of these; just remember that these are relative to the parent's settings.

ModularPawnSettingPrimitiveComponentProperties.jpg

Related topics

Unreal AnimSet Editor


If you wish to visualize the modular pawn without having to run PIE, you can do this within the Unreal AnimSet Editor. By setting the "Extra Mesh #" fields (highlighted by the red fields), you can assign multiple meshes to the same skeleton and animations. In this example, most of Lauryn's meshes were assigned. However there weren't quite enough slots to assign the boots.

ModularPawnSettingTheExtraMeshInAnimSet.jpg

After you have set the extra meshes, click on the Anim tab to display the list of animations. Select any animation and play it. You should see all of the skeletal meshes animating.

ModularPawnPlayAnAnimationToTest.jpg

Related topics

When to use this...


There is another way to attach things onto skeletal meshes and that is by using socket attachments. So, the question is, when do you use socket attachment and this method? The answer is when you need animations to synchronize. In this particular case, the meshes all need to synchronize in order to look like a pawn. The shoulder pads could be done via socket attachments however.

Related topics