第十六章: AI 系统

1. 本章核心目标

本章的目标分为对玩家直接呈现的“显性目标”和项目开发层面的“隐性/战略目标”。

  • 显性目标 (Player-Facing Goals):

    为游戏世界引入动态的生物,丰富玩家的生存体验。玩家可以与这些生物互动,无论是狩猎它们以获取资源,还是躲避它们的攻击以求生存。

  • 隐性/战略目标 (Development & Strategic Goals):

    建立一个可扩展、模块化的AI基础框架。此框架不仅要实现当前所需的基础AI行为,还要为未来引入更多、更复杂的AI类型提供支持。同时,将AI系统与游戏的核心玩法循环(探索、战斗、采集)紧密结合。


2. 系统与功能实现

本章成功实现了两大类AI的核心功能,并通过一个共享的基类进行构建,确保了代码的复用性和可维护性。

2.1 主要系统/功能列表:

  • AI基类 (BP_AI_Master):

    • 作为所有AI角色的父类,统一管理通用属性和逻辑,如 “Damageable” 标签,为子类实现具体行为提供了基础。
  • 被动型AI系统 (BP_AI_Passive - 以鹿为例):

    • 简单漫游 (SimpleRoam): AI在没有外部干扰时,会在导航网格内随机选择一个可达点进行移动,移动间隙有随机时长的停顿。

    • 玩家感知与逃跑 (Flee): 利用 PawnSensing 组件,当在视野内“看到”玩家时,触发逃跑逻辑。AI会提升移动速度,并朝随机方向奔跑。

    • 逃跑冷却机制: 逃跑状态会通过计时器在30-90秒后重置,使AI恢复到简单的漫游状态,避免无限逃跑。

  • 攻击型AI系统 (BP_AI_Aggressive - 以狼为例):

    • 玩家感知与追击 (ChaseTarget): 同样使用 PawnSensing 组件感知玩家,但触发的是追击逻辑。AI会锁定玩家为目标并提升速度进行追击。

    • 攻击逻辑 (AttackOnServer): 当AI移动到玩家附近时,通过 Sphere Overlap Actors 进行范围检测,若玩家在攻击范围内,则对其施加随机伤害。

    • 仇恨丢失机制 (LoseAggro): 追击状态设有10秒的计时器。如果AI持续追击但无法攻击到目标(或因其他条件),仇恨会丢失,AI返回漫游状态。

  • AI死亡与资源采集系统:

    • 通用死亡逻辑: AI接受伤害,生命值降至0后触发死亡事件。死亡时启用Ragdoll物理效果,并使其碰撞体不再阻挡玩家。

    • 尸体采集: 死亡后的AI尸体拥有独立的“尸体生命值” (CorpseHP)。玩家使用带有 “HarvestTool” 标签的工具攻击尸体,会调用工具的接口函数 BPI_HarvestCorpse,根据工具伤害计算并给予玩家相应的资源(如生肉)。

    • 尸体消失: 尸体资源被采集完后,会触发一个延迟销毁机制,使尸体在5秒后从世界中移除。

2.2 与前期系统交互:

本章实现系统 依赖的前期系统 交互方式 可能的冲突与解决方案
AI伤害与死亡 玩家伤害系统、物品系统 AI需要有 Damageable 标签才能被玩家武器伤害。伤害数值由玩家武器决定。 冲突: 无。系统按预期工作,实现了良好的解耦。
AI尸体采集 物品系统、资源数据结构 1. 采集工具需有 HarvestTool 标签。
2. AI通过接口调用工具的采集函数。
3. AI掉落的资源定义在 S_GivenResource 结构体中。
冲突: 逻辑耦合。AI需要知道工具的接口。
解决方案: 使用蓝图接口(Blueprint Interface)进行通信,降低了硬编码依赖,是目前较优的方案。
AI导航 关卡设计 AI的移动范围完全依赖于 Nav Mesh Bounds Volume 的覆盖区域。 冲突:NavMesh 未正确生成或覆盖不全,AI将无法移动。
解决方案: 确保关卡中所有AI活动区域都被 NavMesh 覆盖,并通过按 “P” 键进行可视化调试。

3. 关键设计思想

本章的设计体现了面向对象编程和游戏开发中的一些核心原则与模式。

设计思想/原则 具体应用实例 带来的优势
继承 (Inheritance) BP_AI_PassiveBP_AI_Aggressive 均继承自 BP_AI_Master 代码复用: 共享基础组件和变量。
结构清晰: 建立了清晰的 “Is-A” 关系。
单一职责原则 (SRP) - SimpleRoam 函数只负责漫游逻辑。
- FleeOnServer 函数只负责逃跑逻辑。
- AttackOnServer 函数只负责攻击逻辑。
高内聚低耦合: 每个函数功能明确,易于理解、测试和修改。
接口隔离原则 (ISP) - 创建 BPI_HarvestCorpse 接口专门用于尸体采集。
- 创建 IsTargetDead 接口专门用于查询目标死亡状态。
精确通信: AI与工具/玩家之间只通过必要的接口通信,避免了不必要的函数暴露和依赖。
服务器权威 (Server-Authoritative) 核心逻辑(如状态变更、移动决策、伤害计算)都在标记为 Run on Server 的事件中执行。 防止作弊: 客户端只负责表现,核心游戏状态由服务器控制,保证了多人游戏环境的公平性。
状态机 (State Machine) - AI通过布尔变量(如 isDead, isFleeing, isChasing)隐式地管理其状态(漫游、追击、逃跑、死亡)。
- 动画蓝图使用State Machine来管理 Idle/Walk/Run 等动画状态。
逻辑清晰: 使复杂的AI行为可以被分解为一系列独立且易于管理的状态,以及明确的转换条件。
数据驱动 (Data-Driven) AI死亡后掉落的资源类型和数量由其自身的 GivenResources 变量(一个数据结构数组)定义,而非在代码中硬编码。 高可配置性: 设计师无需修改代码,只需调整数据资产即可改变AI的掉落物,极大提高了迭代效率。

