๐Ÿ’ปExtending the System

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

Overview

A procedural animation feature consists of 2 entities:

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

  • IAnimationLayerJob: a struct that executes custom animation logic.

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

AdditiveLayer example.

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

AdditiveLayerSettings.cs
public class AdditiveLayerSettings : WeaponLayerSettings
{
    ...
    public override IAnimationLayerJob CreateAnimationJob()
    {
        return new AdditiveLayerJob();
    }
}

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

Example

First, create a new FPSAnimatorLayerSettings-derived class for data, and IAnimationLayerJob-implemented struct:

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

    public override IAnimationLayerJob CreateAnimationJob()
    {
        return new YourAnimationLayerJob();
    }
    
#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
}
YourAnimationLayerJob.cs
public struct YourAnimationLayerJob
{
    private YourFeatureLayerSettings _settings;
    private LayerJobData _jobData;
    private TransformStreamHandle _yourBoneHandle;
        
    // Use this method for custom animation logic.
    public void ProcessAnimation(AnimationStream stream)
    {
    }
    
    // Use this for modifying root motion.
    public void ProcessRootMotion(AnimationStream stream)
    {
    }
    
    // This method is called when a new profile is linked.
    public void Initialize(LayerJobData newJobData, FPSAnimatorLayerSettings settings)
    {
        _settings = (AdditiveLayerSettings) settings;
        _jobData = newJobData;
        
        Transform bone = newJobData.rigComponent.GetRigTransform(settings.myRigElement);
        _yourBoneHandle = newJobData.animator.BindStreamTransform(bone);
    }
    
    public AnimationScriptPlayable CreatePlayable(PlayableGraph graph)
    {
        return AnimationScriptPlayable.Create(graph, this);
    }
    
    // Make sure to return a reference to the active asset.
    // It will be used by the FPSBoneController to compute a layer weight.
    public FPSAnimatorLayerSettings GetSettingAsset()
    {
        return _settings;
    }
    
    // Use this method to update job data when a layer is linked.
    public void OnLayerLinked(FPSAnimatorLayerSettings newSettings)
    {
    }
    
    // Use this method when a new weapon or item is equipped.
    public void UpdateEntity(FPSAnimatorEntity newEntity)
    {
    }
    
    // Use this method before the main update to gather input data.
    public void OnPreGameThreadUpdate()
    {
    }
    
    // Use this method to update playable data and gather game thread data.
    public void UpdatePlayableJobData(AnimationScriptPlayable playable, float weight)
    {
        _jobData.weight = weight;
        playable.SetJobData(this);
    }
    
    // Use this method when a finalized pose is required.
    public void LateUpdate()
    {
    }
    
    // Use this method to dispose data manually.
    public void Destroy()
    {
    }
}

In the next chapter, we will learn how to work with the demo project.

Last updated