cocos creator中使用行为树(BehaviorTree) 一

cocos creator中使用行为树(BehaviorTree) 一

前提:

  1. 本文使用的cocos creator版本是1.10.2, 由于creator每个版本都有一些兼容性的坑, 所以还请大家与我的版本保持一致.
  2. 本文全部使用Typescript编写.
  3. 我也是第一次使用行为树制作游戏ai, 本文是自己学习的, 写的不对的, 望大家海涵!

首先感谢论坛大佬提供的行为树可视化制作插件, 附链接.

开始编写... 啦啦啦

行为树可视化制作插件的使用我就不讲了, 论坛讲的很清楚, 但是有一点很重要, 我使用1.10.2版本按照论坛的方法操作会报错, 

解决方法: 打开b3core.0.1.0module.js文件, 找到var Composite = b3.class(b3.z),  将其改为var Composite =b3.Class(b3.BaseNode);

问题解决

那么我们开始行为树第一个AI, 称为monsterSimple. 他的功能是 一旦敌人靠近到一定范围, 立即发出警告!

打开cocos creator, 按照论坛的方法操作后, 我们拥有了

这两个脚本我们之后在说, 先把player(我们控制的对象)搞出来

建立一个新场景,取名GameScene.. 

创建结点player, 红色那个turn表示玩家当前的朝向,

创建控制脚本PlayerCtl

import GameScene from "./GameSCene"
const {ccclass, property} = cc._decorator;

@ccclass
export default class NewClass extends cc.Component {

    direction = 0;      // 当前状态 -1表示向左, 1表示向用, 0表示不动
    faceTo = 1;         // 当前面向
    speed = 100;

    gameCtl: GameScene = null;

    // onLoad () {}

    start () {

    }

    init(gameCtl: GameScene) {
        this.gameCtl = gameCtl;
    }
    /**
     * 设置玩家方向
     * @param dir 
     */
    setDirection(dir: number) {
        this.direction = dir;
        this.faceTo = dir == 0 ? this.faceTo : dir;
        this.node.scaleX = this.faceTo;
    }
    /**
     * 更新玩家位置
     * @param dt 
     */
    playerUpdate(dt: number) {
        if(this.direction == 0) {
            return ;
        }
        this.node.x += this.direction * dt * this.speed;
    }

}

就是一些简单的设置, 简单的控制player结点左右移动

另附上GameScene

import PlayerCtl from "./PlayerCtl";
import { PlayerDir } from "./GameInterface";
const {ccclass, property} = cc._decorator;

/**
 * 一个简单的样例, 用于学习行为树AI
 * 怪物1, 默认在一段位置内巡逻, 一旦发现目标(玩家), 则发动攻击
 */
@ccclass
export default class GameScene extends cc.Component {


    @property(PlayerCtl)    // 到编辑器, 把player结点拖过来, 当然要先把playerCtl绑在结点上
    playerCtl: PlayerCtl = null;



    onLoad() {
        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this);
    }


    start () {
        this.playerCtl.init(this);
        this.runBehaviorTree();
    }

    onKeyDown(event: any) {
        switch(event.keyCode) {
            case cc.KEY.left:
            case cc.KEY.a:
                this.playerCtl.setDirection(PlayerDir.left);
            break;
            case cc.KEY.right:
            case cc.KEY.d:
                this.playerCtl.setDirection(PlayerDir.right);
            break;
        }
    }
    onKeyUp(event: any) {
        switch(event.keyCode) {
            case cc.KEY.left:
            case cc.KEY.a:
            case cc.KEY.right:
            case cc.KEY.d:
                this.playerCtl.setDirection(PlayerDir.stop);
            break;
            
        }
    }

    onDestroy() {
        cc.systemEvent.off(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
        cc.systemEvent.off(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this);
    }

    

    update(dt: number) {   
        // this.runBehaviorTree();
        this.playerCtl.playerUpdate(dt);
    }

 


}

浏览器运行, 现在就可以通过左右键(或者a/s键)控制玩家左右移动.

那么现在开始进入正题

建立一个monster结点

把BehaviorTree脚本挂在该结点上

点击编辑, 打开行为树编辑工具

恩, 一个简单的树搞定, 简单说一下这个树的工作流程, 单纯是自己理解的, 可能不对

每次行为开始, 即被调用的该行为树(BehaviorTree)(我们这就是指monster上挂的BehaviorTree脚本)的tick方法

先进入root(这个并不是必要的), 在进入(Look)结点(顺序由上到下), 在Look结点判断玩家(player)是否进入monster的监视范围内

如果进入了, 那么return SUCCESS, 否则return FAILURE, 如果是SUCCESS, 那么继续向下执行, 进入Attack结点, 执行对应的攻击逻辑

大概就是这么一个流程, 开始敲

新建Look脚本, Attack脚本


const {ccclass, property} = cc._decorator;

@ccclass
export default class NewClass extends cc.Component {

    // LIFE-CYCLE CALLBACKS:

    // onLoad () {}

    start () {
        
    }

    enter (tick,b3,treeNode){
        console.log("enter");
    }

    open (tick,b3,treeNode){
        console.log("open");
    }

    tick (tick,b3,treeNode){
        console.log("tick");
    }

    close (tick,b3,treeNode){
        console.log("close");
    }

    exit (tick,b3,treeNode){
        console.log("exit");
    }

    // update (dt) {}
}

两个脚本是一样的, 这里就放了一个

对了, 我们还没编辑Look 的监视范围, 在打开可视化编辑工具

点击Look结点

添加一个参数, 名字是radio, 值为15, 这个15就是监视范围, 具体数值我们之后在调整

保存之后, 回到代码编辑页面

因为我们需要实时的知道玩家的位置, 用于确认玩家和敌人的距离(我们这里只计算x轴)

在playerCtl中添加getPosition方法

getPosition() {
        return this.node.position;
    }

现在就是让我们在Look脚本中获取玩家位置了, 但是我还没想好怎么方便的获取, 这个留到下次, 这次我们用个垃圾方法

在Look脚本中

@property(PlayerCtl) // 获得玩家
playerCtl: PlayerCtl = null;

修改tick方法

tick (tick,b3,treeNode){
        let radio = treeNode.parameter.radio;
        let playerX = this.playerCtl.getPosition().x;
        if(Math.abs(playerX - this.node.x) < radio) {
            this.setTips("发现你啦!");
            return b3.SUCCESS;
        }else {
            this.setTips("敌人");
            return b3.FAILURE;
        }
    }

setTips(str: string) {
        this.node.getChildByName("str").getComponent(cc.Label).string = str;
    }

恩, 刚刚测试了一下, 监视范围设置15太小了, 我改成200了, 不改也无所谓, 提一下

回到GameScene中

现在的问题就是如何让敌人不断的思考, 以及设置敌人思考的时间间隔

添加方法runBehaviorTree

runBehaviorTree() {
        this.master.getComponent("BehaviorTree").tick();
}

在update中调用该方法

update(dt: number) {   
        this.runBehaviorTree();
        this.playerCtl.playerUpdate(dt);
    }

那么现在是每过一帧, 思考一次

全部写完, 看看效果

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值