4. 核心技术点与难点

本章的实现综合运用了Unreal Engine的多个核心功能,并解决了AI开发中的一些常见难点。

  • 核心技术点:

    • AI导航系统: 使用 Nav Mesh Bounds Volume 为AI生成可行走区域,并利用 AI Move To 节点驱动AI向目标点移动。

    • AI感知系统 (PawnSensingComponent): 高效地实现了基于视野的AI感知功能,作为触发AI行为(逃跑或追击)的入口。

    • 网络复制 (Replication):

      • 通过 RepNotify 变量 isDeadMulticast RPC,确保了AI死亡时的Ragdoll效果能被所有客户端正确观察到。

      • 将所有改变游戏状态的逻辑放在服务器RPC中执行,是构建稳定多人游戏的关键。

    • 蓝图接口 (Blueprint Interface): 实现了AI与玩家工具之间的解耦通信,是不同系统间交互的推荐做法。

    • 动画蓝图与混合空间 (Animation Blueprint & Blend Space): 将AI的移动速度与动画状态(站立、行走、奔跑)动态绑定,使AI动作表现自然流畅。

  • 技术难点与解决方案:

难点描述 解决方案
AI行为状态管理 使用多个布尔变量 (isDead, isMoving, isFleeing/isChasing) 结合自定义事件,构成一个隐式的状态机来控制AI在不同行为模式(漫游、追击/逃跑、攻击、死亡)间的切换。
流畅的AI转向 初始AI转向过于生硬(“snappy”)。通过修改角色移动组件的设置:取消 Use Controller Yaw,启用 Orient Rotation to Movement 并设置合适的 Rotation Rate,实现了平滑自然的转向效果。
多人环境下的状态同步 AI的核心状态(如生命值、是否死亡、当前行为)必须在所有客户端间保持一致。解决方案是采用服务器权威模型,所有状态变更都在服务器上发起,并通过 RepNotifyMulticast 事件同步到客户端。
可复用逻辑的实现 被动型和攻击型AI有共享的逻辑(如漫游、死亡、采集)。解决方案是将大部分死亡和采集逻辑直接从被动AI复制到攻击AI中,实现了快速开发。

5. 自我批判与重构

本章的实现功能完善且结构清晰,但也存在一些可以优化和重构的地方。

  • 遇到的“坑”或关键问题:

    1. 忘记添加标签: 忘记给AI添加 Damageable 标签,导致玩家无法对其造成伤害,这是初期调试中容易忽略的问题。

    2. 逻辑连接错误: 在死亡事件中,错误地将逻辑线连接到了 Branch 的错误引脚,导致AI生命值为0后没有正确触发死亡(Ragdoll)表现。

    3. 导航缺失: 如果场景中没有放置或正确配置 Nav Mesh Bounds Volume,AI将无法移动,这在初次设置时是一个关键步骤。

  • 对前期设计的反思与修正:

    本章的设计是建立在前期伤害和物品系统之上的,交互设计(通过标签和接口)是成功的。这证明了前期将核心功能(如伤害判定、物品使用)与具体实现(玩家、AI)解耦的正确性。

  • 如果重来一次,如何优化?

优化点 当前实现方式 优化方案 优势
逻辑复用 在被动型和攻击型AI之间复制粘贴了大量的死亡和采集逻辑。 将死亡、伤害处理、资源采集等通用逻辑下沉到 BP_AI_Master 基类中,或将其封装到可复用的组件 (Actor Component) 中。 减少代码冗余: 避免重复代码,降低维护成本。
更高内聚: 将相关功能(如“可被采集”)封装在一起,符合组件化设计思想。
AI行为切换 使用多个独立的布尔变量 (isFleeing, isChasing) 来管理状态。 引入**枚举(Enum)**来定义AI的状态(E_AIState: Roaming, Chasing, Fleeing, Dead),并使用一个变量来存储当前状态。 状态管理更清晰: 用一个枚举变量代替多个布尔值,可以更直观地表示AI的当前状态,并能有效防止出现(例如 isFleeingisChasing 同时为 true 的)非法状态组合。
攻击逻辑 使用 AI Move To,在其 On Success 引脚上触发攻击逻辑。 可以在 ChaseTarget 逻辑中,每帧或每隔一小段时间就检查与目标的距离。当距离小于攻击范围时,停止移动并执行攻击。 更灵敏的攻击触发: 无需等待 AI Move To 完成,AI可以在追击过程中更快速地响应并进入攻击状态,行为表现更真实。