Understanding Copy Motion

Hi,

We are in the process of exploring how to implement upper body animations on top of motion matching.

The copy motion strategy presented on 2024’s GDC looks very promising but we are finding it a bit difficult to grasp what the node is actually intending to do and what all the parameters mean.

There are two other posts on UDN that refer to the copy motion node but we were looking for a bit more information if possible.

Currently, we are trying to replicate almost to the T the example shown on the GDC talk. As you can see in the images attached we even used the same names on the cached poses to avoid confusion.

The item pose that we are using has the character holding two guns on a ready state, while the neutral pose has him with his arms by the side.

As explained on the UDN post, we first apply a layered blend per bone filter to the motion matching result and the item pose so that we get a stiff upper body holding two guns with the ready pose. We then calculate a dynamic additive from the motion matching pose to the neutral pose and apply it to the item pose only affecting the spine bones (we cut it at the clavicles and neck).

This produces an animation that looks a little stiff on the arms (since we are using a single frame anim) but carries the motion of the spine, which makes the arms swing side to side.

After looking at the copy motion node code, we noticed that:

  • Base pose is ignored if a pose history node can be fetched with a valid delay an bUseBasePose is true
  • The node appears to find the delta transform between the source bone and the bone to modify (which I imagine should be the same bone most of the time) in the space of CopySpace bone.
  • It then applies that delta in the space of ApplySpace bone to the given BoneToModify.
  • Deltas are calculated from the motion matching unarmed pose and a past motion matching unarmed pose.

From the results shown, I imagine the intention is to achieve the effect of overlapping actions on the arms, however we are struggling to understand what values to use for pretty much all of the parameters in the node. So far we haven’t been able to achieve any reasonable results.

We also noticed the TargetCurveName and the fact that a curve is stored but not really sure of how that relates to the big picture.

Lastly, I imagine there is some sort “correct” configuration on the leg IK node to make the hands look right, it’d be nice to get a run down of how those parameters ought to look.

Would it be possible to get an explanation behind the rationale of the node? what it generally intends to do and why?

Could we get some sample parameter values if available for the copy motion node and the leg IK node for configurations where:

  • You hold an item by the side with 1 hand
  • You aim 1 gun with 1 hand
  • You aim 2 guns with 2 hands
  • You carry a two handed weapon (like pick axe)
  • You aim a two handed weapon (like a rifle).

We are interested here in seeing what the specific configuration is for SourceBone, BoneToModify, CopySpace and ApplySpace.

Also to understand better what all the rotation, curve and translation parameters are for.

Attaching screenshots of the current work for more context.

Thank you in advance!

Steps to Reproduce
Use copy motion.

Just wanted to give an update on this.

I spent some time looking at the node and I think I have a rough understanding of what is going on but confirmation would be appreciated.

From what I have gathered, the node is atttempting to get the delta motion from a given bone from a time in the past to the current time. The intention I imagine is to include the notion of overlapping actions into the static upper body pose (so spine and upper body are not overly in sync).

The biggest issues we were finding were to do with the PoseHistory node. It appears like copy motion can only find the pose history node if it lives within the same anim instance and only go as far back in time as: “PoseCount” * “SamplingInterval” would allow (provided you are caching the right bones in the PoseHistory node).

With that out of the way, on copy motion I gathered that:

-SourceBone: Is the bone from which you want to extract the motion, typically the primary arm hand joint.

-BoneToModify: The bone that will actually receive the delta motion calculated by copy motion and that will ultimately be used to IK the source bone back to. Typically some sort of weapon bone or virtual bone in the hierarchy.

-CopySpace: The bone space in which we want to capture the transform of the source bone. Typically the chest (spine_05) to ensure the motion of the hands is captured faithfully regardless of if the character is twisting.

-Apply Space: The bone space in which we want to reapply the delta motion to the bone to modify. I imagine it is desirable to keep the apply space close to the pelvis so that the delta motion from the source bone is applied relative to the pelvis (kind of in body space).

Ultimately, it seems like the node is capturing a delta offset from the source bone in a specific space and then simply reapplying that delta offset as an additive in a different specific space, is that right?

With that assumption, translation scale and rotation scale make a lot of sense because some times those deltas might be a little too much some times. I imagine is important to consider here that the translation scale will be expressed in “apply space”.

I find a little harder to grasp the concept of TranslationOffset, RotationOffset and RotationPivot. Specially the offsets since we are talking about deltas, will they not increase by a fixed amount the size of the delta?

Lastly, to get best results I imagine there are some “good practice” guidelines as to how you should author the animations. I’m guessing it is fairly crucial to have a relatively neutral spine in the strafe set?

Thanks in advance,

Fran.

Hi Fran,

Sorry it’s taken a while to get back to you on this. We’ve been working through a backlog after the Epic summer break, and I also wanted to confirm the following information with the dev team before following up which slowed things down.

The basic idea with the Copy Motion node is that we want to use it to re-introduce the ‘arm pumping’ motion from the base motion matched locomotion animation. We want to do that because, prior to the Copy Motion node, we’ve layered on an upper body weapon pose onto the motion matched locomotion via the custom Bone Mask node (or a series of Layered Blend Per Bone nodes). That gets us most of the way to the weapon pose that we want, but it removes a lot of the arm-pumping motion. We’re left with the middle pose in this screenshot:

[Image Removed]Now we want to get the arm-pumping motion back onto this pose. That’s what the Copy Motion node allows us to do. It calculates the offset between the source bone (usually the r-hand IK bone) in the Base Pose (usually the locomotion pose generated by motion matching) and the source Base Pose Reference (a static idle pose). Now we effectively have a delta transform that represents the hand-pumping motion from the motion matched locomotion which we can apply (via Leg IK) back onto the arms to reintroduce the arm-pumping.

