☰
UE5状态树嵌套实现FPS敌人AI状态管理实战
2026/10/6 20:10:26 网站建设 项目流程

FPS 游戏开发里,敌人 AI 的“状态管理”一直是个说不完的话题。之前在项目里做 AI 行为切换时,最头疼的就是状态一多,蓝图的连线就乱成一团。交战中要兼顾巡逻、警戒、追击、攻击、搜敌,如果再叠加换弹、受伤、呼叫队友这类局部行为,传统状态机很快就变得没法维护。后来把 UE5 的 State Tree(状态树)引入纯蓝图流程,再用“嵌套状态树”把宏观状态和微观行为拆开管理,整个 AI 状态转换一下子清晰了许多。这篇文章就把这套通过多状态树嵌套实现状态转换的方法整理出来,从概念到完整案例都会讲到,不管是刚开始学 UE5 蓝图的新手,还是已经有 FPS 项目经验的开发者,都可以按这篇流程走一遍。

1. 多状态树在 FPS 游戏开发中的定位

1.1 传统状态机为什么撑不住复杂 FPS 玩法

很多 FPS 项目最早都是用蓝图里的枚举 + Switch 来管理 AI 状态:一个EAIState枚举,里面填上Patrol、Suspect、Combat、Search,然后在 AI 控制器的 Tick 里不停检查各种条件,再调用SetAIState切换状态。

这种方案在状态只有三四个时非常好用,逻辑直白,排错也方便。但敌人 AI 一旦进入战斗,就会出现下面这些情况:

  • 状态枚举越来越多,Patrol之外还要有Investigate、Evade、Retreat、Cover。
  • 每个状态迁移条件都要在 Tick 里重复判断,性能浪费明显。
  • 状态之间互相干扰,例如追击中敌人丢失目标,要切回搜索,但搜索又需要回到上次丢失点的坐标,这一串数据流转写起来很啰嗦。
  • 想加入“受伤后先蹲下 2 秒再还击”这种过渡行为,就会发现原本的状态机没有地方放“过渡状态”,只能强行加枚举,状态数量继续膨胀。

问题根源并不是枚举状态机本身,而是“平面式状态机”把所有行为放在同一个层级里。宏观的阶段切换和微观的帧级行为全部挤在一起,代码阅读和维护成本必然上升。

1.2 State Tree 与行为树、状态机的区别

UE5 的 State Tree(状态树)是引擎自带的状态管理框架,从 UE 5.1 左右开始逐步成熟。它和传统行为树、状态机最大的区别在于:

  • 传统状态机以“状态间的连线”驱动,什么时候进入什么状态写死在逻辑里。
  • 状态树以“当前状态 + 节点内任务 + 转换条件”驱动,状态节点可以独立配置进入行为、执行行为、退出行为。
  • 状态树支持参数、黑板、事件和子树上嵌套,宏观状态用父树,具体行为用子树。

一句话概括:状态树像“分层的状态机”,父层决定现在处在哪个大阶段,子层决定这个阶段内部具体执行哪些小行为。

这里说一个容易混淆的对比。行为树和状态树表面上很相似,都有节点、都有装饰器,但运行模型完全不同:

对比项行为树 Behavior Tree状态树 State Tree
核心驱动从根节点不断执行 Tick,按优先级挑选子节点当前状态的选中节点持续运行,直到发生转换
记忆能力本质上没有状态记忆,BT 执行流每次 Tick 重新选择有明确的状态持有概念,叶子任务持续运行
事件支持通常配合黑板通知原生支持状态树事件,适合状态切换信号
嵌套方式可以嵌套行为树子树可以直接嵌套子状态树资产

如果需求只是“搜索敌人并追击”,行为树完全够用。但如果需求是“多个阶段循环切换,每个阶段内部又有更细的状态”,状态树的优势会更明显。

1.3 为什么用嵌套状态树而不是单棵树

单棵状态树也可以管理全部状态,无非把巡逻、追击、攻击全部平铺成兄弟节点。但 FPS 实战里有两个痛点很难解决:

第一,平铺状态会导致转换条件交叉。例如“追击”状态既受“是否看到玩家”控制,又受“自身血量”控制,还受“子弹是否打空”控制。这些条件全部挂在同一层树上,状态数量稍微增长,编辑器里就会变成一张大网。

