之前有看到过状态机、行为树这一块的内容,但是没有认真细看,现在终于静下心来认真看了看,就看了别人的实现方式(网上代码没有Java实现的),
然后使用Java语言实现了一下
运行之后,在回头看代码,果然又清晰了一些
希望写完这篇博客之后,能认识的更深刻一些,哈哈。
首先,要先感谢下这几位博主,里面的一些概念及代码都是参考这几个小哥来写的。
https://www.cnblogs.com/designyourdream/p/4459971.html
https://segmentfault.com/a/1190000012397660#articleHeader8
https://blog.csdn.net/luyuncsd123/article/details/18351137
一、先看下项目目录
代码在src下面,配置文件基本没有
结构如下,--指向文件夹 -->指向具体类
src
--com.lizhao
--ai 正式代码
--abs 各个接口的基类
--common 通用类,这里放的是枚举类
--ifs 接口
--composite 继承IComposite的接口
--impl 各种行为的实现类,最小单元
--action
--composite
--condition
-->BehaviorTree 行为树
-->BehaviorTreeBuilder 树生成类
--tree 不用看
-->Main 测试方法
任何行为都会有执行状态
package com.lizhao.ai.common; public enum EStatus { Invalid, //初始状态 Success, //成功 Failure, //失败 Running, //运行 Aborted, //终止 };二、从各个接口来看,继承关系。
IBehaviour一个最基本的节点,是一个抽象接口,所有的接口都是继承自他。
不含有子节点(叶子节点)
IAction,游戏中的具体执行操作,比如攻击,捡物品,跑路等
ICondition,游戏中用来判断的一些条件(比如是否看到玩家,地上是否有金币),成功就返回success,失败就返回分failure
含有子节点(组合、枝干节点)
IDecorator,装饰器,将子节点的操作中添加细节,比如重复操作该子节点,具体代码见下篇
IComposite,复合节点,将多个节点进行组合。
ISequence,顺序器,组合到一起的节点会顺序执行,直到全部执行完毕或者某个返回failure
比如组合ABCD节点,顺序就是A-success-B-success-C-success-D-success,
在B那里返回failure,则不会执行到C,A-success-B-failure
ISelector,选择器,组合到一起的节点会顺序执行,直到全部执行完毕或者某个返回success
比如组合ABCD节点,顺序就是A-failure-B-failure-C-failure-D-failure,
在B那里返回success,则不会执行到C,A-failure-B-success
IParallel,并行器,多个子行为同时执行,当满足条件的时候跳出 具体代码见下篇
里面的实现列出
/** * Behavior接口是所有行为树节点的核心,且我规定所有节点的构造和析构方法都必须是protected,以防止在栈上创建对象, * 所有的节点对象通过Create()静态方法在堆上创建,通过Release()方法销毁 * 由于Behavior是个抽象接口,故没有提供Create()方法,本接口满足如下契约 * 在Update方法被首次调用前,调用一次OnInitialize函数,负责初始化等操作 * Update()方法在行为树每次更新时调用且仅调用一次。 * 当行为不再处于运行状态时,调用一次OnTerminate(),并根据返回状态不同执行不同的逻辑 */ public interface IBehaviour { // // //创建对象请调用Create()释放对象请调用Release() // protected Behavior() { // setStatus(EStatus.Invalid); // } EStatus tick();//设置调用顺序,onInitialize--update--onTerminate void onInitialize();//当节点调用前 EStatus update();//节点操作的具体实现 void onTerminate(EStatus Status); //节点调用后执行 void release();//释放对象所占资源 void addChild(IBehaviour child); void setStatus(EStatus status); EStatus getStatus(); }; /************************************************************************/ /* 动作基类 /* 具体执行动作 */ /************************************************************************/ public interface IAction extends IBehaviour { } /************************************************************************/ /* 组合结点 */ import java.util.List; /************************************************************************/ public interface IComposite extends IBehaviour { void addChild(IBehaviour child); void removeChild(IBehaviour child); void clearChild(); List<IBehaviour> getChildren(); void setChildren(List<IBehaviour> behaviours); } /************************************************************************/ /* 条件基类 /* 具体执行动作 */ /************************************************************************/ public interface ICondition extends IBehaviour { boolean isNegation(); void setNegation(boolean negation); } /************************************************************************/ /* 装饰结点 /* 装饰: *********************************************************************** */ public interface IDecorator extends IBehaviour { } /************************************************************************/ /* 顺序节点 */ /*顺序器:依次执行所有节点直到其中一个失败或者全部成功位置 */ import com.lizhao.ai.ifs.IComposite; /************************************************************************/ public interface ISequence extends IComposite { } /************************************************************************/ /* 选择节点 /* 选择器:依次执行每个子节点直到其中一个执行成功或者返回Running状态 */ import com.lizhao.ai.ifs.IComposite; /************************************************************************/ public interface ISelector extends IComposite { } /************************************************************************/ /* 并行节点 /* 并行器:多个行为并行执行 */ /************************************************************************/ public interface IParallel extends IComposite { }
三、然后写了各自的基类,用于实现一些通用的方法
先看继承关系
看代码
import com.lizhao.ai.common.EStatus; import com.lizhao.ai.ifs.IBehaviour; /** * Behavior接口是所有行为树节点的核心,且我规定所有节点的构造和析构方法都必须是protected,以防止在栈上创建对象, * 所有的节点对象通过Create()静态方法在堆上创建,通过Release()方法销毁 * 由于Behavior是个抽象接口,故没有提供Create()方法,本接口满足如下契约 * 在Update方法被首次调用前,调用一次OnInitialize函数,负责初始化等操作 * Update()方法在行为树每次更新时调用且仅调用一次。 * 当行为不再处于运行状态时,调用一次OnTerminate(),并根据返回状态不同执行不同的逻辑 */ public abstract class BaseBehavior implements IBehaviour { protected EStatus status; //创建对象请调用Create()释放对象请调用Release() protected BaseBehavior() { setStatus(EStatus.Invalid); } //包装函数,防止打破调用契约 public EStatus tick() { //update方法被首次调用前执行OnInitlize方法,每次行为树更新时调用一次update方法 //当刚刚更新的行为不再运行时调用OnTerminate方法 if (status != EStatus.Running) { onInitialize(); } status = update(); if (status != EStatus.Running) { onTerminate(status); } return status; } // //释放对象所占资源 public void release(){ }; public void setStatus(EStatus status) { this.status = status; } public EStatus getStatus() { return status; } @Override public void onInitialize() { } @Override public void onTerminate(EStatus Status) { } } import com.lizhao.ai.ifs.IBehaviour; import com.lizhao.ai.ifs.IComposite; import java.util.ArrayList; import java.util.List; public abstract class BaseComposite extends BaseBehavior implements IComposite { protected ArrayList<IBehaviour> children = new ArrayList<>(); @Override public void addChild(IBehaviour child) { children.add(child); } @Override public void removeChild(IBehaviour child) { children.remove(child); } @Override public void clearChild() { children.clear(); } @Override public List<IBehaviour> getChildren() { return children; } @Override public void setChildren(List<IBehaviour> behaviours) { this.children = (ArrayList<IBehaviour>) behaviours; } } import com.lizhao.ai.ifs.ICondition; public abstract class BaseCondition extends BaseBehavior implements ICondition { protected boolean negation = false; @Override public boolean isNegation() { return negation; } @Override public void setNegation(boolean negation) { this.negation = negation; } protected int getRandom() { Double random = Math.random() * 100; // int i = random.intValue(); return random.intValue(); } } import com.lizhao.ai.ifs.IAction; public abstract class BaseAction extends BaseBehavior implements IAction { }
四、接下来就是写具体的实现类了,不列出来了,去下载具体代码看吧。里面的逻辑是根据随机数判断的,因为没有具体的场景。
五、之后就是将这些行为都挂到树上了,写一个BehaviorTree,里面有一个根节点,进行遍历操作。
而树的构造方式这边使用一个BehaviorTreeBuilder,用的是Stack(之前自己也写过树,这个小哥用的stack方式确实是比较好),具体的看下面代码
六、最后就是写一个测试方法来调用下了,我们将会构建这么一个逻辑,
创建了一名角色,该角色一开始处于巡逻状态,
一旦发现敌人,先检查自己生命值是否过低,如果是就逃跑,
否则就攻击敌人
比原文的逻辑简化了一部分,因为一直在写代码,没运行起来,不知道结果,就简化了,剩下部分会在下一篇补上
直接画图的话就是这么个结构
SelectorImpl
--SequenceImpl
--ConditionIsSeeEnemy
--SelectorImpl
--SequenceImpl
--ConditionIsHealthLow
--ActionRunaway
--ActionAttack
--ActionPatrol
具体代码
public class Main { public static void main(String[] args) { BehaviorTreeBuilder builder = new BehaviorTreeBuilder(); BehaviorTree behaviorTree = builder.addBehaviour(new SelectorImpl()) .addBehaviour(new SequenceImpl()) .addBehaviour(new ConditionIsSeeEnemy()) .back() .addBehaviour(new SelectorImpl()) .addBehaviour(new SequenceImpl()) .addBehaviour(new ConditionIsHealthLow()) .back() .addBehaviour(new ActionRunaway()) .back() .back() .addBehaviour(new ActionAttack()) .back() .back() .back() .addBehaviour(new ActionPatrol()) .end(); //模拟执行行为树 for (int i = 0; i < 10; ++i){ behaviorTree.tick(); System.out.println("--------------" + i + "------------"); } System.out.println("pause "); } }运行结果
运行结果: Not SeeEnemy ActionPatrol 巡逻 --------------0------------ Not SeeEnemy ActionPatrol 巡逻 --------------1------------ Not SeeEnemy ActionPatrol 巡逻 --------------2------------ SeeEnemy Health is low ActionRunaway 跑路 --------------3------------ Not SeeEnemy ActionPatrol 巡逻 --------------4------------ SeeEnemy Health is low ActionRunaway 跑路 --------------5------------ Not SeeEnemy ActionPatrol 巡逻 --------------6------------ SeeEnemy Health is low ActionRunaway 跑路 --------------7------------ SeeEnemy Health is low ActionRunaway 跑路 --------------8------------ SeeEnemy Health is low ActionRunaway 跑路 --------------9------------ pause
nice
上面的代码在码云上 https://gitee.com/lizhaoandroid/BehaviorTree ,定格分支为release1.1上
可以加qq群一起学习讨论Java游戏服务器开发的相关知识 676231524