- Home /
How should I go about structuring a finite state machine if I need to make use of all three update functions?
Normally when I have an enemy that I need to write AI for, I'll use a finite state machine, like the one shown below:
public class SomeEnemy : Monobehaviour
{
public enum States {searchingForPlayer, chasingPlayer, attacking, recovering};
private States currentState = States.searchingForPlayer;
void Update()
{
FiniteStateMachine();
}
//Finite state machine-related functions
private void FiniteStateMachine()
{
switch (currentState)
{
case States.searchingForPlayer:
WhileSearchingForPlayer();
break;
case States.chasingPlayer:
WhileChasingPlayer();
break;
case States.attacking:
WhileAttacking();
break;
case States.recovering:
WhileRecovering();
break;
}
}
private void WhileSearchingForPlayer()
{
//Insert code here that checks to see if a player is in range
if (playerIsInRage)
{
currentState = States.chasing;
}
}
private void WhileChasingPlayer()
{
//Insert code here that checks to see if the player is within attacking distance
if (caughtUpToPlayer)
{
currentState = States.attacking;
}
}
private void WhileAttacking()
{
//Attacking animation here
if (animationIsOver)
{
currentState = States.recovering;
}
}
private void WhileRecovering()
{
//Insert code here that waits for a while and then goes back to the searching state
if (doneRecovering)
{
currentState = States.searchingForPlayer;
}
}
}
In most cases, this works really well. It helps me turn big problems into smaller, more manageable problems. Unfortunately, is has an obvious downside: it ends up making the code REALLY long. It's still pretty manageable when I only have a few states and only make use of the normal Update() method.
But recently, I've run into a problem: I have an object that requires me to use all three of the update methods for each state. This means that when I have to add a new state, I need to create not one, but THREE methods devoted to this one state: one for Update(), one for FixedUpdate(), and one for LateUpdate(). I then have to remember to add each of those methods to TRHEE different switch-statements. With one or two states, I can manage it. But when I need to add four or more states, it becomes really messy really quickly.
It would turn the previous example into one looking like this:
public class SomeEnemy : Monobehaviour
{
public enum States {searchingForPlayer, chasingPlayer, attacking, recovering};
private States currentState = States.searchingForPlayer;
void Update()
{
FiniteStateMachine_NormalUpdate();
}
void FixedUpate()
{
FiniteStateMachine_FixedUpadate();
}
void LateUpdate()
{
FiniteStateMachine_LateUpdate();
}
//Finite state machine-related functions
private void FiniteStateMachine_NormalUpdate()
{
switch (currentState)
{
case States.searchingForPlayer:
WhileSearchingForPlayer_NormalUpdate();
break;
case States.chasingPlayer:
WhileChasingPlayer_NormalUpdate();
break;
case States.attacking:
WhileAttacking_NormalUpdate();
break;
case States.recovering:
WhileRecovering_NormalUpdate();
break;
}
}
private void FiniteStateMachine_FixedUpdate()
{
switch (currentState)
{
case States.searchingForPlayer:
WhileSearchingForPlayer_FixedUpdate();
break;
case States.chasingPlayer:
WhileChasingPlayer_FixedUpdate();
break;
case States.attacking:
WhileAttacking_FixedUpdate();
break;
case States.recovering:
WhileRecovering_FixedUpdate();
break;
}
}
private void FiniteStateMachine_LateUpdate()
{
switch (currentState)
{
case States.searchingForPlayer:
WhileSearchingForPlayer_LateUpdate();
break;
case States.chasingPlayer:
WhileChasingPlayer_LateUpdate();
break;
case States.attacking:
WhileAttacking_LateUpdate();
break;
case States.recovering:
WhileRecovering_LateUpdate();
break;
}
}
private void WhileSearchingForPlayer_NormalUpdate()
{
//Code
}
private void WhileSearchingForPlayer_FixedUpdate()
{
//Code
}
private void WhileSearchingForPlayer_LateUpdate()
{
//Code
}
private void WhileChasingPlayer_NormalUpdate()
{
//Code
}
private void WhileChasingPlayer_FixedUpdate()
{
//Code
}
private void WhileChasingPlayer_LateUpdate()
{
//Code
}
//And so on and so forth
}
As you can see, it very quickly becomes an ungodly amount of code for me to keep updated and organized.
So here's my question: What is a smarter way for me to do this? Is there some clever way I can have a finite state machine that allows me to put different code in all three update methods for each state, without needing to write tons and tons of short methods? Is there any way I can keep all of the code for my states separated and organized?
Answer by spraycanmansam · Apr 12, 2014 at 01:36 AM
I would suggest checking out the FSM tutorials on the Unity Gems website (or you might get lucky and the author 'whydoidoit' might chip in with some advice). The framework follows a similar design to your last example, but a bit more manageable. You define an enum with your states and create your state methods according to a naming convention. You would end up with something like --
public class SomeEnemy : StateMachineBehaviourEx
{
public enum State {
Searching,
Chasing,
}
private void Start() {
currentState = State.Searching;
}
private void Searching_Update() {
// code..
}
private void Searching_FixedUpdate() {
// code..
}
private void Chasing_Update() {
// code..
}
private void Chasing_FixedUpdate() {
// code..
}
}
..there is no need to create a 'base' Update or FixedUpdate functions, as the FSM framework handles that behind the scenes with delegates and reflection and will call and cache the appropriate Update method for the current state.
What FS$$anonymous$$ framework are you talking about? Why would I not need a "base" Update function?
You can download it at the end of the Unity Gems FS$$anonymous$$ tutorial --- http://unitygems.com/finite-state-machines-3-final-state-machine-framework/
Short version is that the State$$anonymous$$achineBehaviourEx class that you inherit from handles what functions are called for which state, you just need to declare them according to it's set na$$anonymous$$g convention. It uses the na$$anonymous$$g convention State_$$anonymous$$ethod. For eg: Searching_Update(), Chasing_FixedUpdate(), etc. To use the framework in it's simplest form, that's about the only rule you need to adhere to. There is more to it - like how it uses delegates and reflection to find, cache and call the appropriate method for your state, but it's not essential to know to use the framework in its base form. If you open up the State$$anonymous$$achineBehaviourEx class you will find the default Unity methods that you can use. I'm fairly sure once you see how it works you would have no trouble extending it if you require more non-Unity specific state methods.
The best way is to download it and have a play. It's easy to use and it really is a great framework. It's saved me a lot of time writing the same thing I would have anyway and you can strip it down or extend it easily.
Your answer
Follow this Question
Related Questions
is it possible change picture or video content once published? 0 Answers
Script wait for 3 seconds before running - stops it working altogether 2 Answers
Is there a yield WaitForSeconds type code for the update? 2 Answers
Problem with animation and void fixedupdate() 1 Answer
My Script Won't Update 1 Answer