Altering Animation Results
- CPU Animator
- GPU Animator
AnimationProcessSystem samples and blends CPU bone poses. AnimationApplicationSystem then applies them to bone entities and skinning data. Systems in RukhankaAnimationInjectionSystemGroup run between these phases and can modify the calculated pose.
Bone Data Modification System
Use AnimationStream.Create(runtimeData, rigDefinition) to access a rig's frame data. Its GetLocal* and SetLocal* methods work relative to the parent bone. Its GetWorld* and SetWorld* methods work in the rig's accumulated bone space, not Unity scene world space. Convert a scene-space target into rig space before passing it to the stream.
The example below overrides one bone position per rig. Add BonePositionOverrideComponent to the rig entity, set boneIndex to an index in its baked RigDefinitionBlob.bones array, and provide a rig-space position. This example uses a main-thread loop for clarity; it completes previous animation jobs before accessing shared frame buffers.
using Rukhanka;
using Unity.Entities;
using Unity.Mathematics;
public struct BonePositionOverrideComponent : IComponentData
{
public int boneIndex;
public float3 rigSpacePosition;
}
[WorldSystemFilter(WorldSystemFilterFlags.LocalSimulation | WorldSystemFilterFlags.ClientSimulation)]
[UpdateInGroup(typeof(RukhankaAnimationInjectionSystemGroup))]
public partial struct SimpleBonePositionOverride : ISystem
{
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<RuntimeAnimationData>();
}
public void OnUpdate(ref SystemState state)
{
state.Dependency.Complete();
var runtimeData = SystemAPI.GetSingletonRW<RuntimeAnimationData>().ValueRW;
foreach (var (rig, position) in SystemAPI.Query<
RefRO<RigDefinitionComponent>, RefRO<BonePositionOverrideComponent>>()
.WithDisabled<GPUAnimationEngineTag>()
.WithNone<CullAnimationsTag>()
.WithAll<AnimationToProcessComponent>())
{
var rigDefinition = rig.ValueRO;
var boneIndex = position.ValueRO.boneIndex;
if (!rigDefinition.rigBlob.IsCreated || boneIndex < 0
|| boneIndex >= rigDefinition.rigBlob.Value.bones.Length)
continue;
using var stream = AnimationStream.Create(runtimeData, rigDefinition);
stream.SetWorldPosition(boneIndex, position.ValueRO.rigSpacePosition);
}
}
}
The query excludes GPU and currently culled rigs and respects the enabled state of RigDefinitionComponent. CPU frame data is transient: access it only after CPU pose calculation, and never retain a stream or frame offset across updates. Do not create a stream for a rig that has no valid CPU pose in the current frame.
AnimationStream rebuilds dependent bone poses lazily. Always dispose it after use (using does this automatically), so remaining dirty child poses are rebuilt before animation application.
For a job-based implementation, follow the built-in IK systems: pass the current RuntimeAnimationData, preserve job dependencies, and ensure jobs cannot write the same rig's poses concurrently. Add explicit UpdateBefore or UpdateAfter constraints inside the injection group when your modifications depend on another solver's output.
CPU code cannot modify GPU Animator bone poses through AnimationStream.