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);
    }

那麼現在是每過一幀, 思考一次

全部寫完, 看看效果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章