-
Remember last state: This is a new parameter in the constructor of the
StateMachine
class that is interesting for nested state machines. When set to true, it makes the state machine return to its last active state when it enters, instead of its original start state. You can also use this feature in theHybridStateMachine
class. -
Run states in parallel: The new
ParallelStates
class allows you to run multiple states in parallel. IfneedsExitTime
is set to true, it will wait until any one of the child states callsStateCanExit
before it exits. This behaviour can be overridden by providing a customcanExit
function.E.g.
var attackFsm = new StateMachine(); attackFsm.AddState("Idle"); attackFsm.AddState("Attack"); // ... fsm.AddState("A", new ParallelStates( new State(onLogic: s => MoveTowardsPlayer()), new State(onLogic: s => Animate()), attackFsm ));
With a custom
canExit
function:fsm.AddState("A", new ParallelStates( canExit: s => IsPlayerInRange(), needsExitTime: true, new State(onLogic: s => MoveTowardsPlayer()), new State(onLogic: s => Animate()) ));
-
Active State Changed Event: The
StateMachine
class now has a new event that you can subscribe to that is triggered when its active state is changed:E.g.
fsm.StateChanged += state => print(state.name); fsm.AddState("A"); fsm.AddState("B"); fsm.AddTransition("A", "B"); fsm.Init(); // prints "A" fsm.OnLogic(); // prints "B"
-
Improved the performance of the
OnLogic
and theTrigger
methods of theStateMachine
class when states have multiple outgoing transitions. Depending on the number of transitions, when using string state names, this can make theOnLogic
method up to 15% faster. -
The naming of the key / mouse transition classes has been improved by following the C# naming convention for events.
TransitionOnKey.Press
is nowTransitionOnKey.Pressed
TransitionOnKey.Release
is nowTransitionOnKey.Released
TransitionOnMouse.Press
is nowTransitionOnMouse.Pressed
TransitionOnMouse.Release
is nowTransitionOnMouse.Released
-
Improved documentation.
-
Fix incorrect execution order (timing) bug concerning the
canExit
feature of theState
class. -
Fix
Time.time
access exception bug that occurred during the deserialisation ofState
andState
-derived classes shown in the inspector. -
Fix incorrect output of
GetActiveHierarchyPath()
in theStateWrapper.WrappedState
class.
-
Fix samples not compiling.
-
Reintroduced the
timer
property in theCoState
class that was lost in the previous release.
-
Ghost states: Ghost states are states that the state machine does not want to remain in and will try to exit as soon as possible. This means that the fsm can do multiple transitions in one
OnLogic
call. The "ghost state behaviour" is supported by all state types by setting theisGhostState
field.E.g.
fsm.AddState("A", onEnter: s => print("A")); fsm.AddState("B", new State(onEnter: s => print("B"), isGhostState: true)); fsm.AddState("C", onEnter: s => print("C"); fsm.AddTransition("A", "B"); fsm.AddTransition("B", "C"); fsm.Init(); // Prints "A" fsm.OnLogic(); // Prints "B" and then "C"
-
Exit transitions: Exit transitions finally provide an easy and powerful way to define the exit conditions for nested state machines, essentially levelling up the mechanics behind hierarchical state machines. Previously, the rule that determined when a nested state machine that
needsExitTime
can exit, was implicit, not versatile, and not in the control of the developer.var nested = new StateMachine(needsExitTime: true); nested.AddState("A"); nested.AddState("B"); // ... // The nested fsm can only exit when it is in the "B" state and // the variable x equals 0. move.AddExitTransition("B", t => x == 0);
Exit transitions can also be defined for all states (
AddExitTransitionFromAny
), as trigger transitions (AddExitTriggerTransition
), or as both (AddExitTriggerTransitionFromAny
). -
Transition callbacks: New feature that lets you define a function that is called when a transition succeeds. It is supported by all transition types (e.g. trigger transitions, transitions from any, exit transitions, ...).
fsm.AddTransition( new Transition("A", "B", onTransition: t => print("Transition")) );
This feature is also supported when using the shortcut methods:
// Can be shortened using shortcut methods: fsm.AddTransition("A", "B", onTransition: t => print("Transition"));
The print function will be called just before the transition. You can also define a callback that is called just after the transition:
fsm.AddTransition("A", "B", onTransition: t => print("Before"), afterTransition: t => print("After") );
-
Support for custom actions in
HybridStateMachine
, just like in the normalState
class:var hybrid = new HybridStateMachine(); hybrid.AddState("A", new State().AddAction("Action", () => print("A"))); hybrid.AddAction("Action", () => print("Hybrid")); hybrid.Init(); hybrid.OnAction("Action"); // Prints "Hybrid" and then "A"
-
Option in
HybridStateMachine
to run custom code before and after theOnEnter
/OnLogic
/ ... of its active sub state. Previously, you could only add a custom callback that was run after the respective methods of the sub state. When migrating to this version simply replace theonEnter
parameter withafterOnEnter
in the constructor. For examplevar hybrid = new HybridStateMachine( beforeOnEnter: fsm => print("Before OnEnter"), afterOnLogic: fsm => print("After OnLogic") // ... )
-
Feature for getting the active path in the state hierarchy: When debugging it is often useful to not only see what the active state of the root state machine is (using
ActiveStateName
) but also which state is active in any nested state machine. This path of states can now be retrieved using the newGetActiveHierarchyPath()
method:var fsm = new StateMachine(); var move = new StateMachine(); var jump = new StateMachine(); fsm.AddState("Move", move); move.AddState("Jump", jump); jump.AddState("Falling"); fsm.Init(); print(fsm.GetActiveHierarchyPath()); // Prints "/Move/Jump/Falling"
-
Option in
CoState
to only run the coroutine once. E.g.var state = new CoState(mono, myCoroutine, loop: false);
-
Option in
TransitionAfterDynamic
to only evaluate the dynamic delay when thefrom
state enters. This is useful, e.g. when the delay of a transition should be random. E.g.fsm.AddTransition(new TransitionAfterDynamic( "A", "B", t => Random.Range(2, 10), onlyEvaluateDelayOnEnter: true ));
-
canExit
feature inState
andCoState
: The customcanExit
function that determines when the state is ready to exit to allow for another transition is now called on every frame when a transition is pending and not onlyOnExitRequest
. This is more intuitive and can therefore prevent some unexpected behaviour from emerging. -
The constructor of the
CoState
class now also allows you to pass in an IEnumerator function, that does not take theCoState
as a parameter, as the coroutine -
More documentation for classes / parameters / ... directly visible in the IDE
-
Internal refactors making the code easier to understand and read
- Important: The namespace of UnityHFSM has changed from
FSM
toUnityHFSM
. This means that you have to useusing UnityHFSM
now. - The parameters
onEnter
,onLogic
, ... in the constructor of theHybridStateMachine
class are now equivalent to the new parametersafterOnEnter
,afterOnLogic
, ... - The
onLogic
parameter in the constructor of theCoState
class is now calledcoroutine
, is the second parameter, and no longer optional.
- Multiple bugs relating to delayed transitions (pending transitions system)
-
Action system to allow for adding and calling custom functions apart from
OnLogic
.E.g.
var state = new State() .AddAction("OnGameOver", () => print("Good game")) .AddAction<Collision2D>("OnCollision", collision => print(collision)); fsm.AddState("State", state); fsm.Init(); fsm.OnAction("OnGameOver"); // prints "Good game" fsm.OnAction<Collision2D>("OnCollision", new Collision2D());
-
Two way transitions: New feature that lets the state machine transition from a source to a target state when a condition is true, and from the target to the source state when the condition is false:
fsm.AddTwoWayTransition("Idle", "Shoot", t => isInRange); // Same as fsm.AddTransition("Idle", "Shoot", t => isInRange); fsm.AddTransition("Shoot", "Idle", t => ! isInRange);
fsm.AddTwoWayTransition(transition); fsm.AddTwoWayTriggerTransition(transition);
-
TransitionOnMouse
classes for readable transitions that should occur when a certain mouse button has been pressed / released / ... It is analogous toTransitionOnKey
.E.g.:
fsm.AddTransition(new TransitionOnMouse.Down("Idle", "Shoot", 0));
- Improved performance in many cases for value types as the state names (e.g.
State<int>
) by preventing boxing and minimising GC allocations
-
The
RequestExit()
method of the StateBase class has been renamed toOnExitRequest()
for more clarity. -
The "shortcut methods" of the state machine have been moved to a dedicated class as extension methods. This does not change the API or usage in any way, but makes the internal code cleaner. -> This change reduces the coupling between the base StateMachine class and the State / Transition classes. Instead, the StateMachine only depends on the StateBase and TransitionBase classes. This especially shows that the extension methods are optional and not necessary in a fundamental way.
-
To allow for better testing and more customisation, references to the Timer class have been replaced with the ITimer interface. This allows you to write a custom timer for your use case and allows for time-based transitions to be tested more easily.
// Previously if (timer > 2) { } // Now if (timer.Elapsed > 2) { }
-
As a consequence of the way the action system was implemented, generic datatype of the input parameter of
onEnter
/onLogic
/onExit
forState
andCoState
has changed. The classState
now requires two generic type parameters: One for the type of its ID and one for the type of the IDs of the actions.Previously:
void FollowPlayer(State<string> state) { // ... } fsm.AddState("FollowPlayer", onLogic: FollowPlayer);
Now:
void FollowPlayer(State<string, string> state) { // ... } fsm.AddState("FollowPlayer", onLogic: FollowPlayer);
-
(Internal change) Restructured the
src
folder to make it cleaner
- Fix ArgumentNullException when using the
AddTransitionFromAny
shortcut method
Version 1.8 of UnityHFSM adds support for generics. Now the datatype of state identifiers / names and the type of event names can be easily changed. Thanks to the new "shortcut methods", state machines can be written with less boilerplate than ever and certain cases, such as empty states, can be optimised automatically for you.
-
Support for generics for the state identifiers and event names
-
"Shortcut methods" for reduced boilerplate and automatic optimisation
fsm.AddState("FollowPlayer", new State( onLogic: s => MoveTowardsPlayer() )); // Now fsm.AddState("FollowPlayer", onLogic: s => MoveTowardsPlayer());
fsm.AddState("ExtractIntel", new State()); // Now fsm.AddState("ExtractIntel");
fsm.AddTransition(new Transition("A", "B")); // Now fsm.AddTransition("A", "B");
-
Support for installing the package via Unity's Package Manager UPM
-
Project samples
-
The datatype of the input parameter of
onEnter
/onLogic
/onExit
forState
has changed. This is due to the inheritance hierarchy and the way generic support was added to the codebase while still trying to retain the ease of use of the string versions.Previously:
void FollowPlayer(State state) { // ... } fsm.AddState("FollowPlayer", new State(onLogic: FollowPlayer));
Now:
void FollowPlayer(State<string> state) { // ... } fsm.AddState("FollowPlayer", new State(onLogic: FollowPlayer));
-
States and transitions no longer carry a reference to the MonoBehaviour by default.
-
Now the constructor of
StateMachine
does not require mono anymore =>new StateMachine()
instead ofnew StateMachine(this)
-
The reference to mono has to be passed into the
CoState
constructor =>new CoState(this, ...)
-
-
Fix
KeyNotFoundException
being thrown when an event is activated while no active trigger transition use this event -
Fix incorrect order of events on state changes, which called the
OnEnter
method before the new active transitions / trigger transitions had been loaded