第二,局部行为的复用性差。如果多个大状态都需要“移动”,把移动逻辑复制到每个状态里很蠢。嵌套状态树允许把“巡逻”抽成一个独立子树资产,父树里的警戒、战斗、搜索都可以引用它;也可以把子树继续向下拆分。

所以,嵌套状态树的核心价值是用“树的层级”代替“状态的平面铺开”。父树关心“我现在处于什么宏观阶段”,子树关心“这个阶段里要执行什么操作”,状态之间的转换不再是一堆在线之间连接的 Switch 逻辑,而是通过事件、条件和任务完成的有级联关系的转换。

1.4 本文适合什么读者、能学到什么

本文的内容以 UE5 纯蓝图为主,不需要写 C++。读者的能力线大概是这样:

  • 会创建 UE5 关卡、会添加 Actor、会使用最常见蓝图节点。
  • 看过蓝图枚举和分支,知道什么是 AI Controller、Pawn。
  • 想给自己的 FPS 敌人 AI 做一个结构更清晰的状态管理系统。

学完这篇,你将掌握:

  • State Tree 基础元素:State、Task、Condition、Transition、Evaluator。
  • 如何在纯蓝图流程中创建和配置状态树资产。
  • 如何把父状态树和子状态树嵌套起来,实现大状态与小行为的切换。
  • 如何在 AI 控制器蓝图中启动状态树。
  • 如何使用事件触发状态转换,以及常见排错思路。

2. 环境准备与版本说明

2.1 引擎版本与插件开关

State Tree 在 UE5 中的集成程度因引擎小版本而异。本文的演示思路以 UE 5.x 系列通用功能为基准,具体菜单名称、蓝图节点名可能存在细微差异,建议按你自己安装的引擎版本调整。

简单来说,环境准备分以下几步:

  1. 安装 Unreal Engine 5.x。
  2. 创建项目时选择“Blank”或“第一人称游戏”模板都可以,本文用 Blank 加手动 AI Pawn 的方式演示。
  3. 如果你的项目里找不到 State Tree 相关功能,先打开“编辑 -> 插件”,搜索 StateTree,确认插件处于启用状态。
  4. 在 Build.cs 或模块设置里,普通蓝图项目不需要额外配置依赖,只要引擎模块可用即可。

注意,State Tree 在不同版本中可能被标记为实验性或正式功能。如果插件列表里显示为“Experimental”,仍然可以用于学习项目,但正式上线前需要仔细评估稳定性。

2.2 项目创建与基础资产

这里为了完整跑通流程,建议先创建一个空地图,准备好以下基础资产:

资产类型建议内容用途
AI 角色一个 Character 蓝图作为敌人 Pawn
AI 控制器一个 AIController 蓝图启动和管理状态树
玩家角色第一人称角色或简单胶囊体作为敌人感知的目标
导航网格在场景里放置 NavMeshBoundsVolume保证敌人可以寻路
数据资产StateTree 资产承载父树与子树

如果不想从第一人称模板开始,可以手动创建 Character 子类并在场景中摆放,AI 控制器的 Auto Possess AI 方式选择“Placed in World 或 Spawned”,确保敌人生成后立刻被控制器接管。

2.3 本文演示的 FPS 敌人 AI 需求

在动手前,先把目标行为讲清楚。我们要实现的 FPS 敌人 AI 分为四个大阶段:

  1. 巡逻(Patrol):沿路径点巡逻,无战斗目标。
  2. 警戒(Alert):感知到可疑声音或短暂看到玩家,但还没完全确认。
  3. 战斗(Combat):确认玩家目标,执行追击、开火、换弹等行为。
  4. 搜索(Search):战斗中丢失玩家目标,前往最后已知位置搜索,找不到则回到巡逻。

这四个大阶段属于“父状态”。而“巡逻”内部包含“走向路径点”“到达后等待”;“战斗”内部包含“追击”“射击”“换弹”等小状态。这些小状态适合放到嵌套子树里。

嵌套的目标就是:父状态树只负责宏观阶段切换,子树负责具体小行为,事件负责把子树里的结果传回父树。

3. State Tree 核心概念与蓝图化思路

3.1 状态树中的基础元素

在进入编辑器操作之前,先理解几个关键概念。这些词在 Status Tree Editor 里会反复出现:

State(状态)

状态树中的基本节点,一个 State 代表一个持续的逻辑块。State 可以并行运行,也可以只运行激活的子节点。FPS 敌人 AI 中,“Combat”是一个父 State,它的子树里有“Aim”这样的子 State。

Task(任务)

State 内部具体执行的逻辑。例如“移动到目标点”是一个 MoveTo Task,“播放动画”是一个 Play Anim Task。任务可以继承蓝图基类后用蓝图自定义。在纯蓝图项目中,最容易的是使用已有的蓝图任务节点配合流程任务。

Condition(条件)

判断是否满足进入某个 State 或执行某个 Transition 的布尔条件,相当于状态机中的迁移守卫。例如“IsPlayerInLineOfSight”就是条件。

Transition(转换)

从一个 State 切换到另一个 State 的规则。状态树里的 Transition 通常配置在当前 State 节点上,指定“什么条件下转换到哪个 State”。

Evaluator(评估器)

挂在状态树上周期性求值的逻辑,用于更新参数,例如“计算当前到玩家的距离”并写入黑板或状态树参数。

如果之前用过 UE 的 Blackboard,可以把参数理解为 StateTree 的黑板,多个 State 和 Condition 通过参数共享信息。

3.2 嵌套状态树如何解决状态转换难题

嵌套状态树本质上是在父 State 节点中挂载另一个 StateTree 资产。被挂载的树就是“子状态树”。

父树中的某个 State 激活时,子树开始运行;父树中的 State 退出时,子树也会被终止。这样一个宏观状态可以拥有一个内部状态机,比如:

  • 父状态Combat激活时,子树负责在Chase、Shoot、Reload三个小状态间切换。
  • 当玩家位置丢失、或者敌人血量归零时,父状态通过事件收到“战斗结束”信号,切换到Search或Death。

这里的转换链路是双向的:父树控制子树生命期,子树上抛事件让父树发起状态切换。如果只用一张平铺的大状态树,事件和条件会堆在一起;拆成父子嵌套后,子树的内部细节完全封装起来,父组件和子组件可以独立调试。

3.3 蓝图能与状态树交互的几种方式

纯蓝图流程中,与状态树交互的入口主要有这几个:

方式一:AI 控制器持有 StateTree 组件

在 AI 控制器的蓝图里添加一个 StateTree 组件,启动状态树时指定资产,运行时通过组件读取当前状态。

实操中一般是在 AI 控制器 Event BeginPlay 或 On Possess 里调用“Start State Tree”节点。如果你的 AI 控制器里编译不出这个节点,先确认对象类型和版本,节点名以当前引擎为准。

方式二:状态树外部参数传入

通过 StateTreeReference 或相关参数节点,把外部变量传给状态树。例如把敌人 Pawn、玩家 Pawn 写入状态树参数,这样状态树内部任务才能拿到对象引用。

方式三:事件驱动转换

在状态树的 Transition 条件中,可以监听事件。例如“Alert”状态下的子树发出Target_Spotted事件,父树的 Combat 状态监听到该事件后触发转换。

下面先理解整体结构,第 4 节会完整演示。

4. 实战:用嵌套状态树实现 FPS 敌人 AI 状态切换

4.1 设计父级状态与子级状态

先把要拆分的树画在纸上。不要直接打开编辑器,先规划层级:

父状态树 Enemy_MainTree ├── Idle / Delay(初始等待) ├── Patrol(巡逻) │ └── 子树 Patrol_SubTree │ ├── MoveToNextPoint │ ├── WaitAtPoint │ └── CheckAround ├── Alert(警戒) │ └── 子树 Alert_SubTree │ ├── TurnToNoiseLocation │ └── WaitForConfirm ├── Combat(战斗) │ └── 子树 Combat_SubTree │ ├── Chase │ ├── Shoot │ └── Reload └── Search(搜索) └── 子树 Search_SubTree ├── MoveToLastKnownPosition └── ReturnToPatrol

父树里的每个大阶段对应一个带子树的 State。子树在自己的资产里管理内部小状态。

这样设计的价值在于:如果想要增加“呼叫同伴”这个行为,只需要在 Combat 父 State 的子树里加一个子状态,完全不影响父树其他部分;如果想要把 Patrol 改成更复杂的战术巡逻,替换 Patrol_SubTree 资产本身就可以。

4.2 创建敌人 AI 控制器蓝图

创建 AI 控制器蓝图时,在内容浏览器里右键 -> 蓝图类 -> 父类选择 AIController,命名为BP_EnemyAIController。

打开蓝图后,添加一个 StateTree 组件,名称建议叫StateTreeComp。在 Event On Possess 里做初始化:

Event On Possess ├── Get Controlled Pawn ├── Cast To BP_EnemyCharacter └── 获取玩家 / 场景引用 └── 调用 StateTreeComp 的 Start StateTree

这里不需要写一行 C++,全部是节点连线。关键是在启动前,把下面这些参数写入 StateTree:

参数类型说明
SelfActorObject Reference敌人 Pawn 自身
PlayerActorObject Reference玩家 Pawn
AI ControllerObject Reference当前 AIController
MoveSpeedFloat移动速度,用于移动任务

如果你使用的 StateTree 蓝图交互节点名在当前版本中不叫Start StateTree,可以在 AI 控制器的组件面板找到 StateTreeComp,右键生成事件,查找相关函数。

为了让状态树能拿到玩家位置,可以在 AI 控制器 Tick 里每隔一定时间更新参数,或者通过 Evaluator 周期性求值。对性能和清晰度来说,推荐在 Evaluator 节点里统一更新。

4.3 搭建父状态树:警戒 / 战斗 / 搜索

在内容浏览器右键 -> Artificial Intelligence -> State Tree,创建父状态树资产ST_EnemyMainTree。

打开的 State Tree 编辑器中,左侧是状态节点层级,右侧是参数、条件、任务面板。开始前先添加几个参数:

参数名类型说明
EnemySelfObject敌人 Pawn
PlayerRefObject玩家 Pawn
bIsPlayerSpottedBoolean是否发现玩家
bIsLostTargetBoolean是否丢失玩家
LastKnownLocationVector最后看见玩家位置

然后开始创建父状态节点,顺序如下:

  1. 根节点下添加Initial状态,作为初始等待。
  2. 添加Patrol状态,激活时挂载巡逻子树。
  3. 添加Alert状态,挂载警戒子树。
  4. 添加Combat状态,挂载战斗子树。
  5. 添加Search状态,挂载搜索子树。

父状态树的转换规则建议这样做:

  • Initial->Patrol:延迟 2 秒后自动转换。
  • Patrol->Alert:条件bIsPlayerSpotted == true,转换为警戒。
  • Alert->Combat:收到事件Target_Spotted或条件确认目标。
  • Combat->Search:条件bIsLostTarget == true。
  • Search->Patrol:搜索完成事件Search_Finished。

注意,状态树的 Transition 不能只挂在根或子状态上,需要逐个状态节点配置它们的转换条件。

4.4 搭建嵌套子树:巡逻 / 追击 / 攻击

此时父树只定义了框架,里面还没有细节。接下来创建三个子树资产:

第一个子树:巡逻子树ST_PatrolSub

流程图如下:

State: MoveToNextPoint └── Task: MoveTo / FindNextPatrolPoint State: WaitAtPoint └── Task: Wait 2s State: CheckAround └── Task: 旋转视角 / 更新 bIsPlayerSpotted

这里的核心任务是把“寻找下一个巡逻点”和“移动”拆开。实际项目中可以通过GetRandomReachablePointInRadius或自定义巡逻点数组实现,纯蓝图节点已经能完成。

巡逻子树内部转换:

  • MoveToNextPoint里移动任务完成后,转换到WaitAtPoint。
  • WaitAtPoint等待结束,转换到CheckAround。
  • CheckAround更新完感知,如果玩家未发现,回到MoveToNextPoint;如果发现玩家,则向上发送Target_Spotted事件给父树。

第二个子树:战斗子树ST_CombatSub

战斗子树内部状态:

State: Chase(追击) └── Task: MoveTo Player State: Shoot(射击) └── Task: 朝向玩家 / 发射射线或抛射物 State: Reload(换弹) └── Task: 播放换弹动画 / 等待

转换规则:

  • Chase->Shoot:当距离小于攻击距离,且视线检测成功。
  • Shoot->Reload:当子弹数为 0。
  • Shoot->Chase:玩家距离过远或视线被阻挡。
  • Reload->Shoot:换弹完成后子弹重新填充。

这些内部状态之间的转换跑在子树内部,不会干扰父树。父树只知道“目前处于 Combat 大阶段”。

第三个子树:搜索子树ST_SearchSub

State: MoveToLastKnownLocation └── Task: MoveTo LastKnownLocation State: WaitAndLookAround └── Task: 原地旋转搜索 State: ReturnToPatrol └── Task: 转换到巡逻

如果敌人移动到最后已知位置后,仍然没有发现玩家,则向上发送Search_Finished事件,父树收到事件后把大状态从 Search 切回 Patrol。

4.5 通过事件完成父子状态转换

真正实现“多状态树嵌套的状态转换”的关键就是事件。子树无法直接修改父树的当前状态,但可以通过事件告知父树。

实际操作中,在子树的任务蓝图里发送事件。例如在CheckAround状态中判断bIsPlayerSpotted == true后,从任务蓝图中调用“Send StateTree Event”节点,填入事件名Target_Spotted。

然后在父状态树的Alert状态节点上配置 Transition:

转换项设置
TriggerEvent
Event TagTarget_Spotted
Target StateCombat
Conditions无,或添加一个确认条件

父树收到事件后,发现触发条件匹配,就会执行Alert退出逻辑,然后进入Combat。当Combat被激活时,其挂载的战斗子树自动被激活,战斗内部的 Chase、Shoot、Reload 子状态就可以开始工作。

同样,战斗阶段时如果玩家立刻离开视线,父树判断bIsLostTarget == true,转换到Search,搜索子树开始运行。搜索完成后,子树发送Search_Finished,父树从 Search 切换回 Patrol。

这套机制的最大优势是:每个层级的树只关心自己的转换规则,不需要知道自己被谁调用。子树上抛一个事件即可,父树决定是否响应。

4.6 运行与验证

配置完成后,把BP_EnemyCharacter放到场景中,AI Controller 设置为BP_EnemyAIController。玩家进入关卡,通过移动验证下列行为:

操作预期表现
玩家远离敌人敌人沿巡逻点持续巡逻
玩家进入视野敌人进入警戒,转身观察
玩家暴露敌人进入战斗,追击并开火
玩家躲到掩体后敌人进入搜索,前往最后位置
搜索无果敌人回到巡逻

如果打开状态树调试工具,可以看到当前激活的父状态和子状态,以及事件是否被正确触发。

5. 常见问题与排查思路

5.1 状态树没有启动

现象:AI 控制器已挂载,但状态树完全不运行。

可能原因:

  • StateTree 资产没有正确指定。
  • Start StateTree 节点没有在 On Possess 中调用。
  • AI 控制器没有被正确分配到 Pawn。

排查顺序:

  1. 在 AI 控制器 BeginPlay 里加 PrintString,确认 Possess 事件触发。
  2. 确认 StateTreeComp 引用的资产不是 None。
  3. 确认参数传入逻辑没有在状态树启动前发生异常。

5.2 状态之间无法转换

现象:父状态一直停留在某个状态,迟迟切不到目标状态。

可能原因:

  • Transition 的触发类型设置错误,例如监听事件,但事件名不匹配。
  • Condition 条件不满足,例如bIsPlayerSpotted始终为 false。
  • 目标状态不可转换,某些状态间缺少双向转换配置。

排查顺序:

  1. 打开状态树调试面板,查看当前状态。
  2. 在条件判断位置加断点或 PrintString。
  3. 检查事件名的大小写和 Tag 是否完全一致。

5.3 嵌套子树触发后父状态卡死

现象:进入 Combat 子树后无法跳出,即使玩家消失也不转换。

可能原因:

  • 事件名发送方和接收方不一致。
  • 父状态树的 Transition 没有配置从 Combat 出发的规则。
  • 子树的生命周期管理出现问题,父状态退出时子树没有正常终止。

解决思路:

  • 检查父状态Combat的 Transition 规则,确认bIsLostTarget == true时能转换。
  • 检查子树内是否有正在运行的长任务,例如 Wait 或 Delay 卡住事件发送。

5.4 黑板键 / 参数不生效

现象:状态树内部读取到的参数始终是初始值。

可能原因:

  • 参数名拼写不一致。
  • 外部传入参数时对象引用为 None。
  • Evaluator 更新频率设置得太低。

排查顺序:

  1. 确认参数名和变量名完全一致,注意大小写。
  2. 在 AI 控制器传入参数前打印变量值。
  3. 检查 Evaluator 的 Interval 设置,是否需要实时更新。

5.5 性能问题

现象:敌人多的时候状态树 Tick 开销偏高。

解决思路:

  • 减少每帧更新的 Evaluator 数量,改为 0.2 秒到 0.5 秒更新一次。
  • 不要把视线检测放在每帧执行的 Task 里,尽量做成低频率任务。
  • 对不在玩家附近、或处在非战斗状态的敌人,可以考虑暂停状态树 Tick。

6. 最佳实践与工程建议

6.1 状态粒度设计

嵌套状态树的状态粒度,直接决定后期维护成本。建议遵循“一个状态解决一个问题”的原则。

例如战斗状态内部不要既管位移又管射击又管换弹,而是拆成Chase、Shoot、Reload三个状态。每个状态内部只执行一个核心 Task,转换条件才容易配置。反之,如果拆得过细,例如把“抬起武器”也单独拆一个状态,状态数量会失控。

6.2 嵌套层级控制

父树嵌套子树不是越深越好。推荐最多两层或三层:

  • 第一层:宏观阶段,例如战斗、巡逻、搜索。
  • 第二层:阶段内子行为,例如追击、开火、换弹。
  • 第三层:极少数复杂子行为,例如“破门”、“呼叫支援”这种多步骤流程。

层级超过三层后,事件追踪非常困难,调试面板看不过来,建议重新设计状态划分。

6.3 事件与参数命名规范

状态树项目里最隐蔽的错误就是名字不一致。建议从一开始就建立命名规范:

  • 事件名统一使用目标_动作格式,例如Target_Spotted、Target_Lost、Search_Finished。
  • 参数名统一使用类型前缀:b开头表示布尔,LastKnownLocation这种直接表达含义。
  • 状态节点名使用名词短语,避免出现动词短语,例如状态名Combat,任务名ShootAtTarget。

如果你的项目里事件较多,可以集中维护一张事件表格,放在项目文档中,避免多人协作时各写各的名字。

6.4 调试与回放技巧

UE5 状态树编辑器有内置的调试模式,运行游戏后可以在编辑器中看到当前激活的 State。建议开发时打开“调试器”面板,实时观察状态切换。

实际项目中,最快定位问题的方法是在关键转换点加可视化信息:

  • 在 AI Controller Tick 里打印当前父状态名。
  • 在子树任务开始和结束位置打印进入 XX 状态/发送 XX 事件。
  • 在场景中启用 AI 调试绘制,显示视线检测结果。

不需要做成永久功能,可以在开发宏里包一层编译开关,发布时屏蔽。

6.5 纯蓝图项目的工程约束

既然是纯蓝图项目,要注意不要把所有逻辑都塞进状态树的 Evaluator 和 Task 蓝图里。建议维护几个独立蓝图工具类:

蓝图职责
BP_Utility_LineOfSight封装视线检测、距离判断
BP_Utility_PatrolPoint管理巡逻点数组
BP_Utility_WeaponHandler管理子弹、换弹、开火
BP_Utility_EventBus跨状态广播事件

状态树里的 Condition 和 Task 只负责调用这些工具类的方法,不要在状态树内部堆几百行复杂节点。这样即使状态结构调整,底层工具不用重写。

另外,纯蓝图项目也要注意版本管理。StateTree 资产是数据资产,可以单独打包,但注意不要在版本合并时出现二进制冲突。多人在同一个 StateTree 资产上开发时,建议分工明确,一次只让一个人修改一个子树。

7. 总结与下一步

这篇内容围绕 UE5 纯蓝图 FPS 游戏开发中的状态管理问题,重点拆解了多状态树嵌套的状态转换方法。核心收益可以概括为三点:

第一,父树管宏观阶段、子树管微观行为,状态切换不再依赖平铺枚举和成堆的分支节点。

第二,子树通过事件向上反馈结果,父树通过条件与事件完成跨状态转换,逻辑边界清晰,排错时可逐层定位。

第三,纯蓝图可完成整套搭建,不需要 C++ 扩展。代价是状态树资产的结构设计和命名规范必须在项目早期就定下来。

下一步可以继续学习这几个方向:

  • 把状态树与 UE5 的 Gameplay Ability System(GAS)结合,把技能和 AI 行为解耦。
  • 使用状态树管理玩家角色的操作状态,例如瞄准、换弹、攀爬、交互。
  • 尝试用状态树管理多人网络环境下的 AI 同步,处理客户端与服务器状态归属问题。

如果你正在做 FPS 敌人 AI,建议先照着本文的父子树结构搭一个最小 Demo,跑通“巡逻 -> 警戒 -> 战斗 -> 搜索 -> 巡逻”的闭环,再逐步往里面填射击、换弹、掩体等细节。

如果本文对你有帮助,可以收藏备用。下一篇可以继续聊状态树与黑板的具体参数设计,或者纯蓝图下 FPS 武器系统的拆分方式。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询