The other important thing that the node allows us to do is to delay the transform that’s being used to generate the delta by accessing the source transform via the Pose History. The reason for doing this is to allow us to give a sense of weight to some of the weapons through this delay.

There are various properties that then allow you to tweak the motion depending on your requirements:

  • TranslationOffset = a vector added to the delta transform to move it (usually this is zero for most weapons)
  • RotationOffset = a rotation added to the delta transform to reorient it (also usually zero for most weapons)
  • RotationPivot = When using a single copy motion node for a double-handed weapon, you effectively have a pivot point rooted at the right hand IK bone, which the left hand IK bone then moves relative to. But for some weapons, like the Fortnite pickaxe, we want to move that effective ‘pivot point’ so that it is instead, somewhere along the axis between the right and left hand. Both hands then move relative to this, meaning that we effectively increase the movement on the right hand and decrease it on the left hand.
  • TranslationScale = The full delta transform is usually too large when applied onto the upper body weapon pose. So we use this value to scale it down (often 0.1 or 0.2)
  • RotationScale = similar to the above, we usually want to scale the rotational motion down (often 0.1 or 0.2)
  • TargetCurveName = a curve that is used to store a rotational twist value that can be applied to the elbow (via the LegIK node) to exaggerate the rotation of the elbow around the shoulder -> hand axis. Effectively adds more arm-pumping motion to the elbow.
  • TargetCurveComponent = which component of the delta transform to extract to base the elbow twist on (usually Rotation Angle)
  • TargetCurveScale = a scale value to apply to the elbow twist rotation

I also wanted to mention that really the only effective way to work out what values work for your setup is to test live in PIE. This is how our artists work for each weapon. They test by plugging variables directly into these properties and then modifying those values while PIE is active. Once they’re happy with the behaviour, they save these values out into data assets which we use in production to drive the node properties.

There are some specific workflows in Fortnite that allow this kind of live testing:

  • We are able to animate in place in PIE by preventing the CMC from moving. We do this based on a cvar and some code that are specific to Fortnite. The code is just an override to UFortMovementComp_Character::PhysWalking, which prevents the CMC from moving:

void UFortMovementComp_Character::PhysWalking(float deltaTime, int32 Iterations) { #if !UE_BUILD_SHIPPING if (FortConsoleVariables::EnableMoveInPlace && !IsFalling()) { // Skip phys walking and just compute the velocity for animation when we want to stay in place CalcVelocity(deltaTime, GroundFriction, false, GetMaxBrakingDeceleration()); return; } #endif Super::PhysWalking(deltaTime, Iterations); }* With the CMC locked in place we then simulate a keyboard input via a command which means the character will animate (eg. locomote forward) but the CMC won’t move. To simulate pressing the W key, we would enter ‘input.+ w’. I think this should work in a vanilla version of the engine

  • With the character locomoting in place you can then turn on the debug draw on the copy motion node via a.AnimNode.CopyMotion.Debug to see where the delta transform is
  • While still in PIE you can then eject from the character and start to tweak those variables within the anim graph and see them update directly (setting ‘Slate.bAllowThrottling 0’ helps with this as it allows PIE to update as you change variables in the anim bp editor - otherwise you have to release the mouse before PIE updates).

If you have all of this setup, you can debug your setup a little like this:

[Image Removed]

Hi Euan,

Thank you so much for the thorough explanation, this is super helpful!

I have a handful of extra questions if you don’t mind.

- When dealing with ADS, are you using full body poses that are much more stable on the spine? and is it preferable to scale down certain axis on the hands to keep the weapon more stable?

- When dealing with situations where you are dual wielding or handling two different items, are you using two different copy motio nodes?

- When handling one-handed items like a gun, what is your strategy to passthrough the secondary arm from locomotion? do you just exclude the left arm on the layered blend per bone to inherit the full arm motion from motion matching? or do you tame that motion with some strategy?

Best,

Fran.

No problem, good to hear the info was useful.

> When dealing with ADS, are you using full body poses that are much more stable on the spine? and is it preferable to scale down certain axis on the hands to keep the weapon more stable?

Can you give me a bit more context on this, are you talking about having the ADS animation as the base motion matched locomotion? We don’t do that, our base locomotion is always just walk/jog/run full body with no form of aiming. It’s the same for the idle that we generate the delta transform against, that’s just a standard single-frame idle pose.

> When dealing with situations where you are dual wielding or handling two different items, are you using two different copy motion nodes?

Yeah, we have one node setup to operate on IK_Gun and another other on IK_Hand_L

> When handling one-handed items like a gun, what is your strategy to passthrough the secondary arm from locomotion? do you just exclude the left arm on the layered blend per bone to inherit the full arm motion from motion matching? or do you tame that motion with some strategy?

We just don’t run LegIK on the left arm in that situation. You could also alpha out the Copy Motion node that’s running on the left hand. Or have different branches with the different arm setups and blend between them

Excelent, that’s really useful!

Yeah for ADS I was referring exactly about having specific fullbody strafes that are more stable.

There is an example in the GDC talk where one of the characters is carrying a shield and aiming with a gun. When the camera zooms in for ADS any motion on the hands will be very noticeable, that’s why I asked if:

  1. You used any specific ADS full body animations.
  2. You scaled the delta offset more in a certain axis to avoid the hand moving on the horizontal plane too much.

It sounds like for as long as the spine is not transferring a lot of motion the hands ought to stay in the right place with the motion transferred from copy motion.

Yeah, since the weapon poses are just static, when we layer them ontop of the full body locomotion (via the bone mask/layered blend per bones) we lose a lot of the spine motion. One thing with aiming poses however, is that we tend to blend those in using mesh space blends on the layered blend per bones to keep the aiming more precise. Let me know if you’ve got any more questions on this.