有限状态机(FSM)是表示有限个状态及在这些状态之间的转移和动作等行为的数学模型。在计算机领域有着广泛的应用。通常FSM包含几个要素:状态的管理、状态的监控、状态的触发、状态触发后引发的动作。
状态通常用于组织和表示着执行流,在游戏编码中可以很好的实现AI(npc)。例如,在一个对打类的游戏中,可以使用一个FSM,用每一个状态表示一个动作,如攻击或规避:
一个FSM可以已图片的方式展现,节点表示状态和边界表示转换。每个边上都有一个标签信息表示转换的条件,例如上图中玩家在附近的标签表明当玩家在附近附近时当前状态将从漫步状态转换到攻击状态。
蚂蚁搬家的例子,如果蚂蚁在发现树叶后遇到鼠标光标不逃离的话,图片和源码可以以下面的方式简单的实现:
public class FSM {
private var activeState :Function; // points to the currently active state function
public function FSM() {
}
public function setState(state :Function) :void {
activeState = state;
}
public function update() :void {
if (activeState != null) {
activeState();
}
}
}
public class Ant
{
public var position :Vector3D;
public var velocity :Vector3D;
public var brain :FSM;
public function Ant(posX :Number, posY :Number) {
position = new Vector3D(posX, posY);
velocity = new Vector3D( -1, -1);
brain = new FSM();
// Tell the brain to start looking for the leaf.
brain.setState(findLeaf);
}
/**
* The "findLeaf" state.
* It makes the ant move towards the leaf.
*/
public function findLeaf() :void {
}
/**
* The "goHome" state.
* It makes the ant move towards its home.
*/
public function goHome() :void {
}
/**
* The "runAway" state.
* It makes the ant run away from the mouse cursor.
*/
public function runAway() :void {
}
public function update():void {
// Update the FSM controlling the "brain". It will invoke the currently
// active state function: findLeaf(), goHome() or runAway().
brain.update();
// Apply the velocity vector to the position, making the ant move.
moveBasedOnVelocity();
}
(...)
}
但是如果蚂蚁在发现树叶后遇到鼠标光标也逃离的话,则上述的实现就不正确了,因为在run away状态下,当mouse cursor is distant时,不知道要转换为go home状态还是find leaf状态,这时,就可以采用基于堆栈的状态机的方式来实现。
它可以自己从堆栈中弹出并入栈另一个状态,这意味着一个完整的装换(就像简单的FSM在做)。它可以自己从堆栈中弹出,这意味着完成当前状态已经完成,下一个状态已经在堆栈中等到激活。最后,它可以入栈一个新的状态,这意味着当前活动状态改变后,它从堆栈中弹出本身,之前活动的状态需要重新激活。
public class StackFSM {
private var stack :Array;
public function StackFSM() {
this.stack = new Array();
}
public function update() :void {
var currentStateFunction :Function = getCurrentState();
if (currentStateFunction != null) {
currentStateFunction();
}
}
public function popState() :Function {
return stack.pop();
}
public function pushState(state :Function) :void {
if (getCurrentState() != state) {
stack.push(state);
}
}
public function getCurrentState() :Function {
return stack.length > 0 ? stack[stack.length - 1] : null;
}
}
public class Ant {
(...)
public var brain :StackFSM;
public function Ant(posX :Number, posY :Number) {
(...)
brain = new StackFSM();
// Tell the brain to start looking for the leaf.
brain.pushState(findLeaf);
(...)
}
/**
* The "findLeaf" state.
* It makes the ant move towards the leaf.
*/
public function findLeaf() :void {
// Move the ant towards the leaf.
velocity = new Vector3D(Game.instance.leaf.x - position.x, Game.instance.leaf.y - position.y);
if (distance(Game.instance.leaf, this) <= 10) {
// The ant is extremelly close to the leaf, it's time
// to go home.
brain.popState(); // removes "findLeaf" from the stack.
brain.pushState(goHome); // push "goHome" state, making it the active state.
}
if (distance(Game.mouse, this) <= MOUSE_THREAT_RADIUS) {
// Mouse cursor is threatening us. Let's run away!
// The "runAway" state is pushed on top of "findLeaf", which means
// the "findLeaf" state will be active again when "runAway" ends.
brain.pushState(runAway);
}
}
/**
* The "goHome" state.
* It makes the ant move towards its home.
*/
public function goHome() :void {
// Move the ant towards home
velocity = new Vector3D(Game.instance.home.x - position.x, Game.instance.home.y - position.y);
if (distance(Game.instance.home, this) <= 10) {
// The ant is home, let's find the leaf again.
brain.popState(); // removes "goHome" from the stack.
brain.pushState(findLeaf); // push "findLeaf" state, making it the active state
}
if (distance(Game.mouse, this) <= MOUSE_THREAT_RADIUS) {
// Mouse cursor is threatening us. Let's run away!
// The "runAway" state is pushed on top of "goHome", which means
// the "goHome" state will be active again when "runAway" ends.
brain.pushState(runAway);
}
}
/**
* The "runAway" state.
* It makes the ant run away from the mouse cursor.
*/
public function runAway() :void {
// Move the ant away from the mouse cursor
velocity = new Vector3D(position.x - Game.mouse.x, position.y - Game.mouse.y);
// Is the mouse cursor still close?
if (distance(Game.mouse, this) > MOUSE_THREAT_RADIUS) {
// No, the mouse cursor has gone away. Let's go back to the previously
// active state.
brain.popState();
}
}
(...)
}
本文引自 http://gamedevelopment.tutsplus.com/tutorials/finite-state-machines-theory-and-implementation--gamedev-11867