一、H5游戏之行为树(javascript篇)非原创!!

本篇文章的非代码部分来源于:https://blog.csdn.net/iamagoodguy_/article/details/39757083

本篇文章的所有代码来源于本博主对原文lua代码的改写&&绿色部分文字是本文新增或修改部分

转载请保留以上文字

 

使用BTEditor可以生成行为树(BT,Behaviour Tree)的Lua代码,这里对生成的代码进行解析。

(BTEditor的项目主页:https://github.com/bartoleo/BTEditor)

要注意:

1、行为树每个节点都需要向其父节点返回一个值(可以理解为在执行程序前先通过一个函数来判断JSON数据的子节点是否允许执行),以允许父节点根据子节点运行情况继续运行。

2、每棵树都有一个根节点,这个节点没有特殊意义。

需要解析的行为树节点的解释(发挥想象力吧,这里的Node还能有很多种~):

一、Composite Node(可以理解为行为的关系处理函数

1、Selector:实现子节点或关系,按定义顺序遍历子节点,直到有一个子节点返回true时停止,并返回true。如果子节点全部返回false,则返回false。

2、RandomSelector:类似Selector,不同之处在于,按照随机顺序遍历子节点。

3、Sequence:实现子节点与关系,按定义顺序遍历子节点,直到有一个节点返回false时停止,并返回false。如果子节点全部返回true,则返回true。

4、Parallel:实现逗号表达式的效果,依次执行所有子节点,返回最后一个子节点的返回值。

5、。。。

二、Behaviour Node

1、Action:执行其中定义的行为,并返回true。

2、ConditionAction:如果条件为真,则执行Action,并返回true;否则,不执行Action,并返回false。

3、。。。

三、Decorator Node

1、Successor:拥有一个子节点,执行子节点后返回true。

2、Failure:拥有一个子节点,执行子节点后返回false。

3、Negate:拥有一个子节点,返回子节点返回值的相反之(true变false,false变true)。

4、。。。

四、Condition Node

1、Filter:如果Filter为真,则执行子节点,并返回true;否则,不执行子节点,返回false。

2、。。。

 

下面来看看BTEditor:

例子,如果有钱了,就买糖、买车;否则,需要回家取钱,如果累了,则休息(可能休息后会回家吧),如果不累,就回家取钱!

长成这样:



BTEditor生成的行为树的代码长成下面这个样子(经过lua table转json后的结果):

//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": "BuySugar",
                            "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": "BuyCar",
                            "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
};

现在根据上面的规则来进行代码编写即可。

我写了一个,没有完整实现,并且约定Condition条件判断的函数名使用name来指定。代码如下:

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

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

        obj = object;
        var first_children = bt.children[0];
        console.log("first::",first_children.type)
        BT[first_children.type](first_children);
    },
    // -- 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
            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("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)
        //执行统一层级的条件判断
        if (obj[node.func]()) {
            var first_children = node.children[0];
            BT[first_children.type](first_children);//执行指定【类型】函数
            return true;
        } else {
            return false;   //因为【当前函数】是在上一级函数里面调用的,所以等同“告诉上一级流程是否为true或者false”
        }
    }
};

测试代码如下:

var person = {
    HasMoney: function() { return false },
    Sleepy: function() { return false },
    Rest: function() { console.log('rest') },
    BuyCar: function() { console.log('buy car') },
    BuySugar: function() { console.log('buy sugar') },
    GoHome: function() { console.log('go home') },
}


BT.run(tree, person);

修改上面的HasMoney和Sleepy的返回值,可以得到不同的行为结果。

在线源码阅读:点击跳转

 

更多相关参考:

https://blog.csdn.net/xufeng0991/article/details/60137670

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

 

明明不是原创为啥选原创!!还要不要脸!!

1.博客对非原创文章特不友好,故意选的原创

2.javascript版本的行为树相关解释太少,对H5开发者不友好,故选原创

3.博主有私心,故选原创

4.如果对任何人产生了不利影响,请联系博主,评论在下方。

---------------------------------------------------------------------------------------------瑟瑟发抖ヽ(*。>Д<)o゜


下一篇点击访问​​​​​​​

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陆康永

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值