行为树(Behavior Trees)
行为树(Behavior Trees)是一种在游戏开发中广泛使用的AI设计模式,主要用于描述AI的行为和决策过程,实现更加智能和自然的游戏AI。它由多个节点组成,每个节点代表一个行为或决策,按照特定的方式连接在一起,形成一个树状结构。
在行为树中,根节点是AI的起点,通过遍历子节点来决策AI的行为。节点有以下三种状态:成功(Success)、失败(Failure)和运行(Running)。前两个通知其父节点其操作是成功还是失败。第三种意味着尚未确定成功或失败,并且该节点仍在运行。下次树被选择时,该节点将再次被选择,此时它将再次有机会成功,失败或继续运行。
行为树的节点有以下几种主要原型:
-
组合控制节点(Composite):一种将多个子节点组合在一起的节点,用于实现复杂的行为和决策逻辑。主要包括次序节点(Sequencer)和选择节点(Selector)。次序节点并行执行多个子节点,直到所有子节点都返回成功或者任意一个子节点返回失败为止。选择节点按照顺序执行子节点,当某个子节点返回成功时,停止执行并返回成功。


-
修饰节点(Decorator):一种特殊的节点,它不执行具体的行为或决策,而是修饰其它节点的行为或决策。主要包括逆变节点(Inverter)和重复节点(Repeater)。逆变节点可以将子节点的结果倒转,比如子节点返回了失败,则这个修饰节点会向上返回成功,以此类推。重复节点重复执行其子节点指定的次数或者一直重复执行,直到其子节点返回成功或者失败。


-
叶节点(Leaf):树的最末端,就是这些AI实际上去做事情的命令。
行为树通过模拟树状结构来描述AI的行为和决策过程,使得AI的行为更加灵活、自然和智能。
代码实现

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;namespace BehaviourTree
{public class BehaviourTree : MonoBehaviour{private Node root = null;private Blackboard blackboard;public Node Root{get{return root;}set{root= value;}}void Start(){Init();}void Update(){if(root!=null&& gameObject!=null){root.Evaluate(gameObject.transform,blackboard);}}protected virtual void Init(){blackboard = new Blackboard();}}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;namespace BehaviourTree
{//行为树共享数据public class Blackboard{private Dictionary<string,object> Data;public Blackboard(){Data = new Dictionary<string, object>();}//获取数据public T Get<T>(string key){if(Data.ContainsKey(key)){return (T)Data[key];}return default;}//添加数据public void Add<T>(string key,T value){if(Data.ContainsKey(key)){Data[key] = value;}else{Data.Add(key,value);}}//删除数据public void Remove(string key){if(Data.ContainsKey(key)){Data.Remove(key);}else{Debug.Log("数据不存在 "+key);}}}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;namespace BehaviourTree
{//状态类型public enum Status{Running, //运行中Failure, // 失败Success, //成功}//节点基类public abstract class Node{protected Node parent;protected Status status;Status Status{get{return status;}set{status = value;}}public Node(){}//声明节点状态返回方法public abstract Status Evaluate(Transform transform,Blackboard blackboard);}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;namespace BehaviourTree
{//复合节点类public abstract class Composite : Node{//子节点列表protected List<Node> children = new List<Node>();protected int index;//子节点下标计数protected Composite(List<Node> children){index = 0;foreach(var child in children) {this.children.Add(child);}}}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;namespace BehaviourTree
{//修饰器节点public abstract class Decorator : Node{//子节点列表protected List<Node> children;protected Decorator(List<Node> children){children = new List<Node>(){};foreach(var child in children) {this.children.Add(child);}}}}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;namespace BehaviourTree
{//选择节点,所有子节点都为失败则失败public class Selector : Composite{public Selector(List<Node> children) : base(children){}//所有子节点都为失败则失败public override Status Evaluate(Transform transform, Blackboard blackboard){if(index>=children.Count){index = 0;return Status.Success;}var status = children[index].Evaluate(transform,blackboard);switch(status){case Status.Success:index = 0;return Status.Success;case Status.Failure:index+=1;if(index>=children.Count){index = 0;return Status.Failure;}return Status.Running;case Status.Running:return Status.Running;default:return Status.Failure;}}}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;namespace BehaviourTree
{//顺序节点,所有子节点成功才成功public class Sequencer : Composite{public Sequencer(List<Node> children) : base(children){}//所有子节点成功才成功public override Status Evaluate(Transform transform, Blackboard blackboard){if(index>=children.Count){index = 0;return Status.Success;}var status = children[index].Evaluate(transform,blackboard);switch(status){case Status.Success:index+=1;if(index>=children.Count){index = 0;return Status.Success;}return Status.Running;case Status.Failure:return Status.Failure;case Status.Running:return Status.Running;default:return Status.Failure;}}}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;namespace BehaviourTree
{//任务节点,这里会处理对象具体行为逻辑(叶节点)public abstract class Task : Node{}
}
简单使用
实现敌人在两点之间巡逻,人物靠近会变红温并停止移动,人物远离时继续巡逻

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;
using System.ComponentModel.Design.Serialization;
using System.Linq;namespace BehaviourTree
{public class EnemyBT : BehaviourTree{Vector3 aPos = new Vector3(4.07999992f,-2.21000004f,-2);Vector3 bPos = new Vector3(4.07999992f,1.65999997f,-2)protected override void Init(){//调用基类初始化base.Init();//创建变红节点TurnRed turnRed = new TurnRed();//创建巡逻节点Patrol patrol = new Patrol(aPos,bPos);//创建查找节点FindObject findObject = new FindObject();//创建侦查节点子节点序列Node[] spyChildren = {findObject,turnRed};//创建顺序节点用于执行侦查行为EnemySequencer enemySequencer = new EnemySequencer(spyChildren.ToList());//创建根节点子节点序列Node[] selectorChildren = {enemySequencer,patrol};//创建选择节点用于处理侦查和巡逻行为EnemySelector enemySelector = new EnemySelector(selectorChildren.ToList());//将选择节点设置为根节点Root = enemySelector;}}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;namespace BehaviourTree
{public class EnemySelector : Selector{public EnemySelector(List<Node> children) : base(children){}}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;namespace BehaviourTree
{public class EnemySequencer : Sequencer{public EnemySequencer(List<Node> children) : base(children){}}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;
using DG.Tweening;namespace BehaviourTree
{public class FindObject : Task{float radius = 5f;int layer = 512;public override Status Evaluate(Transform transform, Blackboard blackboard){Collider2D obj = Physics2D.OverlapCircle(transform.position,radius,layer);if(obj!=null){return Status.Success; }transform.gameObject.GetComponent<SpriteRenderer>().DOColor(Color.white,1);return Status.Failure;}}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;
using DG.Tweening;namespace BehaviourTree
{//在aPoint和bPoint之间来回移动public class Patrol : Task{Vector3 aPoint;Vector3 bPoint;float speed = 4f;Vector3 target;public Patrol(Vector3 aPos,Vector3 bPos){aPoint = aPos;bPoint = bPos;target = aPoint;}public override Status Evaluate(Transform transform, Blackboard blackboard){if(Vector2.Distance(transform.position,target)<0.1f){if(target == aPoint){target = bPoint;}else{target = aPoint;}}else{transform.position = Vector2.MoveTowards(transform.position,target,Time.deltaTime*speed);}return Status.Success;}}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;
using DG.Tweening;namespace BehaviourTree
{//变红public class TurnRed : Task{Vector3 bPoint;public override Status Evaluate(Transform transform, Blackboard blackboard){transform.gameObject.GetComponent<SpriteRenderer>().DOColor(Color.red,1);return Status.Success;}}
}相关文章:
行为树(Behavior Trees)
行为树(Behavior Trees)是一种在游戏开发中广泛使用的AI设计模式,主要用于描述AI的行为和决策过程,实现更加智能和自然的游戏AI。它由多个节点组成,每个节点代表一个行为或决策,按照特定的方式连接在一起&a…...
opensssl BIO方式https客户端
废话不多说,代码中使用了两种https客户端的实现方式。 #include <windows.h> #include <WinSock.h>#pragma comment(lib,"ws2_32.lib") #include "../include/openssl\ssl.h" #include "../include/openssl\err.h"#pragm…...
JavaScript之判断是否整数、取余、取整、进制、位或、ES6
MENU 方法一方式二方式三方式四方式五结束语 方法一 使用取余运算符判断,利用任何整数都会被1整除的原理,即余数是0的特点,通过这个规则来判断是否是整数。 let isInteger (val) > val % 1 0;// true isInteger(5); // false isInteger(…...
【打造你自己的Shell:编写定制化命令行体验】
本节重点: 学习进程创建,fork/vfork 学习到进程等待 学习到进程程序替换, 微型shell,重新认识shell运行原理 学习到进程终止,认识$? 一、进程创建 1.1.fork函数初识 在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程…...
PGSQL主键序列
PostgreSQL和 MySQL数据库还是有一定的区别。 下面了解一下 PGSQL的主键序列。 一、主键 1、系统自带主键序列 在 PostgreSQL 中,GENERATED BY DEFAULT 和 GENERATED ALWAYS 是用于定义自动生成的列(Generated Column)的选项。一般可作用…...
pg14.2迁移至KingbaseV8R6后部分表记录数为空
pg14.2迁移至KingbaseV8R6后部分表记录数为空 问题描述 kdts工具迁移详情里显示表数据已迁移成功,但是迁移后测试发现部份表记录数为空 分别查看源库和目标库表记录数 --源库 select count(*) from aaf_sys_param order by 1; 229条--目录库 select count(*) fr…...
【Spring 篇】深入解析SpringMVC的组件魅力
SpringMVC,这个名字在Java Web开发者的耳边仿佛是一首动听的旋律,携着轻盈的氛围,带给我们一种愉悦的编程体验。但是,当我们深入探寻这个框架时,它的魅力远不止表面的简单,它由许多组件构成,每个…...
HPsocket 在 C# 中的运用:一款优秀的 socket 通信框架
摘要:本文将为您详细介绍 HPsocket,一款适用于 win32 平台的 socket 通信框架。同时,我们还将探讨如何在 C# 项目中使用 HPsocket,实现网络通信功能。通过本文,您将深入了解 HPsocket 的特点、优势以及在 C# 中的实际应…...
黑豹程序员-MyBatisPlus封装SQL的where条件的对象 QueryWrapper
说明 我们使用MybatisPlus时,我们可以不直接通过SQL操作数据库,而使用面向对象的方式。 其中遇到一个问题,就是如何用面向对象的方式实现 SQL中的where条件拼接。 MybatisPlus很体贴,它提供了一个QueryWrapper,查询包…...
每日一题——LeetCode1252.奇数值单元格的数目
进阶:你可以设计一个时间复杂度为 O(n m indices.length) 且仅用 O(n m) 额外空间的算法来解决此问题吗? 方法一 直接模拟: 创建一个n x m的矩阵,初始化所有元素为0,对于indices中的每一对[ri,ci],将矩…...
C#学习笔记3-函数与单元测试
现在开始参考书籍变为:《C# 12 and .NET 8 – Modern Cross-Platform Development.Mark Price》 函数 Writing, Debugging, and Testing Functions 写函数Debug运行时 logging单元测试 写函数 一个有着 XML 注释的函数 这里直接举一个例子: Numbe…...
osg屏幕事件处理器和状态集操控器学习
1 osgViewer::WindowSizeHandler 该事件处理器提供了对窗体屏幕的控制,功能如下: 按住或再次键盘f键,则三维窗体在全屏和退出全屏之间切换; 按住键盘>键,则屏幕分辨率增加; 按住键盘<键,则屏幕分辨率减小; 2 osgGA::StateSetManipulator 该事件处理器是状态集操…...
中国泛娱乐出海视频字幕解决方案
随着企业泛娱乐出海越来越成为热门,自动加载视频字幕需求变得越来越普遍,这能够为用户观众提供更好的视频体验。此次九河云为客户带来了aws视频字幕解决方案,满足客户视频字幕生成、翻译及后续编辑等完整工作流的需求。 客户价值:…...
iOS原生应用屏幕适配完整流程
1. 已iPhone 11 布局为设计布局,其他机型已这个来适配 2.变量与控件对应关系 txtViewer: txtAccount txtpwd seg btnOk 3.适配方法实现: //iOS屏幕适配 -(vo...
【征服redis8】Redis的AOF持久化
Redis 支持多种持久化方式来保证数据的可靠性和持久性。前面我们介绍了RDB方式。我们我们介绍第二种方式——AOF(Append Only File)机制是一种常用的持久化方式,它记录了所有对 Redis 数据库进行修改的命令,在 Redis 重启时可以使…...
【动态规划】【二分查找】【C++算法】730. 统计不同回文子序列
作者推荐 【动态规划】【数学】【C算法】18赛车 涉及知识点 动态规划 二分查找 LeetCode730. 统计不同回文子序列 给你一个字符串 s ,返回 s 中不同的非空回文子序列个数 。由于答案可能很大,请返回对 109 7 取余 的结果。 字符串的子序列可以经由…...
android 和 opencv 开发环境搭建
本文详细说明给android项目添加opencv库的详细步骤,并通过实现图片灰度化来查看配置是否成功。 下载OPENCV ANDROID SDK 到官网下载 打开 https://opencv.org/releases/ 选择android,下载完成后解压出下面的文件: 安装android sdk 和 ndk …...
elasticsearch[一]-索引库操作(轻松创建)、文档增删改查、批量写入(效率倍增)
elasticsearch[一]-索引库操作(轻松创建)、文档增删改查、批量写入(效率倍增) 1、初始化 RestClient 在 elasticsearch 提供的 API 中,与 elasticsearch 一切交互都封装在一个名为 RestHighLevelClient 的类中,必须先完成这个对象的初始化,…...
tp6框架中Http类 请求的header、body参数传参 及post、file格式
引入Http类: 在需要使用的地方引入Http类: use think\facade\Http; GET请求示例:$response Http::get(https://example.com/api/resource); 设置Header参数: $headers [ Authorization > Bearer YourAccessToken, Conte…...
基于极限学习机的图像处理,基于ELM的图像分割,基于极限学习机的细胞分割
目录 背影 极限学习机 基于极限学习机的图像,基于ELM的图像分割 主要参数 MATLAB代码 效果图 结果分析 展望 完整代码下载链接:基于极限学习机的图像,基于ELM的图像分割(代码完整,数据齐全)资源-CSDN文库 https://download.csdn.net/download/abc991835105/88759192 背…...
从Argo+K8S到Daft on Ray:我们如何将自动驾驶数据预处理端到端效率提升70%
从ArgoK8S到Daft on Ray:自动驾驶数据预处理架构升级实战 自动驾驶行业的数据处理正面临前所未有的挑战。随着传感器数量和数据采集频率的指数级增长,传统数据处理架构在效率、灵活性和成本效益方面逐渐显露出瓶颈。本文将深入剖析一个真实案例ÿ…...
libcimbar视觉传输工具实战指南:跨设备无网络数据传输解决方案
libcimbar视觉传输工具实战指南:跨设备无网络数据传输解决方案 【免费下载链接】libcimbar Optimized implementation for color-icon-matrix barcodes 项目地址: https://gitcode.com/GitHub_Trending/li/libcimbar 一、核心价值解析:突破网络限…...
拯救受损二维码:用QRazyBox实现高效恢复的4个实战策略
拯救受损二维码:用QRazyBox实现高效恢复的4个实战策略 【免费下载链接】qrazybox QR Code Analysis and Recovery Toolkit 项目地址: https://gitcode.com/gh_mirrors/qr/qrazybox QRazyBox是一款专业的二维码分析与恢复工具包(QR Code Analysis …...
别再让你的Druid监控裸奔了!手把手教你配置账户密码与访问控制
Druid监控安全加固实战:从零构建企业级防护体系 在Java生态中,Druid作为阿里巴巴开源的数据库连接池,凭借其强大的监控功能成为众多企业的标配组件。但令人担忧的是,超过60%的生产环境存在Druid监控页面暴露的安全隐患——这相当于…...
Spring Boot项目必备:用Arthas实现MyBatis Mapper热加载的完整配置流程
Spring Boot项目必备:用Arthas实现MyBatis Mapper热加载的完整配置流程 在持续交付的微服务架构中,开发团队经常面临一个共同挑战:每次修改MyBatis的Mapper XML文件后,都需要重启服务才能验证变更效果。这种低效的反馈循环严重拖慢…...
源码级解耦:企业级 AI 视频平台的微服务架构设计与二次开发实战
引言:定制化需求的“最后一公里”难题 在安防 AI 项目的交付链条中,集成商和技术团队往往处于一个尴尬的境地:市面上的成熟 SaaS 平台虽然开箱即用,但缺乏核心的源码级定制能力,一旦遇到客户特殊的业务逻辑(…...
AIGC技术实操:AI生图、AI视频开发与工具集成
2026年,AIGC技术已从“玩具级应用”走向“产业级工具”,其中AI生图、AI视频成为开发者的热门布局领域,据统计,AIGC/传媒领域商业化进程最快,MCN行业人工智能渗透率超60%,广告行业渗透率达55%。对于开发者而…...
Python量化交易系统:专业回测与组合优化
先把最重要的前提说清楚:国内禁止未经许可的程序化自动交易,下面只做量化研究、回测、信号分析,不含实盘自动下单这套是专业完整版量化系统,Python 可直接运行,结构完整、可扩展包含你要的所有高级功能:多股…...
如何用CodeMaker将Java/Scala开发效率提升300%?5个核心技巧带你掌握智能代码生成
如何用CodeMaker将Java/Scala开发效率提升300%?5个核心技巧带你掌握智能代码生成 【免费下载链接】CodeMaker A idea-plugin for Java/Scala, support custom code template. 项目地址: https://gitcode.com/gh_mirrors/co/CodeMaker 作为Java/Scala开发者&a…...
从SEED-Labs实验到实战:手把手教你编写无零字节的x86 Shellcode(附完整代码)
从SEED-Labs实验到实战:手把手教你编写无零字节的x86 Shellcode(附完整代码) 当你第一次看到"Shellcode"这个词时,可能会联想到某种神秘的编程黑魔法。实际上,它是安全研究中最具实用价值的技能之一——一段…...
