Games often have a variety of AI behavior, such as the monster cut people, players and so on automatic hanging machine. Suppose there is such a simple scenario
A monster in the scene usually patrols at the mouth of the cave. When encountering an enemy, if it is stronger than the other, beat the enemy, if weaker than the enemy, then bypass the escape.
Using the switch or if statement, it is easy to achieve the above requirements
Enum State {
Patrol,
Runaway,
Attack
};
void OnUpdate (state currstate) {
switch (currstage) {case
Patrol: {
if (Seeenemy ()) {
if ( Strongerthanenemy) {
changestate (Attack);
} else {
changestate (runaway);
}
}
break;
Case Attack: {
//fast die
if (Willdiesoon ()) {
changestate (runaway);
} else {
if (Enemydie ()) { C22/>changestate (Patrol);
}
break;
Case runaway: {
if (Safenow ()) {
changestate (Patrol);
}
break;
}}}
At first glance, it's reasonable to achieve this. But in practical applications, this code becomes a nightmare if it encounters a state of complexity.
There are two simpler ways to implement the game AI. One is the behavior tree, also called the decision tree. The other is the method we are going to talk about today, the finite state machine.
A finite state machine is an intelligent body with a finite number of states. To accept certain events in a given state, you can switch from one state to another state. A finite state machine that can only be in one state at any time.
The simplest state machine can be understood by the light switch. There are two states of light, on or off. In the open state to accept the Click event, will be cut to the closed state, in the state of the switch to accept the Click event, will be switched to open state.
State Transformation Table
One way to organize and influence state transitions is to develop a state transformation table.
In the design of AI, it is important to understand the state of the transformation process, otherwise it is easy to make the agent into a certain state and can not go out.
Here we use the "state mode" in the design pattern to implement the code for the finite state machine.
For each state, we need at least three methods.
Public interface State {
/**
* Switch to New status
* @param creature
/void OnEnter (creature creature);
/**
* Leave the current state
* @param
creature
/void OnExit (creature creature);
/**
* Every tick running business
* @param creature
/void execute (creature creature);
}
Gives a state implementation of an attack, with other status codes similar
public class Attackstate implements state {
@Override public
void OnEnter (creature creature) {
//Enter attack status
}
@Override public
void OnExit (creature creature) {
//Leave attack State
}
@Override public
void Execute ( Creature creature) {
player player = (player) creature;
Scene Scene = Player.getscene ();
Monster Monster = Scene.getmonster ();
PLAYER.CHANGEHP (-monster.getattack ());
MONSTER.CHANGEHP (-player.getattack ());
System.err.println ("Meet the enemy, quickly use a double-cut stick, hum ha xi." "
+" Our blood quantity ["+ PLAYER.GETHP () +"] "
+" enemy blood volume ["+ MONSTER.GETHP () +"] ");
}
State switching rules (transition abstract classes) require binding start and end states, and abstract methods to determine whether the agent can be converted in its current state.
Public abstract class Transition {
/** start state * * from
;
/** End Status *
/private state to;
Public Transition {
this.from = from;
This.to = to;
}
/** *
Condition Determination
* @param creature
* @return
/Public Abstract Boolean meetcondition (creature creature);
Public State fromstate () {return
this.from;
}
Public State tostate () {return
this.to
}}
gives a state of implementation, other code similar to
public class Attack2runtransition extends Transition {public
attack2runtransition
Super (from, to);
@Override Public
Boolean meetcondition (creature creature) {
//If you are currently in an attack state and have a lower damage than a monster, then run for your escape
Player Player = (player) creature;
Scene Scene = Player.getscene ();
return PLAYER.GETHP () < //dying
| | player.getattack () > Scene.getmonster (). Getattack (
) | | Math.random () < 0.4 ;//probability of escape, increase random event
}
}
Finite state machine (Agent business performer)
public class Finitestatemachine {private state initstate;
Private State currstate;
/** various states and corresponding conversion rules * * Private map<state, list<transition>> state2transtions = new hashmap<> ();
/** in order to support AI pause * * Private volatile Boolean running = true;
/** Recovery AI Timeout time * * Private long freezetimeout; public void Addtransition (Transition Transition) {list<transition> transitions = state2transtions.get (
Transition.fromstate ());
if (transitions = = null) {transitions = new arraylist<> ();
State2transtions.put (Transition.fromstate (), transitions);
} transitions.add (transition);
Public State getinitstate () {return initstate;
public void Setinitstate (state initstate) {this.initstate = initstate;
public void Enterframe (creature creature) {if (this.currstate = = null) {this.currstate = this.initstate;
This.currState.onEnter (creature);
} set<string> passed = new hashset<> (); String Clazzname = this.currState.geTclass (). GetName (); for (;;) {if (!running) {if (freezetimeout > 0 && system.currenttimemillis () > Freezetimeout) {runnin
G = true;
} else {break;
} this.currState.execute (creature);
if (Passed.contains (Clazzname)) {break;
} passed.add (Clazzname);
list<transition> transitions = state2transtions.get (this.currstate); for (Transition transition:transitions) {if (transition.meetcondition (creature)) {This.currState.onExit (Creatur
e);
This.currstate = Transition.tostate ();
This.currState.onEnter (creature); /** * Suspend AI * @param timeout */public void freeze (long timeout) {this.freezetimeout = System.c
Urrenttimemillis () + timeout; }
}
Sample code
public class Aitest {public static void main (string[] args) throws Exception {player player = new player (100, 15);
Monster Monster = new Monster (120, 10);
Scene Scene = new Scene ();
Scene.setplayer (player);
Scene.setmonster (Monster);
Player.setscene (Scene);
Monster.setscene (Scene);
state patrolstate = new Patrolstate ();
state attackstate = new Attackstate ();
state runstate = new Runawaystate ();
Transition transition1 = new Patrol2attacktransition (patrolstate, attackstate);
Transition transition2 = new Attack2runtransition (attackstate, runstate);
Transition Transition3 = new Atttack2patroltransition (attackstate, patrolstate);
Transition transition4 = new Run2patroltransition (runstate, patrolstate);
Finitestatemachine FSM = new Finitestatemachine ();
Fsm.setinitstate (patrolstate);
Fsm.addtransition (Transition1);
Fsm.addtransition (Transition2);
Fsm.addtransition (Transition3);
Fsm.addtransition (TRANSITION4);
while (true) { Fsm.enterframe (player);
Thread.Sleep (500); }
}
}
Code run results (limited to space, some screenshots are not visible)
Additional auxiliary Code
Biological Class (creature), the player and the monster of the parent class
Public abstract class Creature {
protected long hp;
protected int attack;
Private Scene Scene;
Public creature (long hp, int attack) {
this.hp = hp;
This.attack = attack;
}
Public long GETHP () {return
hp;
}
public void Sethp (long hp) {
this.hp = hp;
}
public void changehp (long changehp) {
this.hp + = CHANGEHP;
}
public int Getattack () {return
attack;
}
public void Setattack (int attack) {
this.attack = attack;
}
Public Scene Getscene () {return
Scene;
}
public void Setscene (Scene Scene) {
this.scene = Scene;
}
public Boolean Isdie () {return
this.hp <= 0;
}
}
Player class. Monster class is similar to it, change a name
Public class player extends creature {public
player (long hp, int attack) {
super (HP, attack);
}
@Override public
String toString () {return
"Player [hp= + hp +", attack= "+ attack +]";
}
}
Scene class
public class Scene {
private player player;
Private Monster Monster;
Public player Getplayer () {return
player;
}
public void Setplayer (player player) {
this.player = player;
}
Public Monster Getmonster () {return
Monster;
}
public void Setmonster (Monster Monster) {
this.monster = Monster;
}
}
layered finite state machine
If a state itself is made up of a small system, we can use a layered finite state machine (hierarchicalfinitestatemachine) for ease of management. For example, the attack status can be subdivided to track enemy-> selection skills-> combat. It's not going to start here.