├── 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 |
--------------------------------------------------------------------------------