Skip to main content

Changing AnimatorState’s AnimationClips at Runtime (Unity)

✍ Last Updated : August 22, 2022

🚪 Prequisite Knowledge (Optional)

  • How to use Unity
  • How to use Unity Animator Graph

❓ Key Question / Problem / Issue

Finding the best solution to handle multiple animators with multiple animations

✅ Expected Output/Definition of Done

Define what information you really need and the definition of done

🎁 Resulting Solution

Since a character can have multiple animationClip for each action (Example, Sword Attack 1, Sword Attack 2, Sword Attack 3) , the animation system need to be able to change animation clips at runtime, or the Animator graph will become unnecessarily complicated. Here’s an example if each sword attack has their own AnimationStatemedia

![animatorA.png](Changing AnimatorState%E2%80%99s AnimationClips at Runtime ef40e35650b7427fa2bdf6e060e25374/animatorA.png)

The basic setup for the animator would be

![animatorB.png](Changing AnimatorState%E2%80%99s AnimationClips at Runtime ef40e35650b7427fa2bdf6e060e25374/animatorB.png)

Movement would be a 1(or 2) dimensional BlendTree of three clips : Idle, Walk, and Run. With speed as the blend parameter.

AnimationStates should have replaceable animation clip to keep the Animator graph simple

Hit and Death animation can be played from any State, and can interrupt any animation except for Death.

Defining the Weapon

The bare minimum class for weapon would be a Type, which is an enumeration defining the type of weapon (Sword, Bow, Axe etc) and a boolean which define in which hand it’s held.

Weapon is a separated prefab and instantiated and attached to the corresponding hand transform, based on rightHandEquipped boolean when equipped.

using UnityEngine;

public class Weapon : MonoBehaviour
{
    public enum Type{SWORD, AXE, BOW, GUN, STAFF, TOME};

    [SerializeField] Type type;
    [Tooltip("Is the weapon held in right hand?")]
    [SerializeField] bool rightHandEquipped = true;

    public Type weaponType => type;
    public bool righthand  => rightHandEquipped;
		//This can be where to add stats to the weapon, like damage, price etc

    // Start is called before the first frame update
    void Start()
    {
        
    }
}

Helper class

The AnimationClipOverrides class is a helper class that’s basically a List of AnimationClip Pairs to get/store the override clips to be applied into animatorOverrideController. Taken from

AnimatorOverrideController

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AnimationClipOverrides : List<KeyValuePair<AnimationClip, AnimationClip>>
{
	public AnimationClipOverrides(int capacity) : base(capacity) {}
	
	public AnimationClip this[string name]
	{
		get { return this.Find(x => x.Key.name.Equals(name)).Value; }
		set
		{
			int index = this.FindIndex(x => x.Key.name.Equals(name));
			if (index != -1)
				this[index] = new KeyValuePair<AnimationClip, AnimationClip>(this[index].Key, value);
		}
	}
}

Weapon Animation Data

This class is to hold the animationOverrideController, animation clips to be used to override animation inside Animator, and the animatorClipOverrides helper. Each weapon that can be used by a character will have their own WeaponAnimationData.

[System.Serializable]
public class WeaponAnimationData
{
    public Weapon.Type type;
    public AnimatorOverrideController animatorOverrideController;
    public AnimationClip[]            regularAttacks;
    public AnimationClip              chargedAttack;
    public AnimationClipOverrides     animatorClipOverrides;
}

Weapon Handler

The WeaponHandler class is responsible for handling the switching of player’s weapon as well as providing the animation data needed by Player based on current equipped weapon

public class WeaponHandler : MonoBehaviour
{
    [SerializeField] Transform rightHand;
    [SerializeField] Transform leftHand;
    [SerializeField] List<WeaponAnimationData> weaponAnimationDatas;

    List<Weapon>        weaponPool = new List<Weapon>();
    Weapon              currentWeapon;
    WeaponAnimationData weaponAnimData;

    public AnimationClip GetAttackClip(int i)   => weaponAnimData.regularAttacks[i % weaponAnimData.regularAttacks.Length];
    public AnimationClip GetChargedAttackClip() => weaponAnimData.chargedAttack;

    // Start is called before the first frame update
    void Start()
    {
    }

    public WeaponAnimationData SwitchWeapon(Weapon weapon)
    {
        if(weapon.weaponType == currentWeapon?.weaponType)
            return weaponAnimData;
            
        if(currentWeapon)
        {
            currentWeapon.gameObject.SetActive(false);
            weaponPool.Add(currentWeapon);
        }
        
				//Check if weapon pool already have weapon with the same weapon type
        currentWeapon = weaponPool.Find(o => o.weaponType == weapon.weaponType);
        if(currentWeapon == null)
            currentWeapon = Instantiate<Weapon>(weapon);
        else
            currentWeapon.gameObject.SetActive(true);

				//Equip weapon in corresponding hand
        if(currentWeapon.righthand)
            currentWeapon.transform.SetParent(rightHand);
        else
            currentWeapon.transform.SetParent(leftHand);
        
				//Reset weapon transformation
        currentWeapon.transform.localScale         = Vector3.one;
        currentWeapon.transform.localEulerAngles   = Vector3.zero;
        currentWeapon.transform.localPosition      = Vector3.zero;

				//Get corresponding weaponAnimationData
        weaponAnimData = weaponAnimationDatas.Find(o => o.type == weapon.weaponType);

				//instantiate and store the animatorClipOverrrides in it's corresponding weaponAnimData
        if(weaponAnimData.animatorClipOverrides == null)
            weaponAnimData.animatorClipOverrides = new AnimationClipOverrides(weaponAnimData.animatorOverrideController.overridesCount);

        return weaponAnimData;
    }
}

Animator Override Controller Setup

Create new AnimatorOverrideController for each weapon types and assign the basic Animator and corresponding animation clips.

![Untitled](Changing AnimatorState%E2%80%99s AnimationClips at Runtime ef40e35650b7427fa2bdf6e060e25374/Untitled.png)

Assign each animatorOverrideController to their corresponding character, in the WeaponHandler component inspector

![Untitled](Changing AnimatorState%E2%80%99s AnimationClips at Runtime ef40e35650b7427fa2bdf6e060e25374/Untitled 1.png)

Assign animationClips that will override the Attack clip, in this case regular attack (can be multiple clip) and charged attack

![Untitled](Changing AnimatorState%E2%80%99s AnimationClips at Runtime ef40e35650b7427fa2bdf6e060e25374/Untitled 2.png)

Switching Weapon

Switching weapon can be called from the any class as long as the gameObject has the properly setup WeaponHandler class. In this example, we’ll use Player class.

To switch weapon, call the WeaponHandler SwitchWeapon and use the WeaponAnimationData returned from the SwitchWeapon function to set/reset the animationClipOverrides and and animator.runtimeAnimatorController in the Player class.

		public void SwitchWeapon(Weapon weapon)
    {
        if(weaponHandler == null)
            weaponHandler = GetComponent<WeaponHandler>();
            
        if(animator == null)
            animator = GetComponent<Animator>();
        
        WeaponAnimationData weaponData      = weaponHandler.SwitchWeapon(weapon);
        animationClipOverrides              = weaponData.animatorClipOverrides;
        animator.runtimeAnimatorController  = weaponData.animatorOverrideController;
        animatorOverrideController          = (AnimatorOverrideController) animator.runtimeAnimatorController;
				animatorOverrideController.GetOverrides (animationClipOverrides);
    }

To change animatorOverrideController animation clip, use the ChangeAnimation function with animKey as the original AnimationClip name in the AnimatorOverrideController, clip is the animationClip to be played, and update if the animator need to be updated instantly.

void ChangeAnimation(string animKey, AnimationClip clip, bool update = true)
	{
		animationClipOverrides[animKey] = clip;
		animatorOverrideController.ApplyOverrides(animationClipOverrides);

		if(update) animator.Update(0);
	}

Example usage

		public void ChargedAttack()
    {
        ChangeAnimation("Attack", weaponHandler.GetChargedAttackClip());
        animator.SetTrigger("Attack");
    }

		public void RegularAttack()
    {
        ChangeAnimation("Attack", weaponHandler.GetAttackClip(0));
        animator.SetTrigger("Attack");
    }