├── .gitignore
├── Test
├── Test1
│ ├── Message.cs
│ ├── Test1Info.txt
│ ├── Test1Controller.cs
│ ├── TestBehaviour.cs
│ ├── Test1.controller
│ └── Test1.unity
├── Test2
│ ├── Test2Controller.cs
│ ├── Test2.controller
│ └── Test2.unity
├── SampleAnim.anim
└── TestController.cs
├── Things to implement.txt
├── Messages.txt
├── Plugins
└── AnimatorFix
│ ├── StateMachineBehaviourFix.cs
│ └── AnimatorFix.cs
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | *.meta
--------------------------------------------------------------------------------
/Test/Test1/Message.cs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Test/Test1/Test1Info.txt:
--------------------------------------------------------------------------------
1 | This tests checks that OnStateEnter, OnStateUpdate and OnStateExit are executed in order, as expected.
2 |
3 | The default StateMachineBehaviour behaviour is that if we transition from state A to state B, there's quite a few frames between
4 | OnStateEnter is called on state B before OnStateExit is called on State A. During that time, OnStateUpdate is called on both states.
--------------------------------------------------------------------------------
/Test/Test1/Test1Controller.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System;
3 |
4 | public class Test1Controller : TestController {
5 |
6 | private readonly Message[] _expectedOrder = {
7 | new Message(MessageType.StateEnter, "State 1"),
8 | new Message(MessageType.StateUpdate, "State 1"),
9 | new Message(MessageType.StateExit, "State 1"),
10 | new Message(MessageType.StateEnter, "State 2"),
11 | new Message(MessageType.StateUpdate, "State 2"),
12 | new Message(MessageType.StateExit, "State 2"),
13 | new Message(MessageType.StateEnter, "State 3"),
14 | new Message(MessageType.StateUpdate, "State 3"),
15 | };
16 |
17 | protected override IMessageMatcher[] expectedOrder {
18 | get { return _expectedOrder; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Test/Test1/TestBehaviour.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 |
4 | public class TestBehaviour : StateMachineBehaviourFix {
5 |
6 | public override void StateEnter(Animator animator) {
7 | TestController.ReportStateEnter(stateName);
8 | }
9 |
10 | public override void StateUpdate(Animator animator) {
11 | TestController.ReportStateUpdate(stateName);
12 | }
13 |
14 | public override void StateExit(Animator animator) {
15 | TestController.ReportStateExit(stateName);
16 | }
17 |
18 | public override void StateMachineEnter(Animator animator) {
19 | TestController.ReportStateMachineEnter(stateName);
20 | }
21 |
22 | public override void StateMachineUpdate(Animator animator) {
23 | TestController.ReportStateMachineUpdate(stateName);
24 | }
25 |
26 | public override void StateMachineExit(Animator animator) {
27 | TestController.ReportStateMachineExit(stateName);
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/Test/Test2/Test2Controller.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 |
4 | public class Test2Controller : TestController {
5 |
6 | private IMessageMatcher[] _expectedOrder = {
7 | new Message(MessageType.StateEnter, "State 1"),
8 | new Message(MessageType.StateUpdate, "State 1"),
9 | new Message(MessageType.StateExit, "State 1"),
10 |
11 | new Message(MessageType.SubStateMachineEnter, "SubStateMachine"),
12 |
13 | new Message(MessageType.StateEnter, "State 2"),
14 | new OrderedMessages(
15 | new Message(MessageType.StateUpdate, "State 2"),
16 | new Message(MessageType.SubStateMachineUpdate, "SubStateMachine")),
17 | new Message(MessageType.StateExit, "State 2"),
18 |
19 | new Message(MessageType.SubStateMachineExit, "SubStateMachine"),
20 |
21 | new Message(MessageType.StateEnter, "State 3"),
22 | new Message(MessageType.StateUpdate, "State 3"),
23 | };
24 |
25 | protected override IMessageMatcher[] expectedOrder {
26 | get { return _expectedOrder; }
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/Things to implement.txt:
--------------------------------------------------------------------------------
1 | 1: Proper OnState-messages. This means that the order when transitioning from state A to state B is guaranteed to be:
2 |
3 | - A.StateUpdate
4 | - A.StateExit
5 | - B.StateEnter
6 | - B.StateUpdate
7 |
8 | This also means that StateMachineEnter and StateMachineExit will be called whenever a state machine is entered/exited, not
9 | just when the flow goes through the entry/exit nodes.
10 |
11 | 2: Awake and OnEnable for the states. This will be the same as for the AnimatorFix script
12 |
13 | 3: A bigger and better API for getting information about the current state of the Animator. This will include:
14 | - CurrentStateName
15 | - CurrentStateMachine/s
16 | - IsInState/Machine that looks a lot better than the default ones
17 | - IsTransitioningTo/From + GetCurrentTransition
18 |
19 | All of these will default to layer 0. This means that this attrocity:
20 | if(animator.GetCurrentAnimatorStateInfo(0).IsName("Foo"))
21 |
22 | Is turned into:
23 | if(animator.IsInState("Foo"))
24 |
25 | Which just reads so_much_better
26 |
27 | 4: Performance: Once all the other stuff is done, this has to be measured and fixed. Both for GC and for general speed.
--------------------------------------------------------------------------------
/Messages.txt:
--------------------------------------------------------------------------------
1 | The methods that are defined in StateMachineBehaviourFix are:
2 |
3 | StateMachineEnter
4 | StateMachineUpdate
5 | StateMachineExit
6 | StateEnter
7 | StateUpdate
8 | StateExit
9 |
10 | The default (Unity) methods that have not been changed are:
11 | OnStateMove
12 | OnStateIK
13 |
14 | On a frame (Update), if the state machine doesn't change state, and state A is the current state, this happens:
15 |
16 | StateUpdate is called on state A.
17 | StateMachineUpdate is called on any subStateMachine that contains A.
18 | - if A is contained in several machines, the methods will be called first on the innermost state machine,
19 | and then progressing outwards. So if the AnimatorController contains SubStateMachine1, which again
20 | contains SubStateMachine2, which contains state A, the order will be:
21 | StateUpdate called on A
22 | StateMachineUpdate called on SubStateMachine2
23 | StateMachineUpdate called on SubStateMachine1
24 |
25 | When the current state changes from state A to state B, this happens:
26 | - StateExit is called on A
27 | - StateMachineExit is called on any state machine exited - that is, state machines that contains A, but not B
28 | As in Update, this happens innermost state to outmost state
29 | - StateMachineEnter is called on any state machine entered - again, this is states that contain B, but not A.
30 | This happens in the reverse order as Update - so the outermost state machine is called Enter on first.
31 | - StateEnter is called on B
32 | - Then StateUpdate and StateMachineUpdate is called on State B and machines containing B as normal.
33 |
34 |
35 | The change is triggered when the Animator changes which state is returned by GetCurrentAnimatorStateInfo
36 |
--------------------------------------------------------------------------------
/Plugins/AnimatorFix/StateMachineBehaviourFix.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using UnityEngine.Experimental.Director;
3 |
4 | ///
5 | /// Use this class instead of StateMachineBehaviour, and attach an AnimatorState to the same object as the Animator.
6 | ///
7 | /// That will give you access to a proper OnStateMachineExit
8 | ///
9 | public abstract class StateMachineBehaviourFix : StateMachineBehaviour {
10 |
11 | [HideInInspector] public string stateName;
12 | [HideInInspector] public int stateHash;
13 | [HideInInspector] public bool attachedToStateMachine;
14 |
15 | #region sealedOriginalMethods
16 |
17 | public sealed override void OnStateMachineExit(Animator animator, int stateMachinePathHash) {}
18 | public sealed override void OnStateMachineExit(Animator animator, int stateMachinePathHash, AnimatorControllerPlayable controller) {}
19 | public sealed override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {}
20 | public sealed override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller) {}
21 | public sealed override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {}
22 | public sealed override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller) {}
23 | public sealed override void OnStateMachineEnter(Animator animator, int stateMachinePathHash) {}
24 | public sealed override void OnStateMachineEnter(Animator animator, int stateMachinePathHash, AnimatorControllerPlayable controller) {}
25 | public sealed override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {}
26 | public sealed override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex, AnimatorControllerPlayable controller) {}
27 |
28 | #endregion
29 |
30 | #region customMethods
31 |
32 | public virtual void StateMachineExit(Animator animator) {}
33 | public virtual void StateMachineUpdate(Animator animator) {}
34 | public virtual void StateMachineEnter(Animator animator) {}
35 | public virtual void StateEnter(Animator animator) {}
36 | public virtual void StateUpdate(Animator animator) {}
37 | public virtual void StateExit(Animator animator) {}
38 |
39 | #endregion
40 | }
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # About
2 | A lean project that aims to fix a bunch of the many issues with Unity's Animator and StateMachineBehaviour systems.
3 | The Animator is a really powerfull system, but it has some major disadvantages. First of all, the API for getting information about the current state of the Animator is horribly obtuse and cumbersome to use. Secondly, the StateMachineBehaviour's messages - especially OnStateMachineEnter/Exit - doesn't fire when you expect them to. Both of these disadvantages are by design, so this project aims to create the tools needed to use the Animator comfortably.
4 |
5 | I want the code to work with your existing Animator-based projects, so this isn't a replacement or a framework. The start of this project is code I wrote for [World To The West](http://www.worldtothewest.com/), in order to work with Animators there. I have seen a lot of people on Unity Answers and the Unity forums that have the same gripes with this stuff as I do, so I figured that there would be interest both in using the code, and helping improve it.
6 |
7 | # Contents
8 | There's two main scripts:
9 | - StateMachineBehaviourFix is supposed to be a replacement for StateMachineBehaviour. It's got an OnStateMachineExit method that's called whenever a state machine is exited. The default Unity StateMachineBehaviour generally doesn't do that.
10 | - AnimatorFix is neccessary to make StateMachineBehaviourFix work. It's attached to the same object as the Animator, and works by parsing the Animator's AnimatorController to find out what methods should be called when. It's also got some convenience methods that the Animator should have had - like GetCurrentStateName(int layer) and IsInSubStateMachine(string name, int layer)
11 |
12 | # Setup:
13 | The repo is meant to be placed directly in your Assets folder. The code's all in Plugins/AnimatorFix. If you want to just use the code for production, you should just copy over the Plugins folder directly.
14 |
15 | # Current issues
16 | This is a very work in progress project. The code from the inital commit is exactly what I needed to work with Animators in World To The West. There's a couple of things that are problematic, and some things that I think this needs to be a worthy stand-alone project:
17 |
18 | Bugs:
19 | - The AnimatorFix script requires you to select the object it's attached to once after you update the corresponding AnimatorController. That's a big inconvenicene
20 |
21 | Problems:
22 | - AnimatorFix isn't a really good name. This project isn't about fixing bugs, but about a fundamental disagreement with Unity about the Animator/StateMachineBehaviour API. Name suggestions are welcome!
23 |
24 | To be considered:
25 | - Right now, the system only replaces OnStateMachineExit. This is the most broken method. The system could easily be expanded to handle all of the state machine messages. That would probably be advantageous; OnStateEnter and OnStateExit are not neccessarily called when you expect them to.
26 |
--------------------------------------------------------------------------------
/Test/SampleAnim.anim:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!74 &7400000
4 | AnimationClip:
5 | m_ObjectHideFlags: 0
6 | m_PrefabParentObject: {fileID: 0}
7 | m_PrefabInternal: {fileID: 0}
8 | m_Name: SampleAnim
9 | serializedVersion: 6
10 | m_Legacy: 0
11 | m_Compressed: 0
12 | m_UseHighQualityCurve: 1
13 | m_RotationCurves: []
14 | m_CompressedRotationCurves: []
15 | m_EulerCurves: []
16 | m_PositionCurves:
17 | - curve:
18 | serializedVersion: 2
19 | m_Curve:
20 | - time: 0
21 | value: {x: 0, y: 0, z: 0}
22 | inSlope: {x: 0, y: 6, z: 0}
23 | outSlope: {x: 0, y: 6, z: 0}
24 | tangentMode: 0
25 | - time: 0.5
26 | value: {x: 0, y: 3, z: 0}
27 | inSlope: {x: 0, y: 6, z: 0}
28 | outSlope: {x: 0, y: -12, z: 0}
29 | tangentMode: 0
30 | - time: 1
31 | value: {x: 0, y: -3, z: 0}
32 | inSlope: {x: 0, y: -12, z: 0}
33 | outSlope: {x: 0, y: 6, z: 0}
34 | tangentMode: 0
35 | - time: 1.5
36 | value: {x: 0, y: 0, z: 0}
37 | inSlope: {x: 0, y: 6, z: 0}
38 | outSlope: {x: 0, y: 6, z: 0}
39 | tangentMode: 0
40 | m_PreInfinity: 2
41 | m_PostInfinity: 2
42 | m_RotationOrder: 4
43 | path:
44 | m_ScaleCurves: []
45 | m_FloatCurves: []
46 | m_PPtrCurves: []
47 | m_SampleRate: 60
48 | m_WrapMode: 0
49 | m_Bounds:
50 | m_Center: {x: 0, y: 0, z: 0}
51 | m_Extent: {x: 0, y: 0, z: 0}
52 | m_ClipBindingConstant:
53 | genericBindings:
54 | - path: 0
55 | attribute: 1
56 | script: {fileID: 0}
57 | classID: 4
58 | customType: 0
59 | isPPtrCurve: 0
60 | pptrCurveMapping: []
61 | m_AnimationClipSettings:
62 | serializedVersion: 2
63 | m_AdditiveReferencePoseClip: {fileID: 0}
64 | m_AdditiveReferencePoseTime: 0
65 | m_StartTime: 0
66 | m_StopTime: 1.5
67 | m_OrientationOffsetY: 0
68 | m_Level: 0
69 | m_CycleOffset: 0
70 | m_HasAdditiveReferencePose: 0
71 | m_LoopTime: 1
72 | m_LoopBlend: 0
73 | m_LoopBlendOrientation: 0
74 | m_LoopBlendPositionY: 0
75 | m_LoopBlendPositionXZ: 0
76 | m_KeepOriginalOrientation: 0
77 | m_KeepOriginalPositionY: 1
78 | m_KeepOriginalPositionXZ: 0
79 | m_HeightFromFeet: 0
80 | m_Mirror: 0
81 | m_EditorCurves:
82 | - curve:
83 | serializedVersion: 2
84 | m_Curve:
85 | - time: 0
86 | value: 0
87 | inSlope: 0
88 | outSlope: 0
89 | tangentMode: 10
90 | - time: 0.5
91 | value: 0
92 | inSlope: 0
93 | outSlope: 0
94 | tangentMode: 10
95 | - time: 1
96 | value: 0
97 | inSlope: 0
98 | outSlope: 0
99 | tangentMode: 10
100 | - time: 1.5
101 | value: 0
102 | inSlope: 0
103 | outSlope: 0
104 | tangentMode: 10
105 | m_PreInfinity: 2
106 | m_PostInfinity: 2
107 | m_RotationOrder: 4
108 | attribute: m_LocalPosition.x
109 | path:
110 | classID: 4
111 | script: {fileID: 0}
112 | - curve:
113 | serializedVersion: 2
114 | m_Curve:
115 | - time: 0
116 | value: 0
117 | inSlope: 6
118 | outSlope: 6
119 | tangentMode: 10
120 | - time: 0.5
121 | value: 3
122 | inSlope: 6
123 | outSlope: -12
124 | tangentMode: 21
125 | - time: 1
126 | value: -3
127 | inSlope: -12
128 | outSlope: 6
129 | tangentMode: 21
130 | - time: 1.5
131 | value: 0
132 | inSlope: 6
133 | outSlope: 6
134 | tangentMode: 10
135 | m_PreInfinity: 2
136 | m_PostInfinity: 2
137 | m_RotationOrder: 4
138 | attribute: m_LocalPosition.y
139 | path:
140 | classID: 4
141 | script: {fileID: 0}
142 | - curve:
143 | serializedVersion: 2
144 | m_Curve:
145 | - time: 0
146 | value: 0
147 | inSlope: 0
148 | outSlope: 0
149 | tangentMode: 10
150 | - time: 0.5
151 | value: 0
152 | inSlope: 0
153 | outSlope: 0
154 | tangentMode: 10
155 | - time: 1
156 | value: 0
157 | inSlope: 0
158 | outSlope: 0
159 | tangentMode: 10
160 | - time: 1.5
161 | value: 0
162 | inSlope: 0
163 | outSlope: 0
164 | tangentMode: 10
165 | m_PreInfinity: 2
166 | m_PostInfinity: 2
167 | m_RotationOrder: 4
168 | attribute: m_LocalPosition.z
169 | path:
170 | classID: 4
171 | script: {fileID: 0}
172 | m_EulerEditorCurves: []
173 | m_HasGenericRootTransform: 1
174 | m_HasMotionFloatCurves: 0
175 | m_GenerateMotionCurves: 0
176 | m_Events: []
177 |
--------------------------------------------------------------------------------
/Test/Test1/Test1.controller:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!91 &9100000
4 | AnimatorController:
5 | m_ObjectHideFlags: 0
6 | m_PrefabParentObject: {fileID: 0}
7 | m_PrefabInternal: {fileID: 0}
8 | m_Name: Test1
9 | serializedVersion: 5
10 | m_AnimatorParameters: []
11 | m_AnimatorLayers:
12 | - serializedVersion: 5
13 | m_Name: Base Layer
14 | m_StateMachine: {fileID: 110721062}
15 | m_Mask: {fileID: 0}
16 | m_Motions: []
17 | m_Behaviours: []
18 | m_BlendingMode: 0
19 | m_SyncedLayerIndex: -1
20 | m_DefaultWeight: 0
21 | m_IKPass: 0
22 | m_SyncedLayerAffectsTiming: 0
23 | m_Controller: {fileID: 9100000}
24 | --- !u!114 &11434432
25 | MonoBehaviour:
26 | m_ObjectHideFlags: 1
27 | m_PrefabParentObject: {fileID: 0}
28 | m_PrefabInternal: {fileID: 0}
29 | m_GameObject: {fileID: 0}
30 | m_Enabled: 1
31 | m_EditorHideFlags: 0
32 | m_Script: {fileID: 11500000, guid: 8a38c01ae6569c340a5e3148eea848bd, type: 3}
33 | m_Name:
34 | m_EditorClassIdentifier:
35 | parentMachine:
36 | stateName: State 3
37 | stateHash: 1000687306
38 | --- !u!114 &11443904
39 | MonoBehaviour:
40 | m_ObjectHideFlags: 1
41 | m_PrefabParentObject: {fileID: 0}
42 | m_PrefabInternal: {fileID: 0}
43 | m_GameObject: {fileID: 0}
44 | m_Enabled: 1
45 | m_EditorHideFlags: 0
46 | m_Script: {fileID: 11500000, guid: 8a38c01ae6569c340a5e3148eea848bd, type: 3}
47 | m_Name:
48 | m_EditorClassIdentifier:
49 | parentMachine:
50 | stateName: State 1
51 | stateHash: -710203418
52 | --- !u!114 &11449336
53 | MonoBehaviour:
54 | m_ObjectHideFlags: 1
55 | m_PrefabParentObject: {fileID: 0}
56 | m_PrefabInternal: {fileID: 0}
57 | m_GameObject: {fileID: 0}
58 | m_Enabled: 1
59 | m_EditorHideFlags: 0
60 | m_Script: {fileID: 11500000, guid: 8a38c01ae6569c340a5e3148eea848bd, type: 3}
61 | m_Name:
62 | m_EditorClassIdentifier:
63 | parentMachine:
64 | stateName: State 2
65 | stateHash: 1285715548
66 | --- !u!1101 &110154082
67 | AnimatorStateTransition:
68 | m_ObjectHideFlags: 1
69 | m_PrefabParentObject: {fileID: 0}
70 | m_PrefabInternal: {fileID: 0}
71 | m_Name:
72 | m_Conditions: []
73 | m_DstStateMachine: {fileID: 0}
74 | m_DstState: {fileID: 110202840}
75 | m_Solo: 0
76 | m_Mute: 0
77 | m_IsExit: 0
78 | serializedVersion: 3
79 | m_TransitionDuration: 0.25
80 | m_TransitionOffset: 0
81 | m_ExitTime: 0.8333333
82 | m_HasExitTime: 1
83 | m_HasFixedDuration: 1
84 | m_InterruptionSource: 0
85 | m_OrderedInterruption: 1
86 | m_CanTransitionToSelf: 1
87 | --- !u!1101 &110177320
88 | AnimatorStateTransition:
89 | m_ObjectHideFlags: 1
90 | m_PrefabParentObject: {fileID: 0}
91 | m_PrefabInternal: {fileID: 0}
92 | m_Name:
93 | m_Conditions: []
94 | m_DstStateMachine: {fileID: 0}
95 | m_DstState: {fileID: 110203410}
96 | m_Solo: 0
97 | m_Mute: 0
98 | m_IsExit: 0
99 | serializedVersion: 3
100 | m_TransitionDuration: 0.25
101 | m_TransitionOffset: 0
102 | m_ExitTime: 0.8333333
103 | m_HasExitTime: 1
104 | m_HasFixedDuration: 1
105 | m_InterruptionSource: 0
106 | m_OrderedInterruption: 1
107 | m_CanTransitionToSelf: 1
108 | --- !u!1102 &110202840
109 | AnimatorState:
110 | serializedVersion: 5
111 | m_ObjectHideFlags: 1
112 | m_PrefabParentObject: {fileID: 0}
113 | m_PrefabInternal: {fileID: 0}
114 | m_Name: State 2
115 | m_Speed: 1
116 | m_CycleOffset: 0
117 | m_Transitions:
118 | - {fileID: 110177320}
119 | m_StateMachineBehaviours:
120 | - {fileID: 11449336}
121 | m_Position: {x: 50, y: 50, z: 0}
122 | m_IKOnFeet: 0
123 | m_WriteDefaultValues: 1
124 | m_Mirror: 0
125 | m_SpeedParameterActive: 0
126 | m_MirrorParameterActive: 0
127 | m_CycleOffsetParameterActive: 0
128 | m_Motion: {fileID: 7400000, guid: 0e96c4a802ce07644bc7b69ff0864dae, type: 2}
129 | m_Tag:
130 | m_SpeedParameter:
131 | m_MirrorParameter:
132 | m_CycleOffsetParameter:
133 | --- !u!1102 &110203410
134 | AnimatorState:
135 | serializedVersion: 5
136 | m_ObjectHideFlags: 1
137 | m_PrefabParentObject: {fileID: 0}
138 | m_PrefabInternal: {fileID: 0}
139 | m_Name: State 3
140 | m_Speed: 1
141 | m_CycleOffset: 0
142 | m_Transitions: []
143 | m_StateMachineBehaviours:
144 | - {fileID: 11434432}
145 | m_Position: {x: 50, y: 50, z: 0}
146 | m_IKOnFeet: 0
147 | m_WriteDefaultValues: 1
148 | m_Mirror: 0
149 | m_SpeedParameterActive: 0
150 | m_MirrorParameterActive: 0
151 | m_CycleOffsetParameterActive: 0
152 | m_Motion: {fileID: 7400000, guid: 0e96c4a802ce07644bc7b69ff0864dae, type: 2}
153 | m_Tag:
154 | m_SpeedParameter:
155 | m_MirrorParameter:
156 | m_CycleOffsetParameter:
157 | --- !u!1102 &110287390
158 | AnimatorState:
159 | serializedVersion: 5
160 | m_ObjectHideFlags: 1
161 | m_PrefabParentObject: {fileID: 0}
162 | m_PrefabInternal: {fileID: 0}
163 | m_Name: State 1
164 | m_Speed: 1
165 | m_CycleOffset: 0
166 | m_Transitions:
167 | - {fileID: 110154082}
168 | m_StateMachineBehaviours:
169 | - {fileID: 11443904}
170 | m_Position: {x: 50, y: 50, z: 0}
171 | m_IKOnFeet: 0
172 | m_WriteDefaultValues: 1
173 | m_Mirror: 0
174 | m_SpeedParameterActive: 0
175 | m_MirrorParameterActive: 0
176 | m_CycleOffsetParameterActive: 0
177 | m_Motion: {fileID: 7400000, guid: 0e96c4a802ce07644bc7b69ff0864dae, type: 2}
178 | m_Tag:
179 | m_SpeedParameter:
180 | m_MirrorParameter:
181 | m_CycleOffsetParameter:
182 | --- !u!1107 &110721062
183 | AnimatorStateMachine:
184 | serializedVersion: 5
185 | m_ObjectHideFlags: 1
186 | m_PrefabParentObject: {fileID: 0}
187 | m_PrefabInternal: {fileID: 0}
188 | m_Name: Base Layer
189 | m_ChildStates:
190 | - serializedVersion: 1
191 | m_State: {fileID: 110287390}
192 | m_Position: {x: 264, y: 120, z: 0}
193 | - serializedVersion: 1
194 | m_State: {fileID: 110202840}
195 | m_Position: {x: 492, y: 120, z: 0}
196 | - serializedVersion: 1
197 | m_State: {fileID: 110203410}
198 | m_Position: {x: 720, y: 120, z: 0}
199 | m_ChildStateMachines: []
200 | m_AnyStateTransitions: []
201 | m_EntryTransitions: []
202 | m_StateMachineTransitions: {}
203 | m_StateMachineBehaviours: []
204 | m_AnyStatePosition: {x: 50, y: 20, z: 0}
205 | m_EntryPosition: {x: 50, y: 120, z: 0}
206 | m_ExitPosition: {x: 48, y: 168, z: 0}
207 | m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
208 | m_DefaultState: {fileID: 110287390}
209 |
--------------------------------------------------------------------------------
/Test/TestController.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using UnityEngine.UI;
3 |
4 | public abstract class TestController : MonoBehaviour {
5 |
6 | [SerializeField] private bool logToConsole;
7 |
8 | protected abstract IMessageMatcher[] expectedOrder { get; }
9 |
10 | private Text text;
11 | private static Message lastMessage;
12 | private static TestController instance;
13 | private static bool testEnded;
14 |
15 | private int currentIndex;
16 |
17 | void Awake() {
18 | instance = this;
19 | currentIndex = -1;
20 |
21 | Canvas c = new GameObject("Canvas", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster)).GetComponent