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

The basic setup for the animator would be

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
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.

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

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

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");
}
No Comments