Implemented a Finite State Machine, but is it the right way?

I have a finite state machine for the player and this works perfectly (so far). I am implementing another for enemies and I had a question. Right now I utilize the Update method to check for a state change. Is this right? Previously I used the dreaded if/else approach but I drove it using coroutines and yields inside the Start method using a if (true) statement.

So my question is have is using the Update method for my FSM the best way? I currently have it checking several times per second only so as not to hammer the FPS.

No - its not the best way (you almost knew that, right?) :smiley:

You should implement a transition callback - e.g. using delegates in C#, so when state changes that it can give you a signal instead of you polling all the time. Waste of CPU cycles

/Thomas

This book gives some good examples of how to create state machines:

I’m using something like this in Javascript:

var state = "InitState";
function Start() {
 while (true) yield new StartCoroutine(state);
}

where each coroutine/state transitions by setting the next state name and returning.

I played around earlier with using entire scripts as individual states, with the OnEnable/Disable implementing the entry/exit actions and Update executing the state, but that was pretty bulky.

Thanks, technicat, for that helpful bit of code.

I am doing the same thing you were doing - using entire scripts as states and enabling/disabling as the states change (as described in the book by Matt Buckland - Programming AI by example).

I’m still learning to code and I thought that coroutines might be an option but I never used them before so I had difficulty trying it out.

I wonder if you could post any additional code snippets to help me understand. For instance, is “InitState” the function name? Is all your state code contained in the same script? Can coroutines return a value? How do you transition to the new state? A code snippet for that is what I really hope to see.

Sorry for the noob questions – still learning about OOP and unity.

Thanks much for any help with this.

Mitch

Yes, yes, no, and here’s a snippet - InitState is a coroutine implementing a state and before it returns, it specifies the next state, StartPlaying. I don’t know if there are any problems with this approach since I just started using it (and I haven’t tried it on the iPhone), but it is convenient to have the whole state machine in one script (especially for shared variables), you can freely use the “yield” instruction", and it seems very readable.

function InitState() {
 InitScores();
 guiText.text = "Welcome to my game! Click to play";
 yield new WaitForSeconds(5);
 while (!Input.GetButton("fire1")) {
   yield;
 }
 state="StartPlaying";
}

Edit: just remembered, I blogged a snippet last week! 域名已过期,无法正常使用 Scroll to the end of the post, there’s just one more small example state there, but it shows branching to different states from a single state.

Delegates are one way to do it. But by no means the only way.

For example, I use a State Transition Table. So when the state is updated by calling SetState(), it tells the FSM to evaluate the transition and handles everything, including throwing events whenever a state changes.

Thanks - this is very helpful. Much appreciated!

Thanks guys great replies. I am actually using the book by Matt Buckland - Programming AI by example. So I have all my states and state machine set up. My question (which I believe was answered) was where is the best place to “poll” for state changes. I was currently using the update method. I am not sure how I would use delegates as an event drives a delegate being called, but in this case I need to be “checking” for those events (using the FSM) in order to change a state (such as the player has moved too close to an enemy so its state has changed from idle to chasing.

So if what I heard is correct it is better to use the Start method using a “while (true)” coroutine method over using the Update method?

so I guess I am saying I was doing it technicat’s way, and I guess I should go back to that way?

If you have a method that is called to change the state, throw an event… “onStateChanged” or something. Use SendMessage() to notify everyone. Easier and consumes less CPU than polling.

Any chance you could post a code snippet to illustrate how this is done?

Buckland’s basic approach is OK for some purposes, but you are likely to end up with a morass of classes, each containing only a small amount of code. If you implement these as MonoBehaviour scripts, then there are some benefits, but the problem of communicating between the classes can be a bit of a pain.

A technique that has worked well for me is implementing the states as methods of a single MonoBehaviour class. Create a delegate and then make all your state methods instances of that delegate. You can actually declare variables of the delegate type - declare one called “currentState” (or whatever) somewhere in the class. All you need to do then is to call the method stored in currentState in the Update method. Often, the state methods will be responsible for selecting the next state (say, after an animation has finished or a certain time has elapsed), but you can also have additional public methods in the class that just change the value of currentState. Essentially, you don’t need an explicit polling operation when you implement states like this.

If you need a bit more functionality, you can build a small state control class around the currentState variable. You can then arrange for, say, a timer to be reset every time the state changes or have a boolean value that is set to true only on the first frame after a state change. The sky is pretty much the limit with this.

You might be better off with Mat Buckland’s approach, depending on what you are doing, but I just thought I’d share this in case it helps.

MitchStan, here’s a good site if you want to read up on state Transition tables. The solution in the tutorial isn’t elegant by any stretch, but it works and is fairly simple.

http://www.ai-junkie.com/architecture/state_driven/tut_state1.html