💻Extending the System

In this section you will learn how to create custom animation modules.

Overview

Animator Layer represents 2 classes:

  • FPSAnimatorLayerSettings: a Scriptable Object that contains all the data.

  • FPSAnimatorLayerState: a class that contains all the animation logic.

If you open up the source code of the framework, you will notice that every layer has its own folder with 2 classes:

FPSAnimatorLayerSettings class is responsible not just for containing the data, but for instantiating the FPSAnimatorLayerState as well:

AdditiveLayerSettings.cs
public class AdditiveLayerSettings : WeaponLayerSettings
{
    ...
    public override FPSAnimatorLayerState CreateState()
    {
        return new AdditiveLayerState();
    }
}

CreateState() method must return a new instance of the desired FPSAnimatorLayerState-type. When you are linking a new Animator Profile, the system iterates over all animation features (Layer Settings) and then invokes the CreateState() method to create the Layer State.

Now let's actually create a custom animator layer.

Example

First, create new FPSAnimatorLayerSettings- and FPSAnimatorLayer-derived classes:

YourFeatureLayerSettings.cs
public class YourFeatureLayerSettings : FPSAnimatorLayerSettings
{
    //Define your settings here, including the KRigElements.
    public KRigElement myRigElement;

    public override YourFeatureLayerState CreateState()
    {
        return new YourFeatureLayerState();
    }
    
#if UNITY_EDITOR
    public override void OnRigUpdated()
    {
        base.OnRigUpdated();
        
        // (!) Always update KRigElements here.
        // Called when a rig asset is updated/changed.
        UpdateRigElement(ref myRigElement);
    }
#endif
}
YourFeatureLayerState.cs
public class AdditiveLayerState : FPSAnimatorLayerState
{
    private YourFeatureLayerSettings _settings;
    
    public override void InitializeState(FPSAnimatorLayerSettings newSettings)
    {
        base.InitializeState(newSettings);
        _settings = (YourFeatureLayerSettings) newSettings;
    }
    
    public override void OnEntityUpdated(FPSAnimatorEntity newEntity)
    {
        // Called when a weapon/item is equipped.
        // Use this callback for weapon/item-related features.
    }
    
    public override void OnGameThreadUpdate()
    {
        // Use this to collect general, game-thread data.
        // It is safe to access character references here.
        // Called on Update() before the main animation pass.
    }
    
    public override void OnEvaluatePose()
    {
        // Apply procedural features here.
    }
    
    public override void OnPostEvaluatePose()
    {
        // Called when all layers are applied.
    }
}

If you want to leverage features like blending or dynamic layer linking, make sure to implement these methods:

YourFeatureLayerState.cs
public class AdditiveLayerState : FPSAnimatorLayerState
{
    ...
    
    // Use this callback to re-initialize values.
    // It is only called when the Link Dynamically is true.
    public override void OnLayerLinked(FPSAnimatorLayerSettings newSettings)
    {
        _settings = (YourFeatureLayerSettings) newSettings;
    }
    
    // Make sure to manually register all the bones affected by this state.
    // This is required for smooth blending and pose caching.
    public override void RegisterBones(ref HashSet<int> registeredBones)
    {
        registeredBones.Add(_settings.myRigElement);
    }
    
    // Make sure to cache the poses for active bones.
    public override void CachePoses(ref List<KPose> cachedPoses)
    {
        cachedPoses.Add(new KPose()
        {
            element = _settings.myRigElement,
            modifyMode = EModifyMode.Add,
            pose = KTransform.Identity,
            space = ESpaceType.ComponentSpace
        });
        
        // Add other poses here.
    }
}

Last updated