二、H5游戏之行为树的运行中状态(javascript篇)

版权声明:为了避免百度一下通片同一篇文章,未经博主允许不得转载。本博客作为笔记使用,正确性请自行验证。 https://blog.csdn.net/u014071104/article/details/81609844

行为树由多种不同类型的节点构成,它们都拥有一个共同的核心功能,即它们会返回三种状态中的一个作为结果。这三种状态分别是:

  • 成功-Success;
  • 失败-Failure;
  • 运行中-Running;

前两个,正如它们的名字,是用来向它们的父节点通知运行的成功或失败的结果。第三种是指还在运行中,结果还未决定,在下一个 tick 的时候再去检查这个节点的运行结果。

这个功能非常重要,它可以让一个节点持续运行一段时间来维持某些行为。比如一个“walk(行走)”的节点会在计算寻路和让角色保持行走的过程中持续返回“Running”来让角色保持这一状态。如果寻路因为某些原因失败,或是除了某些状况让行走的行为不得不中止,那么这个节点会返回“Failure”来告诉它的父节点;如果这个角色走到了指定的目的地,那么节点返回“Success”来表示这个行走的指令已经成功完成。

文字截取于:https://www.indienova.com/indie-game-development/ai-behavior-trees-how-they-work/#iah-6

这篇文章延续上一篇,这篇主要是对Running状态进行一个记述。

现在通过2天的学习,我对状态书有了一点了解,我觉得有必要按照我的思路去手动写下代码,以便更好的理解。

上篇文章的行为树如下:

1.Selector是一个选择节点,他会根据它的子节点下面的type以及func进行选择是否要进行执行某个子节点

2.如果有钱,就执行Sequence下面的所有行为:买车和买房

但是,现在我们有个问题,如果现在我正在买房,下一次在遍历行为树的时候,我们希望很友好的说“不”,毕竟一栋房子都没搞定,谁会有心思去考虑下一栋的问题或者理解买房子是与状态的,某个时间内不允许修改。这也是我们这篇要讲的Running问题

我们的程序会变成类似以下这样(绿色部分下面的分支是我们将要做的):

我们将要在原来的Sequence_1下新增一个Selector替换action_2

效果在线预览

行为树:

//tree
var tree ={
    "name": "",
    "indexchild": 1,
    "height": 50,
    "textwidth": 28,
    "width": 56,
    "children": [
    {
        "name": "Selector_1",
        "indexchild": 1,
        "height": 50,
        "textwidth": 54,
        "width": 82,
        "children": 
        [
                {
                    "name": "Filter_1",
                    "indexchild": 1,
                    "height": 50,
                    "textwidth": 55,
                    "width": 83,
                    "children": [
                            {
                                "name": "Sequence_1",
                                "indexchild": 1,
                                "height": 50,
                                "textwidth": 62,
                                "width": 90,
                                "children": [
                                    {
                                        "name": "Action_2",
                                        "indexchild": 1,
                                        "height": 50,
                                        "textwidth": 51,
                                        "width": 79,
                                        "level": 5,
                                        "func": "BuyCar",
                                        "y": 384,
                                        "selected": false,
                                        "valid": true,
                                        "x": 213.15972180497,
                                        "sleep": false,
                                        "id": "node8",
                                        "sim": "",
                                        "textlines": 3,
                                        "type": "Action",
                                        "levelindex": 5
                                    },
                                    {
                                        "name": "Action_3",
                                        "indexchild": 2,
                                        "height": 50,
                                        "textwidth": 44,
                                        "width": 72,
                                        "level": 5,
                                      //  "func": "isBuySugaring",//"BuySugar"
                                        "y": 384,
                                        "selected": false,
                                        "valid": true,
                                        "x": 328.65972180497,
                                        "sleep": false,
                                        "id": "node9",
                                        "sim": "",
                                        "textlines": 3,
                                        "type": "Selector",//Action
                                        "levelindex": 6,
                                        "children":[

                                            // {
                                            //    "name": "Action_3",
                                                  //   "indexchild": 2,
                                                  //   "height": 50,
                                                  //   "textwidth": 44,
                                                  //   "width": 72,
                                                  //   "level": 5,
                                                  //   "func": "BuySugar",
                                                  //   "y": 384,
                                                  //   "selected": false,
                                                  //   "valid": true,
                                                  //   "x": 328.65972180497,
                                                  //   "sleep": false,
                                                  //   "id": "node9",
                                                  //   "sim": "",
                                                  //   "textlines": 3,
                                                  //   "type": "Action",
                                                  //   "levelindex": 6,
                                            // }
                                            {
                                                "name": "Action_3",
                                                "indexchild": 2,
                                                "height": 50,
                                                "textwidth": 44,
                                                "width": 72,
                                                "level": 5,
                                                "func": "isBuySugaring",//"BuySugar"
                                                "y": 384,
                                                "selected": false,
                                                "valid": true,
                                                "x": 328.65972180497,
                                                "sleep": false,
                                                "id": "node9",
                                                "sim": "",
                                                "textlines": 3,
                                                "type": "Filter",//Action
                                                "levelindex": 6,
                                                "children":[
                                                       {
                                                            "name": "Action_3",
                                                            "indexchild": 2,
                                                            "height": 50,
                                                            "textwidth": 44,
                                                            "width": 72,
                                                            "level": 5,
                                                            "func": "",//
                                                            "y": 384,
                                                            "selected": false,
                                                            "valid": true,
                                                            "x": 328.65972180497,
                                                            "sleep": false,
                                                            "id": "node9",
                                                            "sim": "",
                                                            "textlines": 3,
                                                            "type": "Sequence",//Action
                                                            "levelindex": 6,
                                                            "children":[
                                                                  {
                                                                       "name": "Action_3",
                                                                        "indexchild": 2,
                                                                        "height": 50,
                                                                        "textwidth": 44,
                                                                        "width": 72,
                                                                        "level": 5,
                                                                        "func": "BuySugar",
                                                                        "y": 384,
                                                                        "selected": false,
                                                                        "valid": true,
                                                                        "x": 328.65972180497,
                                                                        "sleep": false,
                                                                        "id": "node9",
                                                                        "sim": "",
                                                                        "textlines": 3,
                                                                        "type": "Action",
                                                                        "levelindex": 6,
                                                                }
                                                            ]
                                                       }
                                                ]
                                            }
                                         ]
                                    }
                                ],
                                "level": 4,
                                "func": "",
                                "y": 288,
                                "selected": false,
                                "valid": true,
                                "x": 261.90972180497,
                                "sleep": false,
                                "id": "node7",
                                "sim": "",
                                "textlines": 3,
                                "type": "Sequence",
                                "levelindex": 4
                            }
                    ],
                    "level": 3,
                    "func": "HasMoney",
                    "y": 192,
                    "selected": false,
                    "valid": true,
                    "x": 265.40972180497,
                    "sleep": false,
                    "id": "node2",
                    "sim": "",
                    "textlines": 3,
                    "type": "Filter",
                    "levelindex": 3
                },
                {
                    "name": "Sleepy",
                    "indexchild": 2,
                    "height": 50,
                    "textwidth": 49,
                    "width": 117,
                    "level": 3,
                    "func": "Rest",
                    "y": 192,
                    "selected": false,
                    "valid": true,
                    "x": 363.75957639191,
                    "sleep": false,
                    "id": "node6",
                    "sim": "",
                    "textlines": 3,
                    "type": "Condition",
                    "levelindex": 7
                },
                {
                    "name": "Action_3",
                    "indexchild": 3,
                    "height": 50,
                    "textwidth": 47,
                    "width": 75,
                    "level": 3,
                    "func": "GoHome",
                    "y": 192,
                    "selected": true,
                    "valid": true,
                    "x": 515.59027819503,
                    "sleep": false,
                    "id": "node5",
                    "sim": "",
                    "textlines": 3,
                    "type": "Action",
                    "levelindex": 8
                }
       ],
        "level": 2,
        "func": "",
        "y": 96,
        "selected": false,
        "valid": true,
        "x": 387,
        "sleep": false,
        "id": "node1",
        "sim": "",
        "textlines": 3,
        "type": "Selector",
        "levelindex": 2
    }],
    "level": 1,
    "func": "",
    "selected": false,
    "valid": true,
    "y": 0,
    "x": 400,
    "id": "__start__",
    "sleep": false,
    "textlines": 3,
    "type": "Start",
    "levelindex": 1
};

行为树处理程序(未改变):

var BT = {
    //-- 1、调用前保证BT中的函数全部都在obj表中
    //-- 2、假设输入合法
    run: function(bt, object) {

        obj = object;
        var first_children = bt.children[0]; //Selecter选择器(数组只有一个元素),type为selecter
        console.log("first::", first_children.type)
        BT[first_children.type](first_children); //执行BT里面的selecter函数
    },
    // -- Composite Node
    //-- Selector
    Selector(node) {
        var return_value = false;
        let childArray = ipairs(node.children);
        for (var child in childArray) {
            var childValue = node.children[child];
            console.log("Selecter  type:", childValue.type)
            //调用filter,filter属于Selecter派发事件的范畴,属于Selecter的内容
            //如果有合适条件的,意思返回true,就执行当前分支的,不在进行下一个执行(selecter的作用)
            if (BT[childValue.type](childValue) == true) {
                console.log("Selecter  type: true:", childValue.type)
                return_value = true;
                break;
            }
        }
        return return_value; //因为【当前函数】是在上一级函数里面调用的,所以等同“告诉上一级流程是否为true或者false”
    },
    //-- Sequence,标记为:"并列的动作
    Sequence(node) {
        var return_value = true;
        console.log("Sequence:1=》2=》3", ipairs(node.children).length)
        let childArray = ipairs(node.children);
        for (var child in childArray) {
            var childValue = childArray[child];
            console.log("=======1232343",childValue)
            console.log("Sequence type:", childValue.type, "~function:", childValue.func)
            //调用action
            if (BT[childValue.type](childValue) == false) {
                return_value = false;
                break;
            }

        }
        return return_value; //因为【当前函数】是在上一级函数里面调用的,所以等同“告诉上一级流程是否为true或者false”
    },
    //-- Behaviour Node
    //-- Action
    Action(node) {
        obj[node.func]();
        return true;
    },
    //-- Condition Action
    Condition(node) {
        if (obj[node.name]()) {
            obj[node.func]();
            return true;
        } else {
            return false; //因为【当前函数】是在上一级函数里面调用的,所以等同“告诉上一级流程是否为true或者false”
        }
    },
    //-- Decorator Node
    //-- Yet nothing ...

    //-- Condition Node
    //-- Filter,过滤,如果满足条件才继续,否则就中断
    Filter(node) {
        console.log("Filtr:", node.func)
        //执行统一层级的条件判断,可以在node.func对应的函数里面判断是 pending?success?false状态来确定是否要进行
         //console.log("Filter=====",node.func,obj[node.func])
        if (obj[node.func]()) {
            var first_children = node.children[0];
            console.log("Filter.type:",first_children.type)
            BT[first_children.type](first_children);
            //执行指定【类型】函数,例如执行买房,可是我正在买中。。  就不需要再次买了
            //是否,可以在行为函数里面加判断,但是这样就和行为树的节点(Selecter)功能冲突了
            //这种情况我们可以,在Sequence分支里面再加一个selecter
            return true;
        } else {
            return false; //因为【当前函数】是在上一级函数里面调用的,所以等同“告诉上一级流程是否为true或者false”
        }
    }
};

其他部分:

// 如果有钱了,就买糖、买车(如果正在买,就3秒后再允许买);
// 否则,需要回家取钱,如果累了,则休息(可能休息后会回家吧),如果不累,就回家取钱!
//================并且约定Condition条件判断的函数名使用name来指定
var obj;
var ipairs = function(obj) {
    if (obj) {
        return obj;
    } else {
        return {};
    }
}

/*
省略=============
var BT={}
var  tree={}

*/


//状态==========(状态里面的布尔值和对应的状态为正关系)
var STATE = {
    BuySugar: true,//能否买房,能为true,否为false
}
//定时器管理
var Timer = {
    BuySugar: {
        Timer: 0,
        number: 5
    }
}
//定时器处理函数(状态管理。。。伪代码)
function TimerDown(timeKey) {
    STATE[timeKey] = false; //状态正在中...
    var canBuy=STATE[timeKey]?"能买":"不能卖";
    document.querySelector("#time").innerHTML=Timer[timeKey].number+"==="+canBuy;
    if (!Timer[timeKey].Timer) {

        Timer[timeKey].Timer = setInterval(function() {
            if (Timer[timeKey].number < 0) {
                
                Timer[timeKey].number = 5;
                clearInterval(Timer[timeKey].Timer);
                Timer[timeKey].Timer = null;
                 STATE[timeKey] = true; //可以继续买,状态结束(成功或失败)
                 console.log("TIME,TIME,TIME,END",Timer[timeKey].Timer,Timer[timeKey].number,STATE[timeKey],"STATE.BuySugar:",STATE.BuySugar)
            } else {
                Timer[timeKey].number -= 1;
                 STATE[timeKey] = false; //不可以继续买房,状态正在中...
            }
           var canBuy=STATE[timeKey]?"能买":"不能卖";
           document.querySelector("#time").innerHTML=Timer[timeKey].number+"==="+canBuy;
        }, 500);
    }
    else{
        STATE[timeKey] = true;
    }
}

var person = {
    isBuySugaring() {
        if(STATE.BuySugar){
            console.log("能买房!")
               
        }
        else{
              console.log("%c 我还在买房中呢,和买房的小姐姐在瞎JB扯,不要打断我!等3秒后下再去买下一栋把,五环买!!",'color:#fff;background:red;');
        }
    
        return STATE.BuySugar;
    },
    HasMoney: function() { console.log("person:hasMoney?"); return true },
    Sleepy: function() { console.log("person:sleepy?"); return true },
    Rest: function() { console.log('person:rest=======') },
    BuyCar: function() { console.log('person:buy car======') },
    BuySugar: function() {
        console.log('%c person:buy sugar=====,来买房去了,拉~拉~。','color:#fff;background:green;');

        TimerDown("BuySugar");

    },
    GoHome: function() { console.log('person:go home=====') },
}

for (var i = 0; i <= 1; i++) {
  BT.run(tree, person);
}

//demo      dom交互==================
window.onload=function(){
    document.querySelector("#btn").onclick=function(){
     BT.run(tree, person);
    }
}

核心改动是在Person里面新增了 isBuySugaring函数来控制是否能买房

如果其他条件都具备的情况下,我们要给人物加一个需要持续的状态

  1. 只需要在行为树JSON里面加一个Seletor
  2. 给Person加一个返回状态的函数

本篇完。

猜你喜欢

转载自blog.csdn.net/u014071104/article/details/81609844
今日推荐