In Unreal Engine 5.2, significant changes have been made to the way a Control Rig FRigVMStruct
and FRigUnit
are implemented in your project's C++ code base. The terminology and the extended use of the notion of a Context inside of custom FRigVMStruct
structures have required these structs to be refactored.
You can use this document to learn more about migrating your Control Rig code to be compatible in Unreal Engine 5.2.
Execute signature changes
In previous versions of Unreal Engine, FRigVMExecuteContext
members were treated as parameters to each RigVM method. In Unreal Engine 5.2 the execute context members are no longer passed. Instead a single parameter called ExecuteContext
is passed to any RigVM method. The parameter will be a Constant (const) or Mutable depending on its use in the structure.
To see changes related to the FRigUnitContext
, see the Opaque Arguments and FRigUnitContext section.
Here you can reference an example signature change from Unreal Engine 5.1 to 5.2, where a FRigUnit
such as FRigUnit_SendEvent
is the signature for FRigUnit_SendEvent::Execute
:
5.1 Signature | 5.2 Signature |
---|---|
|
|
Since the new ExecuteContext
parameter provides access to a const or mutable context already we've removed the RigVMExecuteContext
parameter.
Opaque Arguments and FRigUnitContext
Formerly domain specific RIGVM_METHOD
implementations had the option of passing additional opaque arguments. In Unreal Engine 5.2, the support for opaque arguments in 5.2 has been removed. The only former usage of this mechanism is the FRigUnitContext
for Control Rig nodes. Code which relies on accessing the FRigUnitContext
Context parameter must be updated to use the ExecuteContext.UnitContext
to ensure compatibility.
Members moved from FRigUnitContext to FRigExecuteContext
Here you can reference a list of the members that have moved from the FRigUnitContext
to the FRigExecuteContext
:
FRigUnitContext::DrawInterface
| FRigVMExecuteContext::GetDrawInterface();
|
| FRigUnitContext::DrawContainer
| FRigVMExecuteContext::GetDrawContainer();
|
| FRigUnitContext::DeltaTime
| FRigVMExecuteContext::GetDeltaTime();
|
| FRigUnitContext::AbsoluteTime
| FRigVMExecuteContext::GetAbsoluteTime();
|
| FRigUnitContext::FramesPerSecond |
FRigVMExecuteContext::GetFramesPerSecond();` |
FRigUnitContext::Hierarchy |
FControlRigExecuteContext::Hierarchy; |
FRigUnitContext::ToWorldSpaceTransform |
FRigUnitContext::GetToWorldSpaceTransform(); |
FRigUnitContext::OwningComponent |
FRigUnitContext::GetOwningComponent(); |
FRigUnitContext::OwningActor |
FRigUnitContext::GetOwningActor(); |
FRigUnitContext::World |
FRigUnitContext::GetWorld(); |
FRigUnitContext::Log |
FRigVMExecuteContext::GetLog(); |
FRigUnitContext::NameCache |
FRigVMExecuteContext::GetNameCache(); |
All transform conversion related helper functions also have moved to the FRigVMExecuteContext
.
FRigUnitContext.State Removal
Control Rig and RigVM no longer calls into RIGVM_METHOD callbacks multiple times with the Init or Update state. Instead, it resets the memory to the default during Init, and then always runs what used to be considered the Update state. This means that you may have to change your code from something that used to rely on the Init state for initialization to something which uses an uninitialized flag or a similar method to determine if initialization is necessary.
Example
Here you can reference an example of the refactored code from the Unreal Engine source tree. You can also use the Unreal Engine GitHub source tree to compare additional refactors, such as with Control Rig nodes, between releases.
5.1 FRigUnit_SendEvent::Execute() | 5.2 FRigUnit_SendEvent::Execute() |
---|---|
~~~ FRigUnit_SendEvent_Execute() { DECLARE_SCOPE_HIERARCHICAL_COUNTER_RIGUNIT() if(!bEnable) { return; } if (bOnlyDuringInteraction && !Context.IsInteracting()) { return; } URigHierarchy Hierarchy = ExecuteContext.Hierarchy; if (Hierarchy) { switch (Context.State) { case EControlRigState::Init: { break; } case EControlRigState::Update: { FRigEventContext EventContext; EventContext.Key = Item; EventContext.Event = Event; EventContext.SourceEventName = ExecuteContext.GetEventName(); EventContext.LocalTime = Context.AbsoluteTime + OffsetInSeconds; Hierarchy->SendEvent(EventContext, false / async */); break; } default: { break; } } } }
FRigUnit_SendEvent_Execute() { DECLARE_SCOPE_HIERARCHICAL_COUNTER_RIGUNIT() if(!bEnable) { return; } if (bOnlyDuringInteraction && !ExecuteContext.UnitContext.IsInteracting()) { return; } URigHierarchy Hierarchy = ExecuteContext.Hierarchy; if (Hierarchy) { FRigEventContext EventContext; EventContext.Key = Item; EventContext.Event = Event; EventContext.SourceEventName = ExecuteContext.GetEventName(); EventContext.LocalTime = ExecuteContext.GetAbsoluteTime() + OffsetInSeconds; Hierarchy->SendEvent(EventContext, false / async */); } } ~~~ |