Manual
Animator View Updater
The QuantumEntityViewUpdater
script is responsible for triggering the Animate
callback on each entity with an AnimatorComponent
attached. The entity must have a QuantumEntityViewComponent
that implements the IAnimatorEntityViewComponent
interface. The Animator Addon includes the AnimatorMecanim
and AnimatorPlayables
, which can be used as-is or extended for a more customized workflow.
Animator Mecanim
Provides integration between Quantum Animator and Unity's Mecanim system, allowing for smooth transitions and frame synchronization of animations. It provides frame-rate control and enables the usage of Animation Layers, which is not supported by AnimatorPlayables
.

To prevent transitions from occuring automatically in Unity, make sure to check Mute Graph Transitions On Export
in the Animator Graph
settings.
Animator Playables
It handles animations using Playable Graphs instead of Mecanim, providing a more customizable approach to animation updates with the Quantum Animator. However, not all Mecanim features are supported in this setup, such as Animation Layers.
Animator Events
Quantum Animator supports both Instant
events and Time-Window
events. The event callback is triggered when the specified clip is played at the configured moment during event creation. This means two states that make use of the same clip will share events.
Instant Event
C#
public abstract class AnimatorInstantEventAsset : AnimatorEventAsset, IAnimatorEventAsset
{
/// <inheritdoc cref="AnimatorEventAsset.OnBake"/>
public new AnimatorEvent OnBake(AnimationClip unityAnimationClip, AnimationEvent unityAnimationEvent)
{
var quantumAnimatorEvent = new AnimatorInstantEvent();
quantumAnimatorEvent.AssetRef = Guid;
quantumAnimatorEvent.Time = FP.FromFloat_UNSAFE(unityAnimationEvent.time);
return quantumAnimatorEvent;
}
}
Instant Events will trigger only one time the Execute()
function once the Animator plays the clip that contains such event baked. A good pattern is to inherit from the base AnimatorInstantEventAsset
and override the base class methods with different procedures, like custom Frame.Siganls
.
Events assets can be created using Create->Quantum->Assets...

Time-Window Event
The procedure is similar to instant event creation, but in this case, you must create a second event of the same type in the Unity Clip to mark the end of the event execution. For this event type, the Execute callback is triggered every frame between the start and end frames defined in the Unity Clip.

In case you want to create new events that have a similar behavior with OnEnter()
, Execute()
and OnExit()
methods, by inherit the AnimatorTimeWindowEventAsset
class:
C#
/// <summary>
/// This is a sample of how to use SampleTimeWindowEvent events. Use it as a base to create a new class inheriting from AnimatorInstantEventAsset and
/// implement a custom logic on Execute method
/// </summary>
[Serializable]
public class ExampleTimeWindowEventAsset : AnimatorTimeWindowEventAsset
{
public override unsafe void OnEnter(Frame frame, AnimatorComponent* animatorComponent, LayerData* layerData)
{
Debug.Log($"[Quantum Animator ({frame.Number})] OnEnter animator time window event.");
}
public override unsafe void Execute(Frame frame, AnimatorComponent* animatorComponent, LayerData* layerData)
{
Debug.Log($"[Quantum Animator ({frame.Number})] Execute animator time window event.");
}
public override unsafe void OnExit(Frame frame, AnimatorComponent* animatorComponent, LayerData* layerData)
{
Debug.Log($"[Quantum Animator ({frame.Number})] OnExit animator time window event.");
}
}
Event Setup
Follow these steps to define the event on an animation clip:
- Add the
AnimationEventData
component to the same GameObject the containsAnimator
component, in yourQuantumEntityPrototype
:

- On the Animation window select the clip and add a new
Unity AnimationEvent
:

- Select the created event and specify the
Function
andObject
as the image below:

Animator State Signals
Animator State Signals are now called when changing state both through transitions as well as FadeTo
. These are implemented by the AnimatorBehaviourSystem
and can be implemented by other systems as well.
C#
// Called in the first update when the Animator begins entering a state.
void OnAnimatorStateEnter(Frame frame, EntityRef entity, AnimatorComponent* animator, AnimatorGraph graph, LayerData* layerData, AnimatorState state, FP time);
// Called every frame while the Animator is updating a state.
void OnAnimatorStateUpdate(Frame frame, EntityRef entity, AnimatorComponent* animator, AnimatorGraph graph, LayerData* layerData, AnimatorState state, FP time, AnimatorStateType stateType);
// Called when the Animator updates for the last time, before fading to another state.
void OnAnimatorStateExit(Frame frame, EntityRef entity, AnimatorComponent* animator, AnimatorGraph graph, LayerData* layerData, AnimatorState state, FP time);
These signals provide the following:
Frame
: the currentFrame
that the signal was calledEntityRef
: theEntityRef
associated with theAnimatorComponent
provided to the signal- A pointer to the
AnimatorComponent
- References to the current
AnimatorGraph
andAnimatorState
- A pointer to the
LayerData
- The current, corresponding time of the
LayerData
when the signal was called
OnAnimatorStateUpdate
also provides the AnimatorStateType
, an enum
with the following:
None
FromState
CurrentState
ToState
During transitions, both the ToState
and FromState
send OnAnimatorStateUpdate
signals, so AnimatorStateType
is provided to help distinguish which one is being updated. For example, this could be used to prevent AnimatorStateBehaviours
from executing their updates while being transitioned out of.
Animator State Behaviours
In contrast to Animator Events, Animator State Behaviour
can be independent between states using the same clip and have support for OnStateEnter
, OnStateUpdate
and OnStateExit
.
Setup
1- In the Unity Animator window, select the state where you want to add the behavior, then attach the AnimatorStateBehaviourHolder
script to it.

2- Add a new element to the AnimatorStateBehaviourAssets
list by using the DebugBehavior
example included with the Animator package.

3- Bake the graph asset using the menu option: Tool->Quantum Animator->Bake All Graph Assets
.
4- Add the AnimatorBehaviourSystem
to Default Config Systems
asset.
5- The debug message should be logged every time the state is played.
AnimatorStateBehaviour
is abstract, so classes that inherit from it, must implement the following methods:
C#
bool OnStateEnter(Frame frame, EntityRef entity, AnimatorComponent* animator, AnimatorGraph graph, LayerData* layerData, AnimatorState state, FP time);
bool OnStateUpdate(Frame frame, EntityRef entity, AnimatorComponent* animator, AnimatorGraph graph, LayerData* layerData, AnimatorState state, FP time, AnimatorStateType stateType);
bool OnStateExit(Frame frame, EntityRef entity, AnimatorComponent* animator, AnimatorGraph graph, LayerData* layerData, AnimatorState state, FP time);
These are nearly identical to the Animator State Signals mentioned previously; however, they return a bool
. If using the provided AnimatorBehaviourSystem
, returning true
will stop the system from checking the remaining AnimatorStateBehaviours
that may belong to the AnimatorState
. For example, if an AnimatorStateBehaviour
transitions to a new state using FadeTo
, returning true
will prevent the remaining AnimatorStateBehaviours
from executing their OnAnimatorStateUpdate
calls.
FadeTo
The FadeTo can be used to transition to another state independent of a condition. Enable AllowFadeToTransitions
to enable this feature.
C#
// public void FadeTo(Frame frame, AnimatorComponent* animatorComponent, string stateName, bool setIgnoreTransitions, FP deltaTime)
//usage example
if (input->Run.WasPressed)
{
graph.FadeTo(frame, filter.AnimatorComponent, "Running", true, 0);
}
Root Motion
The Quantum Animator uses the the Unity's AnimationClip
information to bake the motion curves data into the AnimatorGraph
asset. The baked information can later be used in the simulation to modify the entity position and rotation according to the currently playing clip.

How to use
The Root Motion
option needs to be enabled in order to allow the motion data to be processed. It is also important to setup correctally the root motion configuration in the Animation
tab of your source model, more information on Unity's Root Motion documentation.

Disable State Root Motion
By default, the motion data is enabled for all states in the AnimatorGraph
as long s the Root Motion
option is enabled. Adding the Animator Disable Root Motion Behaviour
to a Mecanim state ensures that the state is excluded from root motion calculations.

Usage Example
The QuantumAnimation is only responsible for computing the current motion delta for each Quantum Frame, allowing the user to use the generated data to update the entity's transform as needed. The code below shows an example application of OnAnimatorRootMotion3D
and OnAnimatorRootMotion2D
.
C#
// Handles 3D root motion
public void OnAnimatorRootMotion3D(Frame frame, EntityRef entity, AnimatorFrame deltaFrame, AnimatorFrame currentFrame)
{
//Return in case there is no motion delta
if (deltaFrame.Position == FPVector3.Zero && deltaFrame.RotationY == FP._0) return;
if (frame.Unsafe.TryGetPointer<Transform3D>(entity, out var transform))
{
// Create a quaternion representing the inverse of the current frame's Y-axis rotation
var currentFrameRotation = FPQuaternion.CreateFromYawPitchRoll(currentFrame.RotationY, 0, 0);
currentFrameRotation = FPQuaternion.Inverse(currentFrameRotation);
// Rotate the delta position by the inverse current rotation to align movement
var newPosition = currentFrameRotation * deltaFrame.Position;
// Apply the transform's rotation to the new position to get the world displacement
var displacement = transform->Rotation * newPosition;
var kccSettings = frame.FindAsset<KCCSettings>(frame.Unsafe.GetPointer<KCC>(entity)->Settings);
// Compute an adjusted target hit position for raycasting
var targetHitPosition =(displacement.XOZ.Normalized * FP._0_33 * 2 ) + displacement;
// Perform a raycast in the direction of the intended motion to detect potential collisions with statics
var hits = frame.Physics3D.RaycastAll(transform->Position, targetHitPosition.XOZ, targetHitPosition.Magnitude, -1,
QueryOptions.HitStatics);
if (hits.Count <= 0)
{
// If no collision, disable the character controller temporarily
if (frame.Unsafe.TryGetPointer<KCC>(entity, out var kcc))
{
kcc->SetActive(false);
}
// Apply the motion and rotation to the transform
transform->Position += displacement;
transform->Rotate(FPVector3.Up, deltaFrame.RotationY * FP.Rad2Deg);
}
else
{
// If there is collision, enable the character controller
if (frame.Unsafe.TryGetPointer<KCC>(entity, out var kcc))
{
kcc->SetActive(true);
}
}
}
}
public void OnAnimatorRootMotion2D(Frame frame, EntityRef entity, AnimatorFrame deltaFrame, AnimatorFrame currentFrame)
{
//Return in case there is no motion delta
if (deltaFrame.Position == FPVector3.Zero && deltaFrame.RotationY == FP._0) return;
if (frame.Unsafe.TryGetPointer<Transform2D>(entity, out var transform))
{
// Calculate new rotation by applying delta
FP newRotation = transform->Rotation + deltaFrame.RotationY;
// Normalize rotation to keep it within [-π, π]
while (newRotation < -FP.Pi) newRotation += FP.PiTimes2;
while (newRotation > FP.Pi) newRotation += -FP.PiTimes2;
// Rotate delta movement vector based on new orientation
var deltaMovement = FPVector2.Rotate(deltaFrame.Position.XZ, newRotation);
// Apply movement and rotation to the transform
transform->Position += deltaMovement;
transform->Rotation = newRotation;
}
}
Back to top