├── AnimatorCoder.cs ├── AnimatorValues.cs ├── LICENSE ├── OnParameter.cs └── readme.txt /AnimatorCoder.cs: -------------------------------------------------------------------------------- 1 | //Author: Small Hedge Games 2 | //Date: 02/07/2024 3 | 4 | using System.Collections; 5 | using UnityEngine; 6 | using System; 7 | 8 | namespace SHG.AnimatorCoder 9 | { 10 | public abstract class AnimatorCoder : MonoBehaviour 11 | { 12 | /// The baseline animation logic on a specific layer 13 | public abstract void DefaultAnimation(int layer); 14 | private Animator animator = null; 15 | private Animations[] currentAnimation; 16 | private bool[] layerLocked; 17 | private ParameterDisplay[] parameters; 18 | private Coroutine[] currentCoroutine; 19 | 20 | /// Sets up the Animator Brain 21 | public void Initialize(Animator animator = null) 22 | { 23 | AnimatorValues.Initialize(); 24 | 25 | if(animator == null) 26 | this.animator = GetComponent(); 27 | else 28 | this.animator = animator; 29 | 30 | currentCoroutine = new Coroutine[this.animator.layerCount]; 31 | layerLocked = new bool[this.animator.layerCount]; 32 | currentAnimation = new Animations[this.animator.layerCount]; 33 | 34 | for (int i = 0; i < this.animator.layerCount; ++i) 35 | { 36 | layerLocked[i] = false; 37 | 38 | int hash = this.animator.GetCurrentAnimatorStateInfo(i).shortNameHash; 39 | for (int k = 0; k < AnimatorValues.Animations.Length; ++k) 40 | { 41 | if (hash == AnimatorValues.Animations[k]) 42 | { 43 | currentAnimation[i] = (Animations)Enum.GetValues(typeof(Animations)).GetValue(k); 44 | k = AnimatorValues.Animations.Length; 45 | } 46 | } 47 | } 48 | 49 | string[] names = Enum.GetNames(typeof(Parameters)); 50 | parameters = new ParameterDisplay[names.Length]; 51 | for (int i = 0; i < names.Length; ++i) 52 | { 53 | parameters[i].name = names[i]; 54 | parameters[i].value = false; 55 | } 56 | } 57 | 58 | /// Returns the current animation that is playing 59 | public Animations GetCurrentAnimation(int layer) 60 | { 61 | try 62 | { 63 | return currentAnimation[layer]; 64 | } 65 | catch 66 | { 67 | LogError("Can't retrieve Current Animation. Fix: Initialize() in Start() and don't exceed number of animator layers"); 68 | return Animations.RESET; 69 | } 70 | } 71 | 72 | /// Sets the whole layer to be locked or unlocked 73 | public void SetLocked(bool lockLayer, int layer) 74 | { 75 | try 76 | { 77 | layerLocked[layer] = lockLayer; 78 | } 79 | catch 80 | { 81 | LogError("Can't retrieve Current Animation. Fix: Initialize() in Start() and don't exceed number of animator layers"); 82 | } 83 | } 84 | 85 | public bool IsLocked(int layer) 86 | { 87 | try 88 | { 89 | return layerLocked[layer]; 90 | } 91 | catch 92 | { 93 | LogError("Can't retrieve Current Animation. Fix: Initialize() in Start() and don't exceed number of animator layers"); 94 | return false; 95 | } 96 | } 97 | 98 | /// Sets an animator parameter 99 | public void SetBool(Parameters id, bool value) 100 | { 101 | try 102 | { 103 | parameters[(int)id].value = value; 104 | } 105 | catch 106 | { 107 | LogError("Please Initialize() in Start()"); 108 | } 109 | } 110 | 111 | /// Returns an animator parameter 112 | public bool GetBool(Parameters id) 113 | { 114 | try 115 | { 116 | return parameters[(int)id].value; 117 | } 118 | catch 119 | { 120 | LogError("Please Initialize() in Start()"); 121 | return false; 122 | } 123 | } 124 | 125 | /// Takes in the animation details and the animation layer, then attempts to play the animation 126 | public bool Play(AnimationData data, int layer = 0) 127 | { 128 | try 129 | { 130 | if (data.animation == Animations.RESET) 131 | { 132 | DefaultAnimation(layer); 133 | return false; 134 | } 135 | 136 | if (layerLocked[layer] || currentAnimation[layer] == data.animation) return false; 137 | 138 | if (currentCoroutine[layer] != null) StopCoroutine(currentCoroutine[layer]); 139 | layerLocked[layer] = data.lockLayer; 140 | currentAnimation[layer] = data.animation; 141 | 142 | animator.CrossFade(AnimatorValues.GetHash(currentAnimation[layer]), data.crossfade, layer); 143 | 144 | if (data.nextAnimation != null) 145 | { 146 | currentCoroutine[layer] = StartCoroutine(Wait()); 147 | IEnumerator Wait() 148 | { 149 | animator.Update(0); 150 | float delay = animator.GetNextAnimatorStateInfo(layer).length; 151 | if (data.crossfade == 0) delay = animator.GetCurrentAnimatorStateInfo(layer).length; 152 | yield return new WaitForSeconds(delay - data.nextAnimation.crossfade); 153 | SetLocked(false, layer); 154 | Play(data.nextAnimation, layer); 155 | } 156 | } 157 | 158 | return true; 159 | } 160 | catch 161 | { 162 | LogError("Please Initialize() in Start()"); 163 | return false; 164 | } 165 | } 166 | 167 | private void LogError(string message) 168 | { 169 | Debug.LogError("AnimatorCoder Error: " + message); 170 | } 171 | } 172 | 173 | /// Holds all data about an animation 174 | [Serializable] 175 | public class AnimationData 176 | { 177 | public Animations animation; 178 | /// Should the layer lock for this animation? 179 | public bool lockLayer; 180 | /// Should an animation play immediately after? 181 | public AnimationData nextAnimation; 182 | /// Should there be a transition time into this animation? 183 | public float crossfade = 0; 184 | 185 | /// Sets the animation data 186 | public AnimationData(Animations animation = Animations.RESET, bool lockLayer = false, AnimationData nextAnimation = null, float crossfade = 0) 187 | { 188 | this.animation = animation; 189 | this.lockLayer = lockLayer; 190 | this.nextAnimation = nextAnimation; 191 | this.crossfade = crossfade; 192 | } 193 | } 194 | 195 | /// Class the manages the hashes of animations and parameters 196 | public class AnimatorValues 197 | { 198 | /// Returns the animation hash array 199 | public static int[] Animations { get { return animations; } } 200 | 201 | private static int[] animations; 202 | private static bool initialized = false; 203 | 204 | /// Initializes the animator state names 205 | public static void Initialize() 206 | { 207 | if (initialized) return; 208 | initialized = true; 209 | 210 | string[] names = Enum.GetNames(typeof(Animations)); 211 | 212 | animations = new int[names.Length]; 213 | for (int i = 0; i < names.Length; i++) 214 | animations[i] = Animator.StringToHash(names[i]); 215 | } 216 | 217 | /// Gets the animator hash value of an animation 218 | public static int GetHash(Animations animation) 219 | { 220 | return animations[(int)animation]; 221 | } 222 | } 223 | 224 | /// Allows the animation parameters to be shown in debug inspector 225 | [Serializable] 226 | public struct ParameterDisplay 227 | { 228 | [HideInInspector] public string name; 229 | public bool value; 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /AnimatorValues.cs: -------------------------------------------------------------------------------- 1 | //Author: Small Hedge Games 2 | //Date: 05/04/2024 3 | 4 | namespace SHG.AnimatorCoder 5 | { 6 | /// Complete list of all animation state names 7 | public enum Animations 8 | { 9 | //Change the list below to your animation state names 10 | IDLE, 11 | RUN, 12 | ATTACK1, 13 | ATTACK2, 14 | HIT, 15 | JUMP, 16 | FALL, 17 | RESET //Keep Reset 18 | } 19 | 20 | /// Complete list of all animator parameters 21 | public enum Parameters 22 | { 23 | //Change the list below to your animator parameters 24 | GROUNDED, 25 | FALLING 26 | } 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /OnParameter.cs: -------------------------------------------------------------------------------- 1 | //Author: Small Hedge Games 2 | //Date: 05/04/2024 3 | 4 | using UnityEngine; 5 | 6 | namespace SHG.AnimatorCoder 7 | { 8 | public class OnParameter : StateMachineBehaviour 9 | { 10 | [SerializeField, Tooltip("Parameter to test")] private Parameters parameter; 11 | [SerializeField, Tooltip("Specify whether it should be on or off")] private bool target; 12 | [SerializeField, Tooltip("Chain of animations to play when condition is met")] private AnimationData[] nextAnimations; 13 | 14 | private AnimatorCoder animatorBrain; 15 | 16 | override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) 17 | { 18 | animatorBrain = animator.GetComponent(); 19 | } 20 | 21 | override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) 22 | { 23 | if (animatorBrain.GetBool(parameter) != target) return; 24 | animatorBrain.SetLocked(false, layerIndex); 25 | 26 | for (int i = 0; i < nextAnimations.Length - 1; ++i) 27 | nextAnimations[i].nextAnimation = nextAnimations[i + 1]; 28 | 29 | animatorBrain.Play(nextAnimations[0], layerIndex); 30 | } 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | AnimatorCoder by Small Hedge Games 08/04/2024 2 | 3 | Note: Crossfade is not affected when playing the RESET animation through script or through the ONPARAMETER inspector! 4 | 5 | Setup: 6 | 1. Fill in Animator States and Animator Parameters in AnimatorValues 7 | 2. Use the SHG.AnimatorCoder namespace 8 | 3. Inherit AnimatorCoder in the desired script 9 | 4. Implement DefaultAnimation(int layer) method in desired script 10 | 5. Run Initalize() in the Start() method 11 | 12 | Public Methods: 13 | 14 | void Initialize() - Sets up the AnimatorCoder 15 | bool Play(AnimationData data, int layer) - Attempts to play an animation and returns its success 16 | Animations GetCurrentAnimation(int layer) - Return current animation on layer 17 | void SetLocked(bool lockLayer, int layer) - Set a layer to be locked 18 | bool IsLocked(int layer) - Is the layer locked? 19 | void SetBool(Parameters id, bool value) - Sets a parameter 20 | bool GetBool(Parameters id) - Returns a parameter 21 | 22 | Tutorial: https://youtu.be/9tvDtS1vYuM 23 | --------------------------------------------------------------------------------