[Workaround] Modular Rig — Fix unpredictable translation axes in Sequencer Curve Editor after rotation

When adding a module to a Modular Rig (e.g. the Arm module), Unreal generates a set of controls automatically — for example hand_ctrl and arm_pv_ik_ctrl. These module-generated controls expose a Parent Control connection in the Connections panel, which defines the reference space used for translation values in the Sequencer Curve Editor.

Additionally, the Parent Control connection cannot reference a control generated by the same module — you cannot assign hand_ctrl as its own parent.

The issue: once you keyframe a rotation on one of these controls, its translation axes (X/Y/Z) in the Curve Editor become unpredictable. They don’t follow the control’s new local orientation — they stay frozen in the parent space captured at rig creation time. This makes curve-based animation for combined rot+trans movements (e.g. rotating and repositioning an IK hand) unreliable without resorting to high frame rates and dense keyframe timelines.

In this case, it’s probably best to create a specific control that will act as this modules parent to keep track of the rotation and alleviate issues in the Sequencer Curves.

To do so:

Right click on the control you want to parent in the Rig Hierarchy (in this case hand_r).

Add a new control and name it something like hand_r_copy.

This will create a new control in the rig hierarchy with no parent, but with the Transform of hand_r:

You can now go into the newly created control and change its shape to be just like the original hand_ctrl:


You will know want to setup the module details to update the parent and update the visuals of the original hand_ctrl.

Hide hand_ctrl in the details panel by unchecking selectable.

I have made this small clip to show the difference between the way the curve sequencer behaves with and without this:

You can see that in the case of the left hand (blue) without the fix, if I apply a rotation, then the curve sequencer basically becomes unusable to do small modifications as it does not follow the current rotation on hand_l_ctrl.
Whereas the right hand gets updated with the rotation properly and follows the local axis.

Oh also as note, I think you have to delete and re-add the control rig to your skeletal mesh in the Sequencer for the effect to be taken into account.

Side side note, I tested the curve sequencer without specifying any parents for the module and it behaves the same as setting the parent to root or body_ctrl→ incoherent behaviour in the curve sequencer.


I wanted to confirm that leaving the parent empty didn’t magically fixed the reference axis.

Adding details to how to hide the original control, it seems to me that unchecking the “Selectable” on the IK shape does not hide it. (Although you should still mark it as Not Selectable because we want to make changes to the new parent bone not the child).

I tried instead unchecking “visible”, but I can still see the control in my Level Sequencer.
The best workaround was to scale down the orignal shape to 0,0,0.

Apologies — after further testing, the workaround in this post does not actually work. During my initial testing and in the linked video, I inadvertently rotated the copied control back to align with the base axes, which made it appear fixed. It wasn’t.

Root cause (traced through UE 5.7 source)

The issue could be in SetupControlFromGlobalTransform() (ControlRig.cpp). When a module connects to a parent control, the OffsetStorage (the control’s local reference frame) is computed once during the Construction Event using the parent’s transform at that moment. It is never updated at runtime.

The Sequencer Curve Editor stores Translation X/Y/Z as values relative to this frozen offset. When the parent rotates in Sequencer, the offset doesn’t follow — so the translation axes stay locked to the parent’s orientation at rig creation time, not its current orientation. This breaks the standard definition of Parent Space animation.

Adding an intermediate “copy” parent control doesn’t help — its own offset freezes the same way, one level up.

1 Like