有限状态机在游戏中的应用
程序员文章站
2024-03-19 18:40:16
...
游戏里经常有各种AI行为,例如怪物砍人,玩家自动挂机等等。假设有这样的简易场景
场景里的一只怪物平时就在洞口巡逻。当遇到敌人的时候,如果比对方强大,就揍敌人;如果比敌人弱,就绕道逃跑。
用switch或if语句,很简易实现上面的需求
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: {
// 快死啦
if (willDieSoon()) {
changeState(RunAway);
} else {
if (enemyDie()) {
changeState(Patrol);
}
}
break;
}
case RunAway: {
if (safeNow()) {
changeState(Patrol);
}
break;
}
}
}
乍看一下,这样实现的合理的。但在实际应用场合中,如果碰到状态复杂的情况,这样的代码就变成了恶梦。
有两种比较简单的方法来实现游戏ai。一种是行为树,也叫决策树。另外一种,就是今天要讲的方法,即有限状态机。
一个有限状态机,是一种具有有限数量状态的智能体。在给定的状态下接受某些事件,就能从一个状态切换到另外一个状态。一个有限状态机,在任何时刻都只能处于一种状态。
最简单的状态机,可以通过灯的开关来理解。灯有两种状态,开或关。在开的状态接受点击事件,就会切换成关闭状态;在关的状态下接受点击事件,就会切换成打开状态。
状态变换表
一个用于组织状态和影响状态切换的方法就是制定一个状态变换表。
在设计AI的时候,一定要清楚状态的变换流程,不然很容易使智能体陷入某一个状态而走不出来。
下面我们使用设计模式里的“状态模式”来实现有限状态机的代码。
对于每一个状态,我们至少需要三个方法。
public interface State {
/**
* 切换至新状态
* @param creature
*/
void onEnter(Creature creature);
/**
* 离开当前状态
* @param creature
*/
void onExit(Creature creature);
/**
* 每一个tick跑的业务
* @param creature
*/
void execute(Creature creature);
}
给出一个攻击的状态实现,其他状态代码类似
public class AttackState implements State {
@Override
public void onEnter(Creature creature) {
// 进入攻击状态
}
@Override
public void onExit(Creature creature) {
// 离开攻击状态
}
@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("邂逅敌人,快使用双截棍,哼哼哈兮。"
+ "我方血量["+ player.getHp() + "]"
+ "敌方血量["+ monster.getHp() + "]");
}
}
状态切换规则(Transition抽象类),需要绑定开始状态和结束状态,以及抽象方法用于判断智能体在当前的状态下能否发生转换。
public abstract class Transition {
/** 开始状态 */
private State from;
/** 结束状态 */
private State to;
public Transition(State from, State to) {
this.from = from;
this.to = to;
}
/**
* 条件判定
* @param creature
* @return
*/
public abstract boolean meetCondition(Creature creature);
public State fromState() {
return this.from;
}
public State toState() {
return this.to;
}
}
给出一种状态的实现,其他代码类似
public class Attack2RunTransition extends Transition {
public Attack2RunTransition(State from, State to) {
super(from, to);
}
@Override
public boolean meetCondition(Creature creature) {
// 如果当前在攻击状态,且攻击力比怪物低,那就赶紧逃命吧
Player player = (Player)creature;
Scene scene = player.getScene();
return player.getHp() < 50 // 快死啦
|| player.getAttack() > scene.getMonster().getAttack()
|| Math.random() < 0.4 ; //有概率逃跑,增大随机事件
}
}
有限状态机(智能体业务执行者)public class FiniteStateMachine {
private State initState;
private State currState;
/** 各种状态以及对应的转换规则 */
private Map<State, List<Transition>> state2Transtions = new HashMap<>();
/** 为了支持ai暂停 */
private volatile boolean running = true;
/** 恢复ai超时时间 */
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) {
running = 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(creature);
this.currState = transition.toState();
this.currState.onEnter(creature);
}
}
}
}
/**
* 暂停ai
* @param timeout
*/
public void freeze(long timeout) {
this.freezeTimeOut = System.currentTimeMillis() + timeout;
}
}
示例代码
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);
}
}
}
代码运行结果(限于篇幅,部分截图不可见)
其他辅助代码
生物类(Creature),玩家与怪物的父类
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;
}
}
玩家类。怪物类与它相似,换个名字即可
public class Player extends Creature {
public Player(long hp, int attack) {
super(hp, attack);
}
@Override
public String toString() {
return "Player [hp=" + hp + ", attack=" + attack + "]";
}
}
场景类
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;
}
}
分层有限状态机
如果某个状态本身又是由一系统小状态组成的,那么为了方便管理,我们可以使用分层次的有限状态机(HierarchicalFiniteStateMachine)。例如攻击状态,可细分为跟踪敌人->选择技能->战斗。这里就不展开了。
工程git路径 --> mmorpg框架
上一篇: umi面包屑和页头配置和使用
下一篇: YOLOv3 制作并训练自己的数据集
推荐阅读
-
有限状态机在游戏中的应用
-
一文详解self-attention机制在语义分割中的应用(含论文解析)
-
在java商城开发中map集合的应用 博客分类: 互联网Java hashmaptreemapjavaobject插入
-
高斯背景模型在 监控中的应用 博客分类: 推荐系统 高斯背景模型监控
-
TWaver在电力系统中的应用 博客分类: TWaver Java powersystemSwingTWaver电力电气
-
面向接口编程在hibernate中的应用 博客分类: hibernate 编程HibernateJavaBeanDAO
-
面向接口编程在hibernate中的应用 博客分类: hibernate 编程HibernateJavaBeanDAO
-
java反射-在form提交的一些应用 JavaStrutsServletApacheJSP
-
java反射-在form提交的一些应用 JavaStrutsServletApacheJSP
-
STL在算法设计中的应用——优先队列作为堆