The FSM framework described in part 1 requires the user to make action classes. These action classes can then be reused in any state. This makes the framework very extensive and flexible. This is actually my favorite feature in Playmaker. Playmaker comes with a wide collection of premade action classes. At the same time, you can also add your own actions by extending a base class and the FSM editor can pick up these new actions. A framework of my own has to have the same system.

The following is one of my generic actions:

public class MoveAction : FsmAction { private Transform transform; private Vector3 positionFrom; private Vector3 positionTo; private float duration; private string timeReference; private string finishEvent; private Space space; private CountdownTimer timer; public MoveAction(FsmState owner) : base(owner) { this.timer = new CountdownTimer(1); // uses default time reference } public MoveAction(FsmState owner, string timeReferenceName) : base(owner) { this.timer = new CountdownTimer(1, timeReferenceName); } public void Init(Transform transform, Vector3 positionFrom, Vector3 positionTo, float duration, string finishEvent, Space space = Space.World) { this.transform = transform; this.positionFrom = positionFrom; this.positionTo = positionTo; this.duration = duration; this.finishEvent = finishEvent; this.space = space; } public override void OnEnter() { if(Comparison.TolerantEquals(duration, 0)) { Finish(); return; } if(VectorUtils.Equals(this.positionFrom, this.positionTo)) { // positionFrom and positionTo are already the same Finish(); return; } SetPosition(this.positionFrom); timer.Reset(this.duration); } public override void OnUpdate() { timer.Update(); if(timer.HasElapsed()) { Finish(); return; } // interpolate position SetPosition(Vector3.Lerp(this.positionFrom, this.positionTo, timer.GetRatio())); } private void Finish() { // snap to destination SetPosition(this.positionTo); if(!string.IsNullOrEmpty(finishEvent)) { GetOwner().SendEvent(finishEvent); } } private void SetPosition(Vector3 position) { switch(this.space) { case Space.World: this.transform.position = position; break; case Space.Self: this.transform.localPosition = position; break; } } }

It’s basically an action that moves an object from point A to B at some duration. It also accepts a “Finished” event such that when the action is done, it sends/triggers this event towards the owning FSM which may cause a state change.

This is how it can be used:

private void PrepareFsm() { // States FsmState goHome = fsm.AddState("GoHome"); ... // GoHome actions { MoveAction move = new MoveAction(goHome); float duration = homeDistance / this.moveSpeed; // Calculate duration by speed move.Init(this.transform, this.transform.position, homePosition, duration, FINISHED); goHome.AddAction(move); } ... }

There are a lot of generic actions with this flavor. I have actions like RotateToTarget, ScaleByDuration, SetColorByDuration, TimedWait, etc. The good thing is I only have to write these actions once and they can be reused in any game.

Generic Delegate Action

So far I’ve shown that writing reusable action classes are good. They can also be bad. One of my gripes in Playmaker is making action classes that only uses single calls to other components. In my framework, I used to write action classes like this:

public class Fire : FsmAction { private Ship ship; private string finishEvent; public Fire(FsmState owner, Ship ship, string finishEvent) : base(owner) { this.ship; this.finishEvent = finishEvent; } public override void OnEnter() { ship.Fire(); GetOwner().SendEvent(this.finishEvent); } }

The only significant call here is ship.Fire(). But since I have to interface with the framework, I have to make a new class for it. Not only is this verbose, it kills productivity. This single line calls in an FSM happens a lot.

To fix this, I made a generic action that accepts delegates for its OnEnter(), OnUpdate(), and OnExit() routines. This way, I could call one liners such as ship.Fire() through an anonymous method thus avoiding making a new class altogether.

public class FsmDelegateAction : FsmAction { public delegate void FsmActionRoutine(FsmState owner); private FsmActionRoutine onEnterRoutine; private FsmActionRoutine onUpdateRoutine; private FsmActionRoutine onExitRoutine; public FsmDelegateAction(FsmState owner, FsmActionRoutine onEnterRoutine) : this(owner, onEnterRoutine, null, null) { } public FsmDelegateAction(FsmState owner, FsmActionRoutine onEnterRoutine, FsmActionRoutine onUpdateRoutine, FsmActionRoutine onExitRoutine = null) : base(owner) { this.onEnterRoutine = onEnterRoutine; this.onUpdateRoutine = onUpdateRoutine; this.onExitRoutine = onExitRoutine; } public override void OnEnter() { if(onEnterRoutine != null) { onEnterRoutine(GetOwner()); } } public override void OnUpdate() { if(onUpdateRoutine != null) { onUpdateRoutine(GetOwner()); } } public override void OnExit() { if(onExitRoutine != null) { onExitRoutine(GetOwner()); } } }

Now instead of making a new Fire action class, I could just use this action instead:

// Say the Fsm instance is inside the Ship component private void PrepareFsm() { // States ... FsmState fire = fsm.AddState("Fire"); ... // Fire actions { fire.AddAction(new FsmDelegateAction(fire, delegate(FsmState owner) { Fire(); owner.SendEvent(FINISHED); })); } ... }

This is less verbose. I’m still interfacing with the framework but with less code. Most FSM actions don’t need to be a class of their own. If they need to be, then sure we could refactor that in another action class.

This is how a code based FSM becomes more interesting than an editor based one. You can do manipulations in code that you cannot do in an editor. You can manage the FSM better. You gain a lot of options to organize FSMs through code.