行为树简介

行为树(Behavior Tree) 在行为树中,根节点是AI的起点,通过遍历子节点来决策AI的行为。前两个通知其父节点其操作是成功还是失败。下次树被选择时,该节点将再次被选择,此时它将再次有机会成功,失败或继续运行。行为树(Behavior Trees)是一种在游戏开发中广泛使用的AI设计模式,主要用于描述AI的行为和决策过程,实现更加智能和自然的游戏AI。它由多个节点组成,每个节点代表一个行为或决策,按照特定的方式连接在一起,形成一个树状结构。行为树通过模拟树状结构来描述AI的行为和决策过程,使得AI的行为更加灵活、自然和智能。 阅读详情

本文大致介绍了行为树是什么,适合用于复习行为树的概念,或对行为树做一个大致了解。

什么是行为树

行为树是一种用于控制游戏AI或机器人逻辑的树形结构,它按照层级组织决策和行为,从根节点向下遍历执行。

为什么需要行为树?

状态机在处理复杂AI时会出现状态爆炸——10个行为可能需要20+个状态转换。行为树通过节点组合解决这个问题,让你像搭积木一样构建AI逻辑,无需硬编码状态转换。
一般最简单的行为树可能会这样调用:

void Start()
{
	behaviorTree = new BehaviorTree();
}
// 每帧调用
void Update()
{
	behaviorTree.Execute();
}

行为树的核心组成

行为树由三种核心节点类型构成:

  • 条件节点(Condition) 用于检查游戏状态是否满足某个条件,例如“敌人是否可见”或“生命值是否低于30%”。
  • 动作节点(Action) 用于执行具体的行为,例如“移动到目标位置”或“播放攻击动画”。
  • 组合节点(Composite) 用于组合多个子节点,控制它们的执行顺序和逻辑关系。

节点状态枚举

在开始代码之前,我们先定义节点的三种执行状态。

public enum NodeStatus
{
    Running,  // 节点正在执行中,需要在下一帧继续运行
    Success,  // 节点执行成功,返回正值结果
    Failure   // 节点执行失败,返回负值结果
}

基础节点类

所有节点都继承自抽象的Node类,它定义了行为树的基本框架。每个节点都有一个名称用于调试,每个节点都可以有子节点,并且实现了Tick方法来执行逻辑。

// 行为树节点基类
public abstract class Node
{
    // 节点名称,便于调试时识别
    protected string name;
    
    // 子节点列表,用于组合节点管理多个子节点
    protected List<Node> children = new List<Node>();

    // 构造函数,初始化节点名称
    public Node(string name)
    {
        this.name = name;
    }

    // 添加子节点的方法
    public void AddChild(Node child)
    {
        children.Add(child);
    }

    // 抽象方法,由子类实现具体的执行逻辑
    public abstract NodeStatus Tick();
}

条件节点

所有节点都继承自抽象的Node类,它定义了行为树的基本框架。每个节点都有一个名称用于调试,每个节点都可以有子节点,并且实现了Tick方法来执行逻辑。

// 条件节点:用于检查游戏状态是否满足某个条件
public class ConditionNode : Node
{
    // 条件检查委托,返回true表示条件满足
    private Func<bool> condition;

    public ConditionNode(string name, Func<bool> condition) : base(name)
    {
        this.condition = condition;
    }

    // 执行条件检查
    public override NodeStatus Tick()
    {
        // 如果条件满足,返回成功;否则返回失败
        return condition() ? NodeStatus.Success : NodeStatus.Failure;
    }
}

组合节点详解

选择器

选择器从左到右依次尝试执行子节点,直到某个子节点返回成功为止。用于表示“一系列选项中尝试执行一个”的逻辑,例如尝试攻击敌人,如果攻击不到则尝试逃跑。

// 选择器节点:从左到右尝试执行子节点,找到第一个成功的就停止
public class Selector : Node
{
    public Selector(string name) : base(name) { }

    public override NodeStatus Tick()
    {
        // 遍历所有子节点
        foreach (var child in children)
        {
            // 执行子节点并获取结果
            NodeStatus status = child.Tick();

            // 如果子节点成功,立即返回成功
            if (status == NodeStatus.Success)
            {
                return NodeStatus.Success;
            }

            // 如果子节点正在运行,也立即返回(保持运行状态)
            if (status == NodeStatus.Running)
            {
                return NodeStatus.Running;
            }
            
            // 如果子节点失败,继续尝试下一个子节点
        }

        // 所有子节点都失败,返回失败
        return NodeStatus.Failure;
    }
}

序列

序列从左到右依次执行所有子节点,只有当所有子节点都成功时才返回成功。用于表示“必须按顺序完成所有步骤”的逻辑,例如先检查敌人距离,再转向敌人,最后执行攻击。

// 序列节点:从左到右执行所有子节点,所有成功才返回成功
public class Sequence : Node
{
    public Sequence(string name) : base(name) { }

    public override NodeStatus Tick()
    {
        // 假设整体成功
        bool anyRunning = false;

        // 遍历所有子节点
        foreach (var child in children)
        {
            // 执行子节点并获取结果
            NodeStatus status = child.Tick();

            // 如果子节点失败,立即返回失败(序列中断)
            if (status == NodeStatus.Failure)
            {
                return NodeStatus.Failure;
            }

            // 如果子节点正在运行,记录状态并继续
            if (status == NodeStatus.Running)
            {
                anyRunning = true;
            }
        }

        // 如果有子节点正在运行,返回Running;否则返回成功
        return anyRunning ? NodeStatus.Running : NodeStatus.Success;
    }
}

随机选择器

随机选择器在执行前会打乱子节点的顺序,然后像普通选择器一样从打乱后的第一个节点开始尝试。例如,士兵执行“休息”逻辑时,它可以随机选择“散步”或“睡觉”。

// 随机选择器:打乱子节点顺序后执行选择器逻辑
public class RandomSelector : Node
{   
    public RandomSelector(string name) : base(name) { }

    public override NodeStatus Tick()
    {    
        // 打乱顺序
        var shuffledChildren = RandomShuffle(children);

        // 按打乱后的顺序执行选择器逻辑
        foreach (var child in shuffledChildren)
        {
            NodeStatus status = child.Tick();

            if (status == NodeStatus.Success)
            {
                return NodeStatus.Success;
            }

            if (status == NodeStatus.Running)
            {
                return NodeStatus.Running;
            }
        }

        return NodeStatus.Failure;
    }
}

你还可以实现一个“加权随机选择器”。

随机序列

同随机选择器。

// 随机序列:打乱子节点顺序后执行序列逻辑
public class RandomSequence : Node
{
    private Random random = new Random();

    public RandomSequence(string name) : base(name) { }

    public override NodeStatus Tick()
    {
        // 打乱顺序
        var shuffledChildren = RandomShuffle(children);
        
        // 按打乱后的顺序执行序列逻辑
        bool anyRunning = false;

        foreach (var child in shuffledChildren)
        {
            NodeStatus status = child.Tick();

            if (status == NodeStatus.Failure)
            {
                return NodeStatus.Failure;
            }

            if (status == NodeStatus.Running)
            {
                anyRunning = true;
            }
        }

        return anyRunning ? NodeStatus.Running : NodeStatus.Success;
    }
}

你还可以实现一个“加权随机序列”。

装饰器节点

它有一个子节点,用于修改或增强其子节点的行为。常见的装饰器包括反转结果、重复执行、条件判断等。

// 装饰器基类:用于修改子节点行为的包装节点
public abstract class Decorator : Node
{
    // 装饰器只能有一个子节点
    protected Node child;

    public Decorator(string name, Node child) : base(name)
    {
        this.child = child;
    }
}

// 反转装饰器:将子节点的结果取反
public class Inverter : Decorator
{
    public Inverter(string name, Node child) : base(name, child) { }

    public override NodeStatus Tick()
    {
        // 执行子节点
        NodeStatus status = child.Tick();

        // 反转结果:成功变失败,失败变成功
        if (status == NodeStatus.Success)
        {
            return NodeStatus.Failure;
        }
        else if (status == NodeStatus.Failure)
        {
            return NodeStatus.Success;
        }

        // Running状态保持不变
        return status;
    }
}

// 重复装饰器:持续执行子节点直到成功或达到次数上限
public class Repeater : Decorator
{
    private int repeatCount;      // 需要重复的次数
    private int currentCount = 0; // 当前已重复的次数

    public Repeater(string name, Node child, int repeatCount) : base(name, child)
    {
        this.repeatCount = repeatCount;
    }

    public override NodeStatus Tick()
    {
        // 持续执行子节点
        NodeStatus status = child.Tick();

        // 如果子节点失败,重置计数并返回失败
        if (status == NodeStatus.Failure)
        {
            currentCount = 0;
            return NodeStatus.Failure;
        }

        // 如果子节点成功,增加计数
        if (status == NodeStatus.Success)
        {
            currentCount++;

            // 达到重复次数,返回成功
            if (currentCount >= repeatCount)
            {
                currentCount = 0;
                return NodeStatus.Success;
            }
        }

        // 继续返回Running状态
        return NodeStatus.Running;
    }
}

黑板系统

黑板是行为树中的共享数据存储区,用于在不同节点之间传递和共享数据。一个通用的黑板如下:

public class Blackboard
{
    // 使用字典存储键值对
    private Dictionary<string, object> data = new Dictionary<string, object>();

    // 设置值
    public void SetValue(string key, object value)
    {
        data[key] = value;
    }

    // 获取值
    public T GetValue<T>(string key)
    {
        if (data.ContainsKey(key))
        {
            return (T)data[key];
        }
        
        // 如果键不存在,返回默认值
        return default(T);
    }
}

但显然,这会导致拆箱装箱,有性能问题。所以我一般会为每个游戏写一个黑板类,里面定义你需要的字段。

public class Blackboard
{
        /// <summary>
        /// 决策的目标单位(攻击目标)
        /// </summary>
        public GameObject TargetEnemy { get; set; }
        /// <summary>
        /// 决策的移动方向
        /// </summary>
        public Vector3 Direction { get; set; }
	
        ……
}
行为树(Behavior trees) 文章目录行为树组合节点次序节点选择节点并行节点修饰节点逆变节点成功节点重复节点叶子节点 行为树 行为树由多种不同类型的节点组成,这些节点都会返回三种状态中的一种作为节点的运行结果。三种状态分别是: 成功 - Success 失败 - Failure 运行中 - Running 前两个,正如它们的名字一样,是用来向它们的父节点通知运行的成功或失败。第三种是指还在运行中,结果还未确定,会在下一个 Tick 的时候再去检查这个节点的运行结果。这个功能非常重要,它可以让一个节点持续运行一段时间来维持某些行为。比 阅读详情

相关推荐

BehaviorTree之概念讲解(一)

BehaviorTree简介,即C语言实现一颗行为树

su_fei_ma_su的博客 2110

行为树(Behavior Tree)详细介绍

源自: https://www.gamasutra.com/blogs/ChrisSimpson/20140717/221339/Behavior_trees_for_AI_How_they_work.php 行为树是控制AI实体决策流程的分层节点树。 在树的范围内,叶子是控制AI实体的实际命令,而形成分支的则是各种类型的效用节点,它们控制AI沿着树走,以达到最适合这种情况的命令序列。 这些树可能非常深,节点调用执行特定功能的子树,允许开发人员创建行为库,这些行为库可以链接在一起以提供非常令人信服的AI行为

weixin_42313598的博客 3万+

Behaviac 腾讯开源行为树 简介(给策划)

1.为什么使用BTFSM模型的优势之一是简单。但是FSMs需要用转换(Transition)连接状态(State),因此,状态(State)失去了模块性(Modularity)。BT的主要优势之一就是其更好的封装性和模块性,让游戏逻辑更直观,开发者不会被那些复杂的连线绕晕。FSMBT行为树的基本概念:执行每个节点都会有一个结果(成功,失败或运行)子节点的执行结果由其父节点控制和管理返回运...

2904

行为树(Behavior Tree)介绍

在这篇文章中,我将介绍行为树及其所有术语,将其与有限状态机进行对比,分享一些例子和软件库,并一如既往地给你留下一些资源,如果你想了解更多。

ronnie88597的博客 6425

【ROS2】行为树 BehaviorTree(一):简介、入门

编写第一个行为树程序,完成充电指令:1)检查电量:使用普通函数,注意返回值类型为 BT::NodeStatus2)打开、关闭充电夹:使用成员函数,同样返回值类型为 BT::NodeStatuspublic:3)找到充电位:通过继承动作节点BT::SyncActionNode,重载 tick() 函数来实现public:{}

郭老二 1394

向量库深度解析:选型、索引原理与生产实践

本文系统解析五大主流向量库(pgvector、Milvus、Weaviate、ChromaDB、Faiss)的定位、优缺点与适用场景,深入拆解FLAT、IVF、HNSW等核心索引原理,并重点分析Milvus的Compaction机制。针对数据治理场景,提供精准选型建议:中小规模优先pgvector或Weaviate,超大规模选择Milvus,开发测试推荐ChromaDB。核心索引推荐HNSW,兼顾检索速度与召回率。全文从理论到实践,为开发者提供清晰的向量库选型逻辑与生产落地指南。

a123147abc的博客 360

行为树原理简介

行为树原理简介 本篇博客整理总结自行为树(Behavior Tree)详细介绍。 前言 首先需要说明的是行为树和状态机都是实现游戏AI的一种手段,二者各有利弊。相对于行为树,经典状态机实现起来更简单,且执行效率更高,但是随着状态数量的增加,状态机的结构将会向着不可控的方向发展。而经典行为树则是使用起来结构更加清晰,对于复杂AI的开发效率更高,但是实现起来复杂,且执行效率较低。所以说二者各有千秋。不过现在行为树已经逐渐成为游戏AI开发的主流,具体原因可以看由赖勇浩翻译的《有限状态机时代终结的10大理由》这篇博

BraveRunTo的博客 2986

推荐系统在音乐领域的实践:网易云音乐推荐算法解析

你是否有过这样的经历?打开网易云音乐,“私人FM”里总能推到你想听的歌;“每日推荐”的歌单,往往藏着你没听过但一入耳就爱上的曲目。这些“懂你”的推荐背后,是一套复杂却精密的推荐系统在运转。问题陈述:音乐推荐的难点远超一般商品——音乐是高度主观的艺术,用户兴趣易随心情、场景变化;小众音乐的“长尾效应”显著,如何让冷门好歌被发现?传统协同过滤依赖历史行为,面对新用户/新歌曲的“冷启动”问题束手无策;而单纯的内容推荐又难以捕捉用户的隐性兴趣。核心方案:网易云音乐采用“混合推荐策略”——融合协同过滤(CF)

AI天才研究院 2085

行为树(BT)笔记(二):行为树简介

行为树简介 与有限状态机不同,行为树是一个分层节点树,它控制决策流和“任务”的执行,或者我们将进一步称之为“动作”。 树的叶子是实际命令,即我们的协调组件与系统其余部分交互的位置。 例如,在面向服务的体系结构中,叶子将包含与执行操作的“服务器”通信的“客户端”代码。 在下面的示例中,我们可以看到在序列中执行的两个Actions,DetectObject以及GraspObject。 ...

xiewf的博客 4164

C语言 100道经典编程题适用于专升本,专接本【详细分析版】_c语言专升本题目

/法2//left指向起始 right指向数组末尾= 0){left++;//如果*left是奇数就循环接着向后指,直到指向偶数就退出//(left

2401_84978503的博客 695

沁恒CH32V30X学习笔记02--GPIO的使用教程及2次封装驱动

刚复位后,GPIO 口运行在初始状态,这时大多数 IO 口都是运行在状态所有的 GPIO 口都可以被配置外部中断输入通道,但一个外部中断输入通道最多只能映射到一个GPIO 引脚上,且外部中断通道的序号必须和 GPIO 端口的位号一致,,两方都是一对一的关系。复用功能。

Car12 705

行为树(Behavior Tree)简介

行为树(Behavior Tree) 每个节点执行后返回 成功/失败

双杯献酒的专栏 2831

【Pix4d精品教程】pix4d mapper相机未校准后手动添加连接点完美案例图文教程

如图所示,红色的照片为未校准的图片,软件无法自动获取连接点,有关相机未校准的原因及解决办法,可以参考《pix4d未校准相机:287 out of 402 images calibrated (71%), all images enabled, 6 block,问题分析及解决方案汇总》,本文以案例的形式,详细讲解pix4d中手动添加连接点的方法及操作步骤。 文章目录1. 手动添加连接点2. 重新优化3. 生成质量报告,检查精度 1. 手动添加连接点 在【空三射线】模式下查看校准失败的图片,记下照片的名称。.

智慧空间实验室 2万+

游戏AI –行为树简介

游戏AI是一个非常广泛的主题,尽管有很多资料,但我找不到能以较慢,更容易理解的速度缓慢介绍这些概念的东西。 本文将尝试解释如何基于行为树的概念来设计一个非常简单但可扩展的AI系统。 什么是AI? 人工智能是参与游戏的实体表现出的类似于人类的行为。 这是实体对智能和周到动作的幻想,而不是实际的智能推理驱动的行为。 目的是试图欺骗玩家,使他们认为其他“智能”实体是由人类而不是机器控制的。 说...

最佳 Java 编程 1132

Linux系统之部署经典魔塔小游戏

Linux系统之部署经典魔塔小游戏

jks212454的博客 1812

科研生产力革命:手把手教你打造基于大模型的Research Agent(文献调研+论文写作+思路激发)

本文介绍了如何利用Research Agent(科研智能体)提升科研效率。Research Agent能自主完成文献检索、信息整合和报告生成全流程,省去50%以上的基础工作时间。文章详细讲解了搭建运行环境的方法,包括工具准备和MetaGPT配置,并提供了两种实战方案:基础版Search-O1和专业版MetaGPT Research Agent。此外,还介绍了如何为垂直领域定制Agent,以及优化检索效果、改善报告逻辑等技巧。最后列举了学术研究、行业分析、内容创作和个人学习四大应用场景,帮助用户将AI从聊天工

LXY_LOVE36的博客 266

unity行为树简介

目前在Unity3D游戏中一般复杂的AI都可以看到行为树的身影,简单的AI使用状态机来实现就可以了,所以这里我也是简单的学习下,为以后用到做准备。 行为树的概念出现已经很多年了,总的来说,就是使用各种经典的控制节点+行为节点进行组合,从而实现复杂的AI。 Behavior Designer插件里,主要有四种概念节点,都称之为Task。包括: ...

weixin_30367543的博客 298

生成模型实战 | 基于Transformer生成音乐

本文介绍了基于Transformer架构的音乐生成方法,将音乐视为一系列事件序列进行处理。与将音乐表示为多维图像的MuseGAN不同,该方法使用类似GPT的仅解码器模型,基于先前事件预测下一个音乐事件。模型使用MIDI格式的钢琴音乐数据,通过分词处理将音符转换为note-on、note-off、time-shift和velocity四种事件类型,共388个独特词元,映射为索引序列后输入网络。训练完成后,可通过调整温度参数控制生成音乐的新颖性,从而创作出风格类似训练数据且连贯逼真的音乐作品。

盼小辉丶的博客 5808

游戏ai 行为树_游戏AI –行为树简介

游戏ai 行为树 游戏AI是一个非常广泛的主题,尽管有很多资料,但我找不到能以较慢且更易理解的速度缓慢介绍这些概念的东西。 本文将尝试解释如何基于行为树的概念来设计一个非常简单但可扩展的AI系统。 什么是AI? 人工智能是参与游戏的实体表现出的类似于人类的行为。 与实际的智能推理驱动的行为相比,这更是实体对智能和周到的行动的幻想。 目的是试图欺骗玩家,使他们认为其他“智能”实体是由人类而不...

最佳 Java 编程 590

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

游戏AI - 行为树Part1:简介

游戏AI - 行为树Part1:简介 背景 游戏中的AI,大多数都是按照规则设定好的,没有太多花哨的技术。原因有几个: 一是出于风险成本的考虑,用一个新技术,需要程序员开发相应的算法和框架,游戏设计师重新上手设计方法和摸索算法的适用范围,小团队承受不起这样的开销,大团队又怕做坏名声; 二是“高级”的AI算法并不直观,难以设计。举一个例子,怎么利用神经网络来训练一个士兵AI?我见过的一个例子是设计师用几个按键来操作士兵移动攻击,然后程序自动记录敌我双方的数据和设计师的操作,最后利用这些信息作为input构建一

weixin_47312141的博客 225

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

【Unity】【转载】 游戏中的行为树的一系列教程

游戏AI - 行为树Part1:简介 游戏AI - 行为树Part2:框架 AI 行为树的工作原理

Currybeefer的博客 425

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

人工智能_游戏AI –行为树简介

人工智能 游戏AI是一个非常广泛的主题,尽管有很多材料,但我找不到能以较慢,更易理解的速度缓慢引入这些概念的东西。 本文将尝试解释如何基于行为树的概念来设计一个非常简单但可扩展的AI系统。 什么是AI? 人工智能是参与游戏的实体表现出的类似于人类的行为。 与其说是实体执行的智能和周到的行动,还不如说是一种实际的智能推理驱动的行为。 目的是试图欺骗玩家,使他们认为其他“智能”实体是由人类而不是机...

cunfen0516的博客 827
上一篇: Unity有限状态机 一个支持消息调度系统的泛型状态机
下一篇: Unity 优化只读属性{get;}读取性能
ZZHSourceCode
博客等级 码龄9年 8粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值