精华内容
下载资源
问答
  • 行为树代码实现
    千次阅读
    2018-01-18 16:38:50

    行为树 Behavior Tree C#实现 二

    前篇介绍了 行为树 Behavior Tree 的原理。
    本篇通过前篇原理构思 Behavior Tree 程序设计

    1、节点类型枚举
    节点类型通过枚举列举
    组合节点和修饰节点每一种需要具体的枚举值

    子树节点使用一个枚举值

    叶子节点:
    所有行为节点 共用一个枚举类型
    所有条件节点 共用一个枚举类型

    2、节点执行结果枚举
    定义返回结果枚举值 Fail、Success、Running

    3、添加 抽象节点 AbstractNode
    抽象节点包含抽象函数:
    OnEnter() 开始执行函数
    Execute() 执行中函数
    OnExit() 退出执行函数

    三个函数的执行顺序
    执行当前节点
    (3.1) 如果节点未开始执行,调用 OnEnter 函数
    (3.2) 调用 Execute() 函数,并记录返回结果 resultType
    (3.3) 如果 resultType 不等于 Running,调用 OnExit() 函数,并标记节点未开始执行

    4.添加组合节点抽象类NodeComposite 继承 AbstractNode
    组合节点需要添加子节点,所以加入子节点列表和添加子节点的方法

       protected List<NodeBase> nodeChildList = new List<NodeBase>();
    

    下方各个组合节点具体类,分别实现,并都继承 NodeComposite
    选择节点 NodeSelect 继承 NodeComposite、
    顺序节点 NodeSequence 继承 NodeComposite、
    随机选择节点: NodeRandomSelect 继承 NodeRandom、
    随机顺序节点: NodeRandomSequence 继承 NodeRandom、
    随机权重节点 :NodeRandomPriority 继承 NodeRandom、
    并行节点 NodeParallel 继承 NodeComposite、
    并行执行所有节点: NodeParallelAll 继承 NodeComposite、
    if 判断并行节点 :NodeIfJudgeParallel 继承 NodeIfJudge、
    if 判断顺序节点 :NodeIfJudgeSequence 继承 NodeIfJudge

    5.添加修饰节点抽象类 NodeDecorator 继承 NodeComposite
    修饰节点取反 :NodeDecoratorInverter 继承 NodeDecorator、
    修饰节点重复 :NodeDecoratorRepeat 继承 NodeDecorator、

    修饰节点_返回固定结果抽象类 NodeDecoratorReturnConst 继承 NodeDecorator
    修饰节点_返回 Fail :NodeDecoratorReturnFail 继承 NodeDecoratorReturnConst、
    修饰节点_返回 Success: NodeDecoratorReturnSuccess 继承 NodeDecoratorReturnConst、

    修饰节点:一直执行,直到达到条件 抽象类 NodeDecoratorUntil 继承 NodeDecorator
    修饰节点_直到 Fail : NodeDecoratorUntilFail 继承 NodeDecoratorUntil、
    修饰节点_直到 Success: NodeDecoratorUntilSuccess 继承 NodeDecoratorUntil

    6.添加子树类 NodeSubTree
    继承 NodeComposite,因为子树也是组合节点

    7.添加子节点抽象类 NodeLeaf
    继承 AbstractNode

    8.添加 行为节点抽象类 NodeAction 并继承 NodeLeaf
    自定义添加的行为节点继承 NodeAction

    9.添加 条件节点抽象类 NodeCondition 并继承 NodeLeaf
    自定义添加的条件节点继承 NodeCondition

    10.添加一个将单个配置文件解析为行为树结构的类
    根据配置文件的节点类型,实例化具体类,根据节点的父子关系,将子节点添加给父节点
    行为节点和条件节点配置的参数和条件组需要从配置文件添加到行为节点和条件节点

    11.行为树执行需要拿到跟节点(入口节点)
    所以解析出来的行为树,需要拿到跟节点的实例
    (11.1)每一帧执行 调用行为树 跟节点
    (11.2)跟节点遍历它的子节点,并调用子节点
    (11.3)子节点再遍历它自己的子节点,并调用子子节点

    直到没有子节点为止 返回 (11.1)
    在这里插入图片描述
    注意:上面的遍历子节点并执行子节点是深度优先遍历
    这样 深度优先遍历了 整棵树,并驱动行为树逻辑。
    如上图
    (1) 外部每帧调用跟节点 1
    (2) 节点 1 调用 节点 2
    (3) 节点2 调用 节点 3,节点 3 没有子节点了,节点 3 向节点 2 返回 节点 3 的执行结果
    (3) 如果节点2 根据节点3 执行结果判断,需执行下一个节点4,继续执行 (4),否则跳转到 (5)
    (4) 则调用节点4,节点4没有子节点了,则向节点 2 返回执行结果
    (5) 节点2 已没有下一个可执行节点了,节点2向节点1返回执行结果
    (6) 如果节点1 根据节点2执行结果判断,需执行下一个节点5,继续执行 (7),否则跳转到 (11)
    (7) 节点5 调用节点 6,节点 6 没有子节点了,节点6 想节点5 返回节点6的执行结果
    (8) 如果节点5根据节点6执行结果判断,不需执行下一个节点7,跳转到(9), 否则继续执行 (10)
    (9 )则调用节点7,节点7没有子节点了,则向节点5返回执行结果
    (10) 节点5 已没有下一个可执行节点了,则节点5想节点1返回执行结果
    (11) 节点 1 返回结果

    下篇将实际运用该代码以测试其功能逻辑

    更多相关内容
  •  由于项目的需要,所以实现了一个非常简单的行为树,来应对我们的需求。之所以说简单,是因为我并没有实现很多控制节点,而只是实现了基础的业务的三个节点而已。至于其他的你觉得有用的控制节点,可以自己修改...
  • 行为树JavaScript实现。 它们对于实现AI很有用。 如果您需要有关行为树的更多信息,请查看 , 不错的。 产品特点 需要的:序列,选择器,任务 扩展:装饰器 安装 如果使用npm: npm install behaviortree 或使用...
  • 如果行为树中写有各种行为功能的节点的话,即便没有写过代码的,稍微学习一下,只用行为树也可以做出具有一定的智能行为的角色。 行为树采用节点描述行为逻辑。 主要有:选择节点、顺序节点、并行节点、修饰节点、...

    行为树介绍

    行为树是个节点树,父节点通过不断遍历子节点,根据不同类型的节点执行不同的分支。最终调用叶节点执行功能。行为树也不难理解,他就像代码逻辑一样,只是用节点的方式展现出来,而且比代码更直观。如果行为树中写有各种行为功能的节点的话,即便没有写过代码的,稍微学习一下,只用行为树也可以做出具有一定的智能行为的角色。

    行为树从上到下,从左到右执行。

    行为树采用节点描述行为逻辑。

    主要有:选择节点、顺序节点、并行节点、修饰节点、随机节点、条件节点、行为节点。

    一棵行为树表示一个AI逻辑。要执行这个 AI 逻辑,需要从根节点开始遍历整棵树,遍历执行的过程中,父节点根据自身的类型,确定需要如何执行、执行哪些子节点并继续执行,子节点执行完毕后,会将执行结果反馈给父节点。

    如图,可大致看出角色的行为逻辑。而且添加更多行为时,只用在节点中再添加即可,可扩展性非常高。
    在这里插入图片描述

    执行结果

    节点执行后有三种结果:

    • SUCCEED(执行成功)
    • FAILED(执行失败)
    • RUNNING(正在执行)

    节点类别

    • Composite(组合节点)
      组合节点用来控制树的遍历方式,每种组合节点的遍历方式都不相同。一般有以下几个节点。
      如果结果为 RUNNING,则下一帧仍从这个节点开始运行。

      • Sequence(顺序节点):按照节点顺序执行,如果有一个结果为 FAILED,则中断执行,返回 FAILED,类似于“逻辑与(And)”。
      • Selector(选择节点):按照节点顺序执行,如果有一个结果为 SUCCEED,则中断执行,返回 SUCCEED,类似于“逻辑或(Or)”。
      • Parallel(并行节点):子节点中有一个结果为 FAILED,则中断执行返回 FAILED
        如果有一个结果为 RUNNING,则执行完返回 RUNNING
        全部结果都为 SUCCEED,结果返回 SUCCEED
    • Decorator(修饰节点):修饰节点(Decorator)修饰节点不能独立存在,其作用为对子节点进行修饰,以得到我们所希望的结果.
      修饰节点有很多种,其中有一些是用于决定是否允许子节点运行的,也叫过滤器,例如 Until Success, Until Fail 等,首先确定需要的结果,循环执行子节点,直到节点返回的结果和需要的结果相同时向父节点返回需要的结果,否则返回 RUNNING

      • Inverter(反转):任务执行结果如果为 SUCCEED,则结果转为 FAILED;任务执行结果如果为 FAILED,则结果转为 SUCCEED;结果为 RUNNING 则不变。
      • UntilSuccess(直到成功):一直执行,返回 RUNNING,直到结果为 SUCCEED
      • UntilFail(直到失败):一直执行,返回 RUNNING,直到结果为 FAILED
      • Counter(计数):重复执行子节点多次。
      • …(可以自行设计其他更多符合自己需求的Decorator节点)
    • Leaf(叶节点):对叶节点进行重写,以进行逻辑判断和功能的执行。

      • Condition(条件节点):判断条件是否成立,只返回 SUCCEEDFAILED 这两种状态。
      • Action(行为节点):控制节点,执行各种功能。

    其他类

    • Blackboard(黑板):存储节点树中的全局数据。
    • Root(行为树根节点):用来运行整个行为树。

    节点脚本

    开始进行节点的设计,我们先在 文件系统 中创建一个 src 文件夹,我们之后创建的脚本都放在这个文件夹里
    在这里插入图片描述

    通过上面的信息,我们可以添加任务结果枚举,如下的 enum{} 内容,添加 _task() 方法执行任务,并返回执行结果。剩下添加 rootactor 用于可能会操作用到的变量。

    行为树节点的基类

    脚本名:BT_Node.gd

    ## BTNode 行为树的基类节点
    extends Node
    
    ## 任务执行结果
    enum {
    	SUCCEED,		# 执行成功
    	FAILED,			# 执行败
    	RUNNING,		# 正在执行
    }
    
    var root			# 节点的根节点
    var actor			# 控制的节点
    var task_idx = 0	# 当前执行的 task 的 index(执行的第几个节点)
    
    
    ## 节点的任务,返回执行结果
    func _task() -> int:
    	return SUCCEED
    

    Composite 节点

    组合节点,控制树的遍历方式。

    Sequence 节点

    脚本名:Composite_Sequence.gd

    ## Sequence 执行成功则继续执行,执行一次失败则返回失败
    extends "BT_Node.gd"
    
    
    var result = SUCCEED
    
    
    func _task():
    	while task_idx < get_child_count():
    		result = get_child(task_idx)._task()
    		# 执行成功继续执行下一个,直到失败或束
    		if result == SUCCEED:
    			task_idx += 1
    		else:
    			break
    	
    	if task_idx >= get_child_count() || result == FAILED:
    		task_idx = 0
    		if result == FAILED:
    			return FAILED
    	
    	# 如果都没有执行失败的,则回 SUCCEED
    	return SUCCEED
    
    Selector 节点

    脚本名:Composite_Selector.gd

    ## Selector 执行失败则继续执行,执行一次成功则返回成功
    extends "BT_Node.gd"
    
    
    var result = FAILED
    
    
    func _task():
    	while task_idx < get_child_count():
    		result = get_child(task_idx)._task()
    		# 执行失败继续执行下一个,直到成功败或结束
    		if result == FAILED:
    			task_idx += 1
    		else:
    			break
    	
    	if task_idx >= get_child_count() || result == SUCCEED:
    		task_idx = 0
    		if result == SUCCEED:
    			return SUCCEED
    	
    	# 如果都没有成功执行的,则回 FAILED
    	return FAILED
    
    Parallel 节点

    脚本名:Composite_Parallel.gd

    ## Paraller 并行节点,全部节点都执行一遍
    extends "BT_Node.gd"
    
    
    var result = SUCCEED
    
    
    func _task():
    	var is_running = false
    	
    	# 运行全部子节点,有一个为失败,则返回 FAILED
    	for task_idx in get_child_count():
    		var node = get_child(task_idx)
    		result = get_child(task_idx)._task()
    		if result == FAILED:
    			return FAILED
    		elif result == RUNNING:
    			is_running = true
    	
    	# 如果有运行的节点则返回 RUNNING
    	if is_running:
    		return RUNNING
    	
    	# 如果全部都是成功状态,则返回 SUCCEE
    	return SUCCEED
    

    Decorator 节点

    改变子节点任务执行的结果。以下做两个可能会用到的两个节点。

    Inverter 节点

    脚本名:Decorator_Inverter.gd

    ## Inverter 取反
    extends "BT_Node.gd"
    
    
    var result 
    
    
    func _task():
    	result = get_child(0)._task()
    	# 如果 成功,则返回 失败
    	if result == SUCCEED:
    		return FAILED
    	# 如果 失败,则返回 成功
    	elif result == FAILED:
    		return SUCCEED
    	
    	else:
    		return RUNNING
    
    Counter 节点

    脚本名:Decorator_Counter.gd

    在这里,这个脚本中其实可以只写 run_task() 方法中的代码,可以少写一半代码。我是额外写了一个 _run_loop() 方法,供更多不同情况的需求。

    ## Counter 计数器,运行指定次数
    extends "BT_Node.gd"
    
    
    ## 执行类型
    enum RunType {
        TaskCount,		# 任务执行次数(多帧的时间执行完最大次数)
    	LoopCount,		# 循环次数(一帧时间执行完最大次数)
    }
    
    
    export (RunType) var run_type = RunType.TaskCount
    export var max_count = 3	# 执行最大次数
    
    
    var run_func : FuncRef
    var count = 0	# 执行节点的次数
    
    
    func _ready():
    	if run_type == RunType.LoopCount:
    		run_func = funcref(self, "_run_loop")
    	elif run_type == RunType.TaskCount:
    		run_func = funcref(self, "_run_task")
    
    
    func _task():
    	return run_func.call_func()
    
    
    func _run_loop():
    	count = 0
    	while count < max_count:
    		# 计数
    		count += 1
    		if get_child(0)._task() == FAILED:
    			return FAILED
    	return SUCCEED
    
    
    func _run_task():
    	var result = get_child(0)._task()
    	count += 1
    	
    	if result == FAILED:
    		count = 0
    		return FAILED
    	
    	if count < max_count:
    		return RUNNING
    	else:
    		count = 0
    		return SUCCEED
    

    在设计 Leaf 节点之前,Leaf 需要用到 Blackboard 进行存储数据,所以我们设计一下 Blackboard

    Blackboard

    脚本名:Blackboard.gd

    ## Blackboard 黑板,存储数据
    extends Reference
    
    var data = {}	# 存放数据
    

    Leaf 节点

    脚本名:BT_Leaf.gd

    叶节点,用户重写相关的方法,实现各种功能。

    ## BT_Leaf 行为树叶节点,用于重写行为树
    extends "BT_Node.gd"
    
    
    var blackboard = null	# 黑板(记录设置数据,用于 Action 节点中)
    
    
    #==================================================
    # Set/Get
    #==================================================
    ## 设置黑板
    func set_blackboard(value):
    	blackboard = value
    
    ## 设置数据
    func set_data(property: String, value):
    	blackboard.data[property] = value
    
    ## 获取数据
    func get_data(property: String):
    	return blackboard.data[property]
    
    
    #==================================================
    # 自定义方法
    #==================================================
    func _task():
    	return SUCCEED
    
    Condition 节点

    脚本名:Leaf_Condition.gd

    ## Condition 条件节点
    extends "BT_Leaf.gd"
    
    
    func _task():
    	return SUCCEED if condition() else FAILED
    
    
    # 重写这个方法
    func condition() -> bool:
    	return true
    
    Action 节点

    脚本名:Leaf_Action.gd

    ## Action 控制节点,执行功能
    extends "BT_Leaf.gd"
    
    
    func _task():
    	return action(get_viewport().get_physics_process_delta_time())
    
    
    # 重写这个方法
    func action(delta: float) -> int:
    	return SUCCEED
    

    最后设计用于运行整个节点数的根节点。

    Root 节点

    脚本名:BT_Root.gd

    用来执行驱动整个行为树节点

    ## Root 根节点
    extends "BT_Node.gd"
    
    
    const Blackboard = preload("Blackboard.gd")
    
    
    var blackboard		# 全局行为树黑板
    
    
    ##==================================================
    #   内置方法
    ##==================================================
    func _ready():
    	_init_data()
    	_init_node(self)
    
    
    func _physics_process(delta):
    	get_child(0)._task()
    
    
    ##==================================================
    #   自定义方法
    ##==================================================
    ## 初始化当前数据
    func _init_data():
    	root = self
    	actor = get_parent()
    	blackboard = Blackboard.new()
    
    
    ## 初始化节点
    func _init_node(node: Node):
    	node.actor = self.actor
    	node.root = self.root
    	if node.has_method("set_blackboard"):
    		node.blackboard = self.blackboard
    	
    	# 不断向子节点迭代,对节点树中的所有节点进行初始化设置
    	for child in node.get_children():
    		_init_node(child)
    

    至此,行为树设计结束。


    相关学习链接:

    展开全文
  • 行为:Python 中的行为树实现使用behave ,你可以定义一个这样的行为树: tree = ( is_greater_than_10 >> wow_large_number | is_between_0_and_10 >> count_from_1 | failer * repeat(3) * doomed)bb = tree....
  • “本篇文章的非代码部分来源于:https://blog.csdn.net/iamagoodguy_/article/details/39757083 本篇文章的所有代码来源于本...使用BTEditor可以生成行为树(BT,Behaviour Tree)的Lua代码,这里对生成的代码进行...

    本篇文章的非代码部分来源于: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゜


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

    展开全文
  • C#/Unity 行为树 简单实现

    千次阅读 2018-07-25 21:02:08
     行为树的概念, 各种 Unity 插件 都没时间介绍。 代码是看了 Unity的 2D Game Kit , 提炼出来,测试! 对于学习和理解行为树会很有帮助!!! 纯代码, 没有Unity插件的节点编辑导出配置等内容。  using ...

    孙广东   2018.7.25    

              行为树的概念, 各种 Unity 插件 都没时间介绍。 代码是看了 Unity的 2D Game Kit , 提炼出来,测试!   对于学习和理解行为树会很有帮助!!!   纯代码, 没有Unity插件的节点编辑导出配置等内容。 

    using BTAI;
    using UnityEngine;
    
    public class TestBT : MonoBehaviour, BTAI.IBTDebugable
    {
        Root aiRoot = BT.Root();
    
    
        private void OnEnable()
        {
            aiRoot.OpenBranch(
                    BT.If(TestVisibleTarget).OpenBranch(
                    BT.Call(Aim),
                    BT.Call(Shoot)
                     ),
                    BT.Sequence().OpenBranch(
                    BT.Call(Walk),
                    BT.Wait(5.0f),
                    BT.Call(Turn),
                    BT.Wait(1.0f),
                    BT.Call(Turn)
                 )
            );
        }
    
        private void Turn()
        {
            Debug.Log("执行了 Turn");
        }
    
        private void Walk()
        {
            Debug.Log("执行了 Walk");
        }
    
        private void Shoot()
        {
            Debug.Log("执行了 Shoot");
        }
    
        private void Aim()
        {
            Debug.Log("执行了 Aim");
        }
    
        private bool TestVisibleTarget()
        {
            var isSuccess = UnityEngine.Random.Range(0, 2) == 1;
            Debug.Log("执行了 TestVisibleTarget    Result:" + isSuccess);
    
            return isSuccess;
        }
    
        private void Update()
        {
            aiRoot.Tick();
        }
    
        public Root GetAIRoot()
        {
            return aiRoot;
        }
    }
    

    using System.Collections.Generic;
    using UnityEngine;
    
    /// <summary>
    /// 这只是脚本系统
    /// 行为树会从Root节点开始遍历子节点。Update中执行
    /// 每个节点都有相关的操作,但是基本上就是返回三种状态
    /// ● Success​: 节点成功完成任务
    /// ● Failure​: 节点未通过任务
    /// ● Continue​:节点尚未完成任务。
    /// 但是每个节点的父节点对子节点的结果处理方式还不同。  例如
    /// ● Test 节点: 测试节点将调用其子节点并在测试为真时返回子节点状态,如果测试为假,则返回Failure而不调用其子节点。
    /// 行为树的一种构造方式如下:
    /// Root aiRoot = BT.Root(); 
    /// aiRoot.Do(  
    /// BT.If(TestVisibleTarget).Do(
    ///  BT.Call(Aim),
    ///  BT.Call(Shoot)
    ///  ),
    ///  BT.Sequence().Do(
    ///  BT.Call(Walk),
    ///  BT.Wait(5.0f),
    ///  BT.Call(Turn),
    ///  BT.Wait(1.0f),
    ///  BT.Call(Turn)
    ///  )
    /// ); 
    ///然后在Update中 调用   ​aiRoot.Tick()​ 。  刚刚构造的行为树是怎么样的检查过程呢?  
    ///1、首先检查TestVisibleTarget是否返回Ture,如果是继续执行子节点执行Aim函数和Shoot函数
    ///2、TestVisibleTarget是否返回false,if节点将返回Failure, 然后Root 将转向下一个子节点。这是个Sequence节点,它从执行第一个子节点开始。
    ///   1)将调用Walk函数,直接返回 Success,以便Sequence将下一个子节点激活并执行它。
    ///   2)执行Wait 节点,只是要等待5秒,还是第一次调用,所以肯定返回Running状态, 当Sequence从子节点上得到Running状态时,不会更改激活的子节点索引,下次Update的时候还是从这个节点开始执行
    ///3、Update的执行,当Wait节点等待的时间到了的时候,将会返回Success, 以便序列将转到下一个孩子。
    ///脚本中的Node列表 
    /// Sequence:
    //一个接一个地执行子节点。如果子节点返回:
    //●Success:Sequence将选择下一帧的下一个孩子开始。
    //●Failure:Sequence将返回到下一帧的第一个子节点(从头开始)。
    //●Continue:Sequence将在下一帧再次调用该节点。
    //RandomSequence:
    // 每次调用时,从子列表中执行一个随机子节点。您可以在构造函数中指定要应用于每个子项的权重列表作为int数组,以使某些子项更有可能被选中。
    //Selector :
    //按顺序执行所有子项,直到一个返回Success,然后退出而不执行其余子节点。如果没有返回Success,则此节点将返回Failure。
    
    // Condition :
    // 如果给定函数返回true,则此节点返回Success;如果为false,则返回Failure。
    // 与其他依赖于子节点结果的节点链接时很有用(例如,Sequence,Selector等)
    // If :
    //调用给定的函数。
    // ●如果返回true,则调用当前活动的子级并返回其状态。
    // ●否则,它将在不调用其子项的情况下返回Failure
    // While:
    //只要给定函数返回true,就返回Continue(因此,下一帧将再次从该节点开始,而不会评估所有先前的节点)。
    //子节点们将陆续被执行。
    //当函数返回false并且循环中断时,将返回Failure。
    // Call 
    //调用给定的函数,它将始终返回Success。是动作节点!
    //Repeat 
    //将连续执行给定次数的所有子节点。
    //始终返回Continue,直到达到计数,并返回Success。
    //Wait
    //将返回Continue,直到达到给定时间(首次调用时开始),然后返回Success。
    //Trigger 
    //允许在给定的动画师animator中设置Trigger参数(如果最后一个参数设置为false,则取消设置触发器)。始终返回成功。
    //SetBool
    //允许在给定的animator中设置布尔参数的值。始终返回成功
    //SetActive 
    //设置给定GameObject的活动/非活动状态。始终返回成功。
    /// </summary>
    namespace BTAI
    {
        public enum BTState
        {
            Failure,
            Success,
            Continue,
            Abort
        }
    
        /// <summary>
        /// 节点 对象工厂
        /// </summary>
        public static class BT
        {
            public static Root Root() { return new Root(); }
            public static Sequence Sequence() { return new Sequence(); }
            public static Selector Selector(bool shuffle = false) { return new Selector(shuffle); }
            public static Action RunCoroutine(System.Func<IEnumerator<BTState>> coroutine) { return new Action(coroutine); }
            public static Action Call(System.Action fn) { return new Action(fn); }
            public static ConditionalBranch If(System.Func<bool> fn) { return new ConditionalBranch(fn); }
            public static While While(System.Func<bool> fn) { return new While(fn); }
            public static Condition Condition(System.Func<bool> fn) { return new Condition(fn); }
            public static Repeat Repeat(int count) { return new Repeat(count); }
            public static Wait Wait(float seconds) { return new Wait(seconds); }
            public static Trigger Trigger(Animator animator, string name, bool set = true) { return new Trigger(animator, name, set); }
            public static WaitForAnimatorState WaitForAnimatorState(Animator animator, string name, int layer = 0) { return new WaitForAnimatorState(animator, name, layer); }
            public static SetBool SetBool(Animator animator, string name, bool value) { return new SetBool(animator, name, value); }
            public static SetActive SetActive(GameObject gameObject, bool active) { return new SetActive(gameObject, active); }
            public static WaitForAnimatorSignal WaitForAnimatorSignal(Animator animator, string name, string state, int layer = 0) { return new WaitForAnimatorSignal(animator, name, state, layer); }
            public static Terminate Terminate() { return new Terminate(); }
            public static Log Log(string msg) { return new Log(msg); }
            public static RandomSequence RandomSequence(int[] weights = null) { return new BTAI.RandomSequence(weights); }
    
        }
    
        /// <summary>
        /// 节点抽象类
        /// </summary>
        public abstract class BTNode
        {
            public abstract BTState Tick();
        }
    
        /// <summary>
        /// 包含子节点的组合 节点基类
        /// </summary>
        public abstract class Branch : BTNode
        {
            protected int activeChild;
            protected List<BTNode> children = new List<BTNode>();
            public virtual Branch OpenBranch(params BTNode[] children)
            {
                for (var i = 0; i < children.Length; i++)
                    this.children.Add(children[i]);
                return this;
            }
    
            public List<BTNode> Children()
            {
                return children;
            }
    
            public int ActiveChild()
            {
                return activeChild;
            }
    
            public virtual void ResetChildren()
            {
                activeChild = 0;
                for (var i = 0; i < children.Count; i++)
                {
                    Branch b = children[i] as Branch;
                    if (b != null)
                    {
                        b.ResetChildren();
                    }
                }
            }
        }
    
        /// <summary>
        /// 装饰节点    只包含一个子节点,用于某种方式改变这个节点的行为
        /// 比如过滤器(用于决定是否允许子节点运行的,如:Until Success, Until Fail等),这种节点的子节点应该是条件节点,条件节点一直检测“视线中是否有敌人”,知道发现敌人为止。
        /// 或者 Limit 节点,用于指定某个子节点的最大运行次数
        /// 或者 Timer节点,设置了一个计时器,不会立即执行子节点,而是等一段时间,时间到了开始执行子节点
        /// 或者 TimerLimit节点,用于指定某个子节点的最长运行时间。
        /// 或者 用于产生某个返回状态,
        /// </summary>
        public abstract class Decorator : BTNode
        {
            protected BTNode child;
            public Decorator Do(BTNode child)
            {
                this.child = child;
                return this;
            }
        }
    
        /// <summary>
        /// 顺序节点 (从左到右依次执行所有子节点,只要子节点返回Success就继续执行后续子节点,直到遇到Failure或者Runing, 
        /// 停止后续执行,并把这个节点返回给父节点,只有它的所有子节点都是Success他才会向父节点返回Success)
        /// </summary>
        public class Sequence : Branch
        {
            public override BTState Tick()
            {
                var childState = children[activeChild].Tick();
                switch (childState)
                {
                    case BTState.Success:
                        activeChild++;
                        if (activeChild == children.Count)
                        {
                            activeChild = 0;
                            return BTState.Success;
                        }
                        else
                            return BTState.Continue;
                    case BTState.Failure:
                        activeChild = 0;
                        return BTState.Failure;
                    case BTState.Continue:
                        return BTState.Continue;
                    case BTState.Abort:
                        activeChild = 0;
                        return BTState.Abort;
                }
                throw new System.Exception("This should never happen, but clearly it has.");
            }
        }
    
        /// <summary>
        /// 选择节点从左到右依次执行所有子节点 ,只要遇到failure就继续执行后续子节点,直到遇到一个节点返回Success或Running为止。向父节点返回Success或Running
        /// 所有子节点都是Fail, 那么向父节点凡湖Fail
        /// 选择节点 用来在可能的行为集合中选择第一个成功的。 比如一个试图躲避枪击的AI角色, 它可以通过寻找隐蔽点, 或离开危险区域, 或寻找援助等多种方式实现目标。
        /// 利用选择节点,他会尝试寻找Cover,失败后在试图逃离危险区域。
        /// </summary>
        public class Selector : Branch
        {
            public Selector(bool shuffle)
            {
                if (shuffle)
                {
                    var n = children.Count;
                    while (n > 1)
                    {
                        n--;
                        var k = Mathf.FloorToInt(Random.value * (n + 1));
                        var value = children[k];
                        children[k] = children[n];
                        children[n] = value;
                    }
                }
            }
    
            public override BTState Tick()
            {
                var childState = children[activeChild].Tick();
                switch (childState)
                {
                    case BTState.Success:
                        activeChild = 0;
                        return BTState.Success;
                    case BTState.Failure:
                        activeChild++;
                        if (activeChild == children.Count)
                        {
                            activeChild = 0;
                            return BTState.Failure;
                        }
                        else
                            return BTState.Continue;
                    case BTState.Continue:
                        return BTState.Continue;
                    case BTState.Abort:
                        activeChild = 0;
                        return BTState.Abort;
                }
                throw new System.Exception("This should never happen, but clearly it has.");
            }
        }
    
        /// <summary>
        /// 行为节点  调用方法,或运行协程。完成实际工作, 例如播放动画,让角色移动位置,感知敌人,更换武器,播放声音,增加生命值等。
        /// </summary>
        public class Action : BTNode
        {
            System.Action fn;
            System.Func<IEnumerator<BTState>> coroutineFactory;
            IEnumerator<BTState> coroutine;
            public Action(System.Action fn)
            {
                this.fn = fn;
            }
            public Action(System.Func<IEnumerator<BTState>> coroutineFactory)
            {
                this.coroutineFactory = coroutineFactory;
            }
            public override BTState Tick()
            {
                if (fn != null)
                {
                    fn();
                    return BTState.Success;
                }
                else
                {
                    if (coroutine == null)
                        coroutine = coroutineFactory();
                    if (!coroutine.MoveNext())
                    {
                        coroutine = null;
                        return BTState.Success;
                    }
                    var result = coroutine.Current;
                    if (result == BTState.Continue)
                        return BTState.Continue;
                    else
                    {
                        coroutine = null;
                        return result;
                    }
                }
            }
    
            public override string ToString()
            {
                return "Action : " + fn.Method.ToString();
            }
        }
    
        /// <summary>
        /// 条件节点   调用方法,如果方法返回true则返回成功,否则返回失败。
        /// 用来测试当前是否满足某些性质或条件,例如“玩家是否在20米之内?”“是否能看到玩家?”“生命值是否大于50?”“弹药是否足够?”等
        /// </summary>
        public class Condition : BTNode
        {
            public System.Func<bool> fn;
    
            public Condition(System.Func<bool> fn)
            {
                this.fn = fn;
            }
            public override BTState Tick()
            {
                return fn() ? BTState.Success : BTState.Failure;
            }
    
            public override string ToString()
            {
                return "Condition : " + fn.Method.ToString();
            }
        }
    
        /// <summary>
        /// 当方法为True的时候 尝试执行当前  子节点
        /// </summary>
        public class ConditionalBranch : Block
        {
            public System.Func<bool> fn;
            bool tested = false;
            public ConditionalBranch(System.Func<bool> fn)
            {
                this.fn = fn;
            }
            public override BTState Tick()
            {
                if (!tested)
                {
                    tested = fn();
                }
                if (tested)
                {
                    // 当前子节点执行完就进入下一个节点(超上限就返回到第一个)
                    var result = base.Tick();
                    // 没执行完
                    if (result == BTState.Continue)
                        return BTState.Continue;
                    else
                    {
                        tested = false;
                        // 最后一个子节点执行完,才会为Ture
                        return result;
                    }
                }
                else
                {
                    return BTState.Failure;
                }
            }
    
            public override string ToString()
            {
                return "ConditionalBranch : " + fn.Method.ToString();
            }
        }
    
        /// <summary>
        /// While节点   只要方法  返回True 就执行所有子节点, 否则返回 Failure
        /// </summary>
        public class While : Block
        {
            public System.Func<bool> fn;
    
            public While(System.Func<bool> fn)
            {
                this.fn = fn;
            }
    
            public override BTState Tick()
            {
                if (fn())
                    base.Tick();
                else
                {
                    //if we exit the loop
                    ResetChildren();
                    return BTState.Failure;
                }
    
                return BTState.Continue;
            }
    
            public override string ToString()
            {
                return "While : " + fn.Method.ToString();
            }
        }
    
        /// <summary>
        /// 阻塞节点  如果当前子节点是Continue 说明没有执行完,阻塞着,执行完之后在继续它后面的兄弟节点 不管成功失败。
        /// 如果当前结点是最后一个节点并执行完毕,说明成功!否则就是处于Continue状态。 
        /// 几个基本上是抽象节点, 像是让所有子节点都执行一遍, 当前子节点执行完就进入下一个节点(超上限就返回到第一个)
        /// </summary>
        public abstract class Block : Branch
        {
            public override BTState Tick()
            {
                switch (children[activeChild].Tick())
                {
                    case BTState.Continue:
                        return BTState.Continue;
                    default:
                        activeChild++;
                        if (activeChild == children.Count)
                        {
                            activeChild = 0;
                            return BTState.Success;
                        }
                        return BTState.Continue;
                }
            }
        }
    
        public class Root : Block
        {
            public bool isTerminated = false;
    
            public override BTState Tick()
            {
                if (isTerminated) return BTState.Abort;
                while (true)
                {
                    switch (children[activeChild].Tick())
                    {
                        case BTState.Continue:
                            return BTState.Continue;
                        case BTState.Abort:
                            isTerminated = true;
                            return BTState.Abort;
                        default:
                            activeChild++;
                            if (activeChild == children.Count)
                            {
                                activeChild = 0;
                                return BTState.Success;
                            }
                            continue;
                    }
                }
            }
        }
    
        /// <summary>
        /// 多次运行子节点(一个子节点执行一次就算一次)
        /// </summary>
        public class Repeat : Block
        {
            public int count = 1;
            int currentCount = 0;
            public Repeat(int count)
            {
                this.count = count;
            }
            public override BTState Tick()
            {
                if (count > 0 && currentCount < count)
                {
                    var result = base.Tick();
                    switch (result)
                    {
                        case BTState.Continue:
                            return BTState.Continue;
                        default:
                            currentCount++;
                            if (currentCount == count)
                            {
                                currentCount = 0;
                                return BTState.Success;
                            }
                            return BTState.Continue;
                    }
                }
                return BTState.Success;
            }
    
            public override string ToString()
            {
                return "Repeat Until : " + currentCount + " / " + count;
            }
        }
    
    
        /// <summary>
        /// 随机的顺序  执行子节点 
        /// </summary>
        public class RandomSequence : Block
        {
            int[] m_Weight = null;
            int[] m_AddedWeight = null;
    
            /// <summary>
            /// 每次再次触发时,将选择一个随机子节点
            /// </summary>
            /// <param name="weight">保留null,以便所有子节点具有相同的权重。
            /// 如果权重低于子节点, 则后续子节点的权重都为1</param>
            public RandomSequence(int[] weight = null)
            {
                activeChild = -1;
    
                m_Weight = weight;
            }
    
            public override Branch OpenBranch(params BTNode[] children)
            {
                m_AddedWeight = new int[children.Length];
    
                for (int i = 0; i < children.Length; ++i)
                {
                    int weight = 0;
                    int previousWeight = 0;
    
                    if (m_Weight == null || m_Weight.Length <= i)
                    {//如果没有那个权重, 就将权重 设置为1
                        weight = 1;
                    }
                    else
                        weight = m_Weight[i];
    
                    if (i > 0)
                        previousWeight = m_AddedWeight[i - 1];
    
                    m_AddedWeight[i] = weight + previousWeight;
                }
    
                return base.OpenBranch(children);
            }
    
            public override BTState Tick()
            {
                if (activeChild == -1)
                    PickNewChild();
    
                var result = children[activeChild].Tick();
    
                switch (result)
                {
                    case BTState.Continue:
                        return BTState.Continue;
                    default:
                        PickNewChild();
                        return result;
                }
            }
    
            void PickNewChild()
            {
                int choice = Random.Range(0, m_AddedWeight[m_AddedWeight.Length - 1]);
    
                for (int i = 0; i < m_AddedWeight.Length; ++i)
                {
                    if (choice - m_AddedWeight[i] <= 0)
                    {
                        activeChild = i;
                        break;
                    }
                }
            }
    
            public override string ToString()
            {
                return "Random Sequence : " + activeChild + "/" + children.Count;
            }
        }
    
    
        /// <summary>
        /// 暂停执行几秒钟。
        /// </summary>
        public class Wait : BTNode
        {
            public float seconds = 0;
            float future = -1;
            public Wait(float seconds)
            {
                this.seconds = seconds;
            }
    
            public override BTState Tick()
            {
                if (future < 0)
                    future = Time.time + seconds;
    
                if (Time.time >= future)
                {
                    future = -1;
                    return BTState.Success;
                }
                else
                    return BTState.Continue;
            }
    
            public override string ToString()
            {
                return "Wait : " + (future - Time.time) + " / " + seconds;
            }
        }
    
        /// <summary>
        /// 设置动画  trigger 参数
        /// </summary>
        public class Trigger : BTNode
        {
            Animator animator;
            int id;
            string triggerName;
            bool set = true;
    
            //如果 set == false, 则重置trigger而不是设置它。
            public Trigger(Animator animator, string name, bool set = true)
            {
                this.id = Animator.StringToHash(name);
                this.animator = animator;
                this.triggerName = name;
                this.set = set;
            }
    
            public override BTState Tick()
            {
                if (set)
                    animator.SetTrigger(id);
                else
                    animator.ResetTrigger(id);
    
                return BTState.Success;
            }
    
            public override string ToString()
            {
                return "Trigger : " + triggerName;
            }
        }
    
        /// <summary>
        /// 设置动画 boolean 参数
        /// </summary>
        public class SetBool : BTNode
        {
            Animator animator;
            int id;
            bool value;
            string triggerName;
    
            public SetBool(Animator animator, string name, bool value)
            {
                this.id = Animator.StringToHash(name);
                this.animator = animator;
                this.value = value;
                this.triggerName = name;
            }
    
            public override BTState Tick()
            {
                animator.SetBool(id, value);
                return BTState.Success;
            }
    
            public override string ToString()
            {
                return "SetBool : " + triggerName + " = " + value.ToString();
            }
        }
    
        /// <summary>
        /// 等待animator达到一个状态。
        /// </summary>
        public class WaitForAnimatorState : BTNode
        {
            Animator animator;
            int id;
            int layer;
            string stateName;
    
            public WaitForAnimatorState(Animator animator, string name, int layer = 0)
            {
                this.id = Animator.StringToHash(name);
                if (!animator.HasState(layer, this.id))
                {
                    Debug.LogError("The animator does not have state: " + name);
                }
                this.animator = animator;
                this.layer = layer;
                this.stateName = name;
            }
    
            public override BTState Tick()
            {
                var state = animator.GetCurrentAnimatorStateInfo(layer);
                if (state.fullPathHash == this.id || state.shortNameHash == this.id)
                    return BTState.Success;
                return BTState.Continue;
            }
    
            public override string ToString()
            {
                return "Wait For State : " + stateName;
            }
        }
    
        /// <summary>
        /// 设置 GameObject 的激活状态
        /// </summary>
        public class SetActive : BTNode
        {
    
            GameObject gameObject;
            bool active;
    
            public SetActive(GameObject gameObject, bool active)
            {
                this.gameObject = gameObject;
                this.active = active;
            }
    
            public override BTState Tick()
            {
                gameObject.SetActive(this.active);
                return BTState.Success;
            }
    
            public override string ToString()
            {
                return "Set Active : " + gameObject.name + " = " + active;
            }
        }
    
        /// <summary>
        /// 等待animator从SendSignal状态机行为 接收信号。   SendSignal : StateMachineBehaviour
        /// </summary>
        public class WaitForAnimatorSignal : BTNode
        {
            // 进入或退出动画都为 False, 只有执行中为True
            internal bool isSet = false;
            string name;
            int id;
    
            public WaitForAnimatorSignal(Animator animator, string name, string state, int layer = 0)
            {
                this.name = name;
                this.id = Animator.StringToHash(name);
                if (!animator.HasState(layer, this.id))
                {
                    Debug.LogError("The animator does not have state: " + name);
                }
                else
                {
                    SendSignal.Register(animator, name, this);
                }
            }
    
            public override BTState Tick()
            {
                if (!isSet)
                    return BTState.Continue;
                else
                {
                    isSet = false;
                    return BTState.Success;
                }
    
            }
    
            public override string ToString()
            {
                return "Wait For Animator Signal : " + name;
            }
        }
    
        /// <summary>
        /// 终止节点  切换到中止 状态
        /// </summary>
        public class Terminate : BTNode
        {
    
            public override BTState Tick()
            {
                return BTState.Abort;
            }
    
        }
    
        /// <summary>
        /// Log  输出Log 的节点
        /// </summary>
        public class Log : BTNode
        {
            string msg;
    
            public Log(string msg)
            {
                this.msg = msg;
            }
    
            public override BTState Tick()
            {
                Debug.Log(msg);
                return BTState.Success;
            }
        }
    
    }
    
    #if UNITY_EDITOR
    namespace BTAI
    {
        public interface IBTDebugable
        {
            Root GetAIRoot();
        }
    }
    #endif

     

     

    using BTAI;
    using System.Collections.Generic;
    using UnityEditor;
    using UnityEngine;
    
    namespace Gamekit2D
    {
    
        /// <summary>
        /// 运行是查看  行为树中所有节点的状态 
        /// </summary>
        public class BTDebug : EditorWindow
        {
            protected BTAI.Root _currentRoot = null;
    
    
            [MenuItem("Kit Tools/Behaviour Tree Debug")]
            static void OpenWindow()
            {
                BTDebug btdebug = GetWindow<BTDebug>();
                btdebug.Show();
            }
    
            private void OnGUI()
            {
                if (!Application.isPlaying)
                {
                    EditorGUILayout.HelpBox("Only work during play mode.", MessageType.Info);
                }
                else
                {
                    if (_currentRoot == null)
                        FindRoot();
                    else
                    {
                        RecursiveTreeParsing(_currentRoot, 0, true);
                    }
                }
            }
    
            void Update()
            {
                Repaint();
            }
    
            void RecursiveTreeParsing(Branch branch, int indent, bool parentIsActive)
            {
                List<BTNode> nodes = branch.Children();
    
                for (int i = 0; i < nodes.Count; ++i)
                {
                    EditorGUI.indentLevel = indent;
    
                    bool isActiveChild = branch.ActiveChild() == i;
                    GUI.color = (isActiveChild && parentIsActive) ? Color.green : Color.white;
                    EditorGUILayout.LabelField(nodes[i].ToString());
    
                    if (nodes[i] is Branch)
                        RecursiveTreeParsing(nodes[i] as Branch, indent + 1, isActiveChild);
                }
            }
    
            void FindRoot()
            {
                if (Selection.activeGameObject == null)
                {
                    _currentRoot = null;
                    return;
                }
    
                IBTDebugable debugable = Selection.activeGameObject.GetComponentInChildren<IBTDebugable>();
    
                if (debugable != null)
                {
                    _currentRoot = debugable.GetAIRoot();
                }
            }
    
        }
    }

               就是在  菜单    “Kit Tools/Behaviour Tree Debug" 可以查看     TestBT.cs 对象所在行为树

     

     

    展开全文
  • Behavior Designer 是专为每个人设计的行为树实现 - 程序员、艺术家、设计师。 Behavior Designer 提供具有强大的 API 的直观可视化编辑器,帮您轻松创建新任务。它还包含数百个任务,PlayMaker 集成和广泛的第三方...
  • 行为树比较好比较详细的两份资料。包括一些实现细节和概念 behavior tree
  • 提出一种新的恶意代码检测技术,能自动检测和遏制(未知)恶意代码,并实现了原型系统。首先用支持向量机对恶意代码样本的行为构造分类器,来判断样本是否是恶意代码,同时对恶意代码提取出特征码。运行在主机的代理...
  • Unity3D行为树用法实例

    千次阅读 2021-04-07 18:28:14
    新建的工程,菜单栏没有行为树插件,我们需要首先安装插件 首先,去网上下载 Behavior Designer(本例中使用 Behavior Designer 1.6.4.unitypackage),然后在 Project 视图的 Assets 目录上右键,选择 Import ...
  • Java游戏服务器开发之行为树

    万次阅读 2018-05-25 16:39:07
    Java游戏服务器开发之行为树 之前有看到过状态机、行为树这一块的内容,但是没有认真细看,现在终于静下心来认真看了看,就看了别人的实现方式(网上代码没有Java实现的), 然后使用Java语言实现了一下 运行之后,在...
  • 行为树(Behavior trees)

    千次阅读 2021-07-08 14:36:09
    文章目录行为树组合节点次序节点选择节点并行节点修饰节点逆变节点成功节点重复节点叶子节点 行为树 行为树由多种不同类型的节点组成,这些节点都会返回三种状态中的一种作为节点的运行结果。三种状态分别是: 成功...
  • 行为树的原理及实现

    万次阅读 2016-08-04 11:09:56
    查阅了一些行为树资料,目前最主要是参考了这篇文章,看完后感觉行为树实乃强大,绝对是替代状态机的不二之选。但从理论看起来很简单的行为树,真正着手起来却发现很多细节无从下手。 总结起来,就是: ...
  • go版本行为树

    千次阅读 2017-11-30 16:43:54
    本人之前是做Unity3d手游的,对这些有所涉猎,最近在用go语言写后台,我们刚开发了几个服务器,需要在后台编写一个程序监控服务的可用性,如果不可用可用报警之类的,然后我就想到了用行为树实现这个功能。...
  • 对于角色挂机自动战斗,Unity有行为树插件Behavior Designer可以实现,但不能实现战斗逻辑热更,所以我用Lua对着Behavior Designer重新实现了部分基础功能,这样,使用Lua版的行为树实现挂机自动战斗,就可以热更啦...
  • 行为树游戏AI设计

    千次阅读 2022-02-22 09:17:45
    上一篇写的状态机(FSM)(http://t.csdn.cn/DPYUb)有一定的局限性,当我们敌人有多种行为逻辑时,像还是拿我寒假沉迷刷刷刷的无主之地3中,普通的机械警察等拿枪的小怪都不止冲向玩家和攻击玩家两种状态,有很多种...
  • 第6-2课:决策树、博弈树和行为树

    千次阅读 2020-09-22 12:17:12
    在以各种“XX学习”为代表的人工智能技术普及之前,游戏里常见的角色 AI 都是各种预设的行为逻辑,比如博弈树和行为树,当然也会用到各种专家知识库。当这些预设的行为逻辑足够复杂的时候,往往会让游戏玩家觉得游戏...
  • Java行为树是一个Java框架,用于轻松地构建和运行通用行为树。 此处,“行为树”是指用于控制视频游戏中角色行为的技术。 重要说明:我们的源代码已移至GitHub。 您可以在以下位置找到它:...
  • 在游戏自动化测试领域,行为树...但是如果真要用行为树框架去几乎完全替代纯代码编程,做很细致的的逻辑控制,这个想法是有误的。 行为树本身最方便的地方是能够通过可视化界面的方式去维护玩家行为(以业务逻辑的拆分
  • 开发游戏AI之行为树

    千次阅读 2017-06-12 09:48:29
    采用行为树快速开发游戏AI 腾讯互娱研发部引擎技术中心 黄晨   游戏AI的目标之一就是要找到一种简单并可扩展的开发逻辑的方案,常用的技术包括有限状态机(FSM)、分层有限状态机(HFSM)、面向目标的动作规划...
  • UE4-AI行为树编辑器功能扩展

    千次阅读 2020-03-19 16:00:45
    虚幻4引擎的行为树编辑器只支持在单颗行为树中查找某一节点。项目开发后期行为树节点及黑板变量庞杂,特别是在多人开发制作AI行为树时,若前期文档不够完善,后期想要对之前的节点或黑板变量修改,很难知道某一节点...
  • 二叉查找的编程与实现 C语言

    万次阅读 热门讨论 2022-07-10 15:29:42
    输入数据的第一行为一个正整数T,表示测试数据的组数,即共 T 组测试数据。每组测试数据的第一行输入正整数 n(5 ≤n ≤20),第二行输入n个整数,要求依次完成以下工作:1)以这n个整数生成一棵用链式存储结构存储的...
  • ros2 nav2 行为树插件引擎原理和应用

    千次阅读 2020-11-26 15:53:17
    文章目录Nav2 行为树插件引擎原理和应用一个完整应用demoGroot行为树设计和监视器安装使用Groot实时监视行为树行为树运行库行为树插件引擎自定义...action服务节点行为树action与ROS2 action相结合实现功能和逻辑的解耦...
  • 小眼游戏架构:战斗篇:行为树

    千次阅读 2019-07-28 13:21:58
    自己编写行为树框架 1. 配置:我们代码中写的节点类和事件类的列表,策划在编写AI脚本的时候需要; 2. 注册:根据策划编写的AI文件将事件实例和节点实例绑定在一起; 3. 触发:事件触发的时候就会执行绑定的节点; 4...
  • 行为树(Behavior Tree)详细介绍

    万次阅读 多人点赞 2021-03-08 17:56:13
    行为树是控制AI实体决策流程的分层节点树。 在树的范围内,叶子是控制AI实体的实际命令,而形成分支的则是各种类型的效用节点,它们控制AI沿着树走,以达到最适合这种情况的命令序列。 这些树可能非常深,节点调用...
  • 行为树简介 与有限状态机不同,行为树是一个分层节点树,它控制决策流和“任务”的执行,或者我们将进一步称之为“动作”。 树的叶子是实际命令,即我们的协调组件与系统其余部分交互的位置。 例如,在面向服务的...
  • Behavior Designer 行为树中文版教程

    千次阅读 2018-04-20 23:24:30
    Behavior Designer 是一个行为树插件!是为了让设计师,程序员,美术人员方便使用的可视化编辑器!Behavior Designer 提供了强大的 API 可以让你轻松的创建 tasks(任务),配合 uScript 和 PlayMaker 这样的插件,...
  • 行为树 Behavior Tree 实例 五

    千次阅读 2018-01-18 19:11:46
    行为树 Behavior Tree 实例 前篇完成了 Behavior Tree 的核心代码 下面通过实际例子使用Unity来测试核心代码 下面是行为树结构图 本篇实现 选择节点 1 的所有内容 执行流程如下 遍历根节点 ...
  • Unity3D行为树系统编程实现

    千次阅读 2015-03-06 12:32:19
    Unity3D行为树系统编程实现行为树在AI领域使用十分广泛,...关于行为树的介绍和行为树概念请看《使用行为树behavior-tree实现游戏ai》这是业界一个前辈写的文章我只是代为转载。 很久没有写文章了,如果文笔

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 139,402
精华内容 55,760
关键字:

行为树代码实现