Advertisement

Better implementation for FSM

Started by February 17, 2006 12:40 AM
7 comments, last by CosmosChen 18 years, 9 months ago
Hi guys~ I have this question recently. I want to have a FSM architecture that is reuseable, easy to debug, easy to add new behaviors. I have tried several ways but all of them has defects. I can't find a good solution on it. So, if you have any idea, help me please...What do you do in your project? What is a better design for FSM in game AI? I know it depend on what your game is, but I think there should be a common way or templet way to handle it. In my understanding, there are several ways to implement a FSM. 1. Use switch case: This is the basic one but not good. I use this in my first game. The code looks bad, but it is easy to trace the state flow. 2. Creat these base classes CState, CTransition, CCondition and derive class from them. The CState contains a CTransition table. The CTransition contains the output state and CConditions. The CCondition is for checking any kind of thing you want. If you need a NPC begin at wandering and attack any player when he see a player. So, A CWander state can derived from CState and insert a CTransion to it. The CTransition's output state is CAttack and has a CSeeingPlayerConditon. In this case, all behavior codes OnEnterState(), OnUpdateState(), and OnExitState() are implement inside those children classes. I have tried this, but it is hard to debug and has data problems. It is difficult to share data between states. 3. Use function pointer. I know this from the "AI Game Wisdon 2--A Data Driven FSM". This is similar to the above one. The only different is all the behavior codes are implement in a class. The CState here has three function pointers. You can register all the OnEnterXXXState(), OnUpdateXXXState(), and OnExitXXXState() functions from a class. This means all behavoir codes are inside a class. It makes debug become easy and I can get data easy. This seems to be a good way.
I would use asimpler version of 2), but I dont understand your problem with sharing data between the states. I think you are putting too much responsability to your FSM. The States should'nt hold any data except references/pointer to their own transitions. The FSM should *only* encapsulate what you said: States, Transitions/Conditions.

Personally, I like Event-drived programming, I would replace condition for events. (async vs sync...) You feed an event to the FSM, and the FSM check if any of the Transitions for the current state apply. If it does, change to the new State.

Thats all!

The rest of the AI should interract with the FSM in only two ways:

-Query what is the current state
-Feed events.

That way you can keep your FSM nice and general.

Now about the "more simple" part:
To avoid code/class explosion, uses two enums instead of all the classes:

-one enum for every state possible
-one enum for every event.

Then your FSM only need contain:

-The current State
-An array of Transitions as triplets: (Original State, Event, New State).

With an appropriate data structure, it'll be efficient, and can be applied everywhere: For the AI, the engine logic, even the UI.

good luck!

Advertisement
boost::graph
check out the article at my website:

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

Quote: Original post by Steadtler
I would use asimpler version of 2), but I dont understand your problem with sharing data between the states. I think you are putting too much responsability to your FSM. The States should'nt hold any data except references/pointer to their own transitions. The FSM should *only* encapsulate what you said: States, Transitions/Conditions.

Personally, I like Event-drived programming, I would replace condition for events. (async vs sync...) You feed an event to the FSM, and the FSM check if any of the Transitions for the current state apply. If it does, change to the new State.

Thats all!

The rest of the AI should interract with the FSM in only two ways:

-Query what is the current state
-Feed events.

That way you can keep your FSM nice and general.

Now about the "more simple" part:
To avoid code/class explosion, uses two enums instead of all the classes:

-one enum for every state possible
-one enum for every event.

Then your FSM only need contain:

-The current State
-An array of Transitions as triplets: (Original State, Event, New State).

With an appropriate data structure, it'll be efficient, and can be applied everywhere: For the AI, the engine logic, even the UI.

good luck!


Thanks a lot. Let me make sure about it. Do you mean the behavior in a state is coded outside the state class? The state mechine only return the current state id and then the caller can do something depend on this id, right?

Thats correct, at least it is the style I favor. Keep it nice, simple, generic. You *can* encode some information in the States if you wish, but it should be totally invisible to the FSM.

Ideally the FSM should be a template class...
Advertisement
Quote: Original post by fup
check out the article at my website:

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


Wow~ Thanks a lot. In your web page, you check conditions and then switch to another state inside the state execute function. I have a question about this. Do you think that is a good idea to seperate the condition of transition out of the state? I mean to create another class call CCondition and call it's execute function to check conditions. If it is success, it can return true and switch to another state. So, if I have this, I can combine states with different conditions. This make states more reuseable, right?
Quote: Original post by CosmosChen
Quote: Original post by fup
check out the article at my website:

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


Wow~ Thanks a lot. In your web page, you check conditions and then switch to another state inside the state execute function. I have a question about this. Do you think that is a good idea to seperate the condition of transition out of the state? I mean to create another class call CCondition and call it's execute function to check conditions. If it is success, it can return true and switch to another state. So, if I have this, I can combine states with different conditions. This make states more reuseable, right?


It would be a good idea. You could have two states with the same behavior but with different transitions. For example, the states "Paralyzed" and the State "Sleeping".

Also consider what is more efficient between synchronous and asynchronous. Having all of the current state transition check for positive at each iteration, or checking the if the current state's transitions apply each time something happens? Depend on your game.
Through the web page. Right now I realize that it is almost the same to use a state class or a function to implement the behavior inside a state.
Here is what I think a good FSM should have:

1. Move out condition checks of states.
This can let whatever states or conditions more reuseable and the user can combine them.

2. The condition can handle both pull driven and event driven.
I found that we always need to check something in each update cycle and I also found that we always need to driven by a event that happen in the game world.

3. A Global state or something more, a Hierarchical FSM.
There are always need to do the same checks in states. So, it is better to put these condition checks inside a global state. This is quite similar to a HFSM or it is a HFSM??

Do you think so?

This topic is closed to new replies.

Advertisement