// you’re reading...

Actionscript 3.0

AS3 Finite State Machine

Why the heck do we need libraries like this?! The answer is simple, organization. It’s nice to come back to a project years later and know exactly what’s going on. Now the concept of a Finite State Machine seemed a bit abstract to me when I first read about it. My research lead me to believe it was exactly what I needed.

The need for libraries like this hit home a few days ago when I was building a playlist plugin for OSMF. The display object I was working with had a bunch of children that came in and out of the screen in an animated fashion. It occurred to me this display object had more than one state. An Initial state that shows up when first run, and an open state that happened once I rolled over a hotspot.

So, I was reminded of a library I built a couple of years ago. I always meant to share it with the community. I decided to scavenge around my old files. Once I found it, I dusted it off and added a couple more things to it. So with out further a due, here it is.

WHAT IS IT?

It’s a library of code that lets you organize the different states of a class. I’ve only ever used it for display object but I guess you could use it for other things as well. It’s super simple and easy to follow. I’ve also made it as light weight as possible. There’s no need to extend your class but I’ve included two helper classes to do just that.

HOW TO USE IT

The first thing you need to do is decide your class is in need of state management. Then create an instance of StateManager and pass it the class you will be managing. This example will have a simple square on the upper left hand corner. Later, we’ll use that square to demonstrate the different states of our Sprite.

package
{
	import com.meekgeek.statemachines.finite.manager.StateManager;
 
	import flash.display.Sprite;
 
	public class FSMTest extends Sprite
	{
		public var square:Square;
 
		private var _manager:StateManager;
 
		public function FSMTest()
		{
			square = new Square();
 
			this.addChild( square )
 
			_manager = new StateManager(this);
		}
	}
}
 
import flash.display.Sprite;
 
class Square extends Sprite
{
	public function Square()
	{
		this.graphics.beginFill(0x00)
		this.graphics.drawRect(0,0,50,50);
		this.graphics.endFill();
	}
}

Then start adding your states. In order to do just that, you’ll have to create a class that extends State. These state classes will be where all of your management code goes in. In the background, these state classes will be told what the context ( in our example FSMTest ) will be. Once added, the StateManager will call three function of that state class. “doIntro”, “action”, and “doOutro” at different times of the process. “doIntro” gets called at the beginning of the state. “Action” once the “doIntro” is complete and “doOutro” is called when moving away from a state. In order to move the states along, it’s your responsibility to dispatch a couple of events to the StateManager. The State class, which you have extended has a few helper methods to help you do this. We’ll go over that in just a bit.

I like to keep the states of my display in a separate folder. So, first create a states folder next to your display class. Then create your InitState that extends State.

package states
{
	import com.meekgeek.statemachines.finite.states.State;
 
	public class InitState extends State
	{
		public function InitState()
		{
			super();
		}
	}
}

Now lets add some code. In the Initial State, our square will be centered in the middle of the stage. So, we add the code to do just that in the “doIntro” method. I’ll be tweening the square over to the middle with a popular Tween engine, once the animation is complete, I’ll dispatch my “StateEvent.ON_INTRO_COMPLETE” StateEvent. I can do this by just calling the protected function “signalIntroComplete”.

package states
{
	import com.greensock.TweenMax;
	import com.meekgeek.statemachines.finite.states.State;
 
	public class InitState extends State
	{
		public function InitState()
		{
			super();
		}
 
		override public function doIntro():void
		{
			TweenMax.to( 
				FSMTest( this.context ).square, 
				.5, 
				{ 	
					x:300, 
					y:300, 
					onComplete:this.signalIntroComplete 
				} 
			);
		}
	}
}

Once in that state, let’s color, the square, red. We only want this to happen once we’re in the action section of our state. The action status is given to a state once it’s “doIntro” method is complete and no other states have set. Otherwise, the action is skipped and “doOutro” is called immediately.

override public function action():void
{
	var ct:ColorTransform = new ColorTransform();
		ct.color = 0xFF0000;
 
	FSMTest( this.context ).square.transform.colorTransform = ct;
}

That’s it, we’ve created our first State, now lets add it the StateManager back in our FSMTest class.

_manager.addState( "init", new InitState(), true );

You’ll notice, the “addState” method accepts three parameters. The first is a key, this is how we reference our states from now on. The second is an instance of the state we just created. The third method is optional, it lets the StateManager know whether or not to use the state you just created as the initial state. Because we do, we set it to true.

It’s best practice to set our key as a constant in our State. This will remove any chance of misspellings in the future. So lets do that,

Add this to the properties section of your InitState class.

static public const KEY:String = "init";

and change our code in our managed display to represent that.

_manager.addState( InitState.KEY, new InitState(), true );

All we have to do now, is create another state to demonstrate. The following state moves the square to the right side of the stage at 400px by 100px and colors the square blue. I’ve called it ‘BlueState’.

package states
{
	import com.greensock.TweenMax;
	import com.meekgeek.statemachines.finite.states.State;
 
	import flash.geom.ColorTransform;
 
	public class BlueState extends State
	{
		static public const KEY:String = 'blue';
 
		public function BlueState()
		{
			super();
		}
 
		override public function doIntro():void
		{
			TweenMax.to( 
				FSMTest( this.context ).square, 
				.5, 
				{ 	
					x:400, 
					y:100, 
					onComplete:this.signalIntroComplete 
				} 
			);
		}
 
		override public function action():void
		{
			var ct:ColorTransform = new ColorTransform();
			ct.color = 0x0000FF;
 
			FSMTest( this.context ).square.transform.colorTransform = ct;
		}
	}
}

Now lets add it the the StateManager.

_manager = new StateManager(this);
_manager.addState( InitState.KEY, new InitState(), true );
_manager.addState( BlueState.KEY, new BlueState() );

Run the example and you’ll see that our square immediatly goes to the Initial state and turns red. That the basics of it, pretty easy right.

So now, lets change states. We’ll do this by making square a button and when clicked will move our square to the ‘BlueState’.

square.buttonMode = true;
square.addEventListener( MouseEvent.CLICK, changetoBlueState );

Now lets create the “changeToBlueSate” function and call the “setState” method on our StateManager.

private function changeToBlueSate(e:MouseEvent):void
{
	_manager.setState( BlueState.KEY );
}

Run the code again, and then click on our new button. So far so good. Let’s now make away to go back to the InitState when we get to BlueState. The StateManager dispatches events at all parts of a state change. They are “onIntroStart”, “onIntroComplete”, “onAction”, “onOutroStart”, and “onOutroComplete”. The event fired has reference to State key. We’ll use this to remove the current listener to move to blue and replace it with one to move to Init.

_manager.addEventListener( StateManagerEvent.ON_OUTRO_START, onOutroStart );
_manager.addEventListener( StateManagerEvent.ON_INTRO_START, onIntroStart );
private function onOutroStart(e:StateManagerEvent):void
{
	switch(e.key)
	{
		case InitState.KEY:
			square.removeEventListener( MouseEvent.CLICK, changeToBlueSate );
		break;
	}
}
 
private function onIntroStart(e:StateManagerEvent):void
{
	switch(e.key)
	{
		case BlueState.KEY:
			square.addEventListener( MouseEvent.CLICK, changeToInitState );
		break;
	}
}

Run the code again and play with it.

ADDING SOME POLISH

I’ve included some helper class you can extend if you’re going to be using Movieclips or Sprites. They initialize the StageManger and even listen for all the different events that get fired. Just override some protected function in order to add and remove event listeners based on the state you’re in.

WHAT NOW?

The possiblities our endless. For games, you can use this for character animations. You don’t have to use tweens, you can control frames in your states using the Movieclip methods. I’ve built really neat looking menus and the animation of pages loading in an out. I love this library and hope that you’ll love it too. It’s really up to you how you want to use it.

Download the code here.

Discussion

2 comments for “AS3 Finite State Machine”

  1. Hey joel how do you use the helper classes to use sprites and movieclips? I would like my states to extend sprites but i am not quite sure how to do it. Do you have any help or sample code on how to implement them? Thanks in advance! Great work btw!

    Posted by Rz | November 2, 2011, 9:50 PM
  2. Thanks Rz, for the compliment. The helper classes “SpriteMachine” and “MovieclipMachine” are really just there because I found my self doing the same thing over and over again. Now, state classes never need to extend any kind of display object because they’re only job is to deal with the context which is what you pass to the StateManager constructor. When you use a helper class like SpriteMachine, I’m doing that for you already. All you need to do is start creating your States and add them. Inside of any State, you have reference to the context via the context protected variable. I hope this helps. I’ll email you some sample code today sometime and update this post.

    Posted by Joel Caballero | November 3, 2011, 10:33 AM

Post a comment