Files
zeling_v2/Docs/ThirdPart/PathBerserker2d_Technical_Evaluation.md
2026-05-19 23:20:44 +08:00

957 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PathBerserker2d 技术评估与使用手册
> **版本**: PathBerserker2d (Unity Asset Store)
> **Unity 要求**: 2020.3+
> **依赖**: `com.unity.modules.physics2d`
> **许可**: Unity Asset Store EULA
> **包路径**: `Assets/PathBerserker2d/`
> **官方文档**: https://oribow.github.io/PathBerserker2dDemo/Documentation/
---
## 目录
1. [概述与设计哲学](#1-概述与设计哲学)
2. [架构总览](#2-架构总览)
3. [核心组件](#3-核心组件)
4. [NavAgent 详解](#4-navagent-详解)
5. [NavSurface 详解](#5-navsurface-详解)
6. [NavLink 与 NavLinkCluster](#6-navlink-与-navlinkcluster)
7. [辅助组件](#7-辅助组件)
8. [全局系统 — PBWorld 与 Settings](#8-全局系统--pbworld-与-settings)
9. [路径与寻路请求](#9-路径与寻路请求)
10. [预置行为脚本](#10-预置行为脚本)
11. [TransformBasedMovement 移动系统](#11-transformbasedmovement-移动系统)
12. [Demo 场景与使用模式](#12-demo-场景与使用模式)
13. [Corgi 引擎集成](#13-corgi-引擎集成)
14. [全局设置参考](#14-全局设置参考)
15. [与 BaseGames 架构集成方案](#15-与-basegames-架构集成方案)
16. [性能分析与优化建议](#16-性能分析与优化建议)
17. [优缺点总结](#17-优缺点总结)
18. [总结与建议](#18-总结与建议)
---
## 1. 概述与设计哲学
### 1.1 什么是 PathBerserker2d
PathBerserker2d 是一款专为 **2D 游戏** 设计的导航/寻路插件。与 Unity 内置的 NavMesh面向 3D不同它基于 **线段Segment** 而非面片构建导航图,天然适合 2D 横版游戏的地形表示。
核心理念:
> **从 2D Collider 自动烘焙导航线段,通过多线程 A\* 异步寻路,以事件驱动的方式委托移动实现。**
### 1.2 核心设计原则
| 原则 | 说明 |
|------|------|
| **线段导航** | 导航数据为沿 Collider2D 表面的线段,适合平台跳跃和横版场景 |
| **自动烘焙** | 从子对象的 Collider2D 自动提取导航线段,无需手动标记 |
| **异步多线程** | 寻路计算在后台线程完成默认4线程不阻塞主线程 |
| **事件驱动** | NavAgent 通过事件通知移动组件,移动实现完全解耦 |
| **动态世界** | 支持运行时烘焙、移动平台、动态链接启停 |
| **链接系统** | 跳跃、下落、攀爬、电梯、传送等复杂行为通过 NavLink 建模 |
### 1.3 适用场景
- 2D 横版平台跳跃Metroidvania / Platformer
- 2D 俯视角 RPG360° 模式)
- 需要 AI 自动寻路的 2D 游戏
- 含电梯、梯子、传送门、移动平台等复杂地形的关卡
---
## 2. 架构总览
### 2.1 系统架构图
```
NavSurface (烘焙 Collider2D → 线段数据)
↓ OnEnable 时添加
PBWorld.NavGraph (全局导航图B2DynamicTree 空间索引)
↑ NavLink / NavLinkCluster 添加连接
↑ NavAreaMarker 标记区域 NavTag
↑ NavSegmentSubstractor 裁剪线段
NavAgent.PathTo() → PBWorld.PathTo(PathRequest)
↓ 入队
PathfinderThread (多线程 A*) → PathRequest.Fulfill(Path)
↓ 主线程轮询
NavAgent.HandlePathRequest() → 开始 FollowPath
↓ 事件驱动
TransformBasedMovement / 自定义移动 (实际移动)
↓ 完成后回调
NavAgent.CompleteSegmentTraversal() / CompleteLinkTraversal()
```
### 2.2 关键设计模式
| 模式 | 说明 |
|------|------|
| **异步多线程寻路** | PathRequest 入队 → PathfinderThread 后台 A\* → 主线程轮询结果 |
| **事件驱动移动** | NavAgent 触发事件,外部组件响应事件实现移动逻辑 |
| **动态世界** | NavSurface 可运行时烘焙/加载/卸载,链接可动态添加/移除/切换可穿越性 |
| **移动平台支持** | 所有坐标相对 NavSurface 本地坐标存储Transform 变化时自动跟随 |
| **自动重新寻路** | `autoRepathIntervall` 控制周期性重算路径,应对世界变化 |
| **WebGL 兼容** | 检测平台后使用协程替代线程,确保单线程环境可用 |
### 2.3 目录结构
```
Assets/PathBerserker2d/
├── Scripts/PathBerserker2d/ # 核心源码
│ ├── NavAgent/ # NavAgent.cs, TransformBasedMovement.cs
│ │ └── NavAgentUsers/ # 预置行为脚本 (8个)
│ ├── NavSurface/ # NavSurface.cs, 碰撞体过滤, 线段创建
│ │ ├── NavSegments/ # 线段数据结构
│ │ └── Creation/ # 烘焙管线
│ ├── NavObjects/ # NavLink, NavLinkCluster, NavAreaMarker 等
│ ├── NavGraph/ # 导航图, 图节点, 空间索引
│ ├── Pathfinder/ # A* 寻路, 路径请求, 多线程
│ ├── PBWorld.cs # 全局单例管理器
│ ├── PathBerserker2dSettings.cs # 全局设置
│ └── IVelocityProvider.cs # 速度接口
├── Demo/ # 演示场景与脚本
│ ├── Scenes/ # 15+ 场景
│ └── Scripts/ # 演示脚本
├── Corgi/ # Corgi Engine 集成层
├── Resources/ # 全局设置资源文件
└── Documentation/ # 官方文档 (zip)
```
---
## 3. 核心组件
### 3.1 组件总览
| 组件 | 菜单路径 | 职责 |
|------|---------|------|
| **NavSurface** | `PathBerserker2d/Nav Surface` | 从 Collider2D 烘焙导航线段 |
| **NavAgent** | `PathBerserker2d/Nav Agent` | 寻路请求与路径跟随 |
| **NavLink** | `PathBerserker2d/Nav Link` | 两点间导航链接(跳跃/下落/攀爬等) |
| **NavLinkCluster** | `PathBerserker2d/Nav Link Cluster` | 多点互联链接集群(电梯/梯子) |
| **NavAreaMarker** | `PathBerserker2d/Nav Area Marker` | 区域 NavTag 标记 |
| **NavSegmentSubstractor** | — | 在烘焙时裁剪指定区域的线段 |
| **DynamicObstacle** | — | 标记 GameObject 在烘焙时被忽略 |
| **TransformBasedMovement** | — | 基于 Transform 的默认移动实现 |
### 3.2 组件依赖关系
```
NavAgent ←需要→ TransformBasedMovement (或自定义移动组件)
NavAgent ←依赖→ PBWorld (全局单例,自动创建)
NavSurface ←依赖→ 子对象的 Collider2D
NavLink / NavLinkCluster ←映射→ NavSurface 上的线段
NavAreaMarker ←需要→ RectTransform
NavSegmentSubstractor ←需要→ RectTransform
```
---
## 4. NavAgent 详解
### 4.1 状态机
```
NavAgent 状态:
┌─────────┐
│ Idle │ ←── 初始 / Stop() / ForceStop() / 到达目标
└────┬─────┘
│ PathTo()
┌─────────────────────────────┐
│ FollowPath │
│ ┌─────────────────────┐ │
│ │ OnSegment │ ←─ 在线段上移动
│ ├─────────────────────┤ │
│ │ WaitForLinkOnSegment│ ←─ 等待链接可用
│ ├─────────────────────┤ │
│ │ OnLink │ ←─ 正在穿越链接
│ └─────────────────────┘ │
└─────────────────────────────┘
```
### 4.2 公共方法
| 方法 | 返回值 | 说明 |
|------|--------|------|
| `PathTo(Vector2 goal)` | `bool` | 寻路到目标位置 |
| `PathTo(params Vector2[] goals)` | `bool` | 寻路到多个目标中最近的 |
| `UpdatePath()` | `void` | 触发重新寻路(不改变目标) |
| `SetRandomDestination()` | `bool` | 设置随机目标 |
| `Stop()` | `void` | 优雅停止(完成当前链接后停止) |
| `ForceStop()` | `void` | 立即强制停止 |
| `CompleteSegmentTraversal()` | `void` | 通知已完成线段移动(由移动组件调用) |
| `CompleteLinkTraversal()` | `void` | 通知已完成链接穿越(由移动组件调用) |
| `WarpToNearestSegment()` | `void` | 传送到最近线段 |
| `CanTraverseLink(int linkType)` | `bool` | 检查是否可穿越指定类型链接 |
| `CanReach(Vector2 goal)` | `bool` | 同步检查目标是否可达 |
| `CreatePathRequest()` | `PathRequest` | 创建自定义寻路请求 |
### 4.3 公共属性
| 属性 | 类型 | 说明 |
|------|------|------|
| `CurrentStatus` | `NavAgentStatus` | 当前状态枚举 |
| `IsIdle` | `bool` | 是否空闲 |
| `IsFollowingAPath` | `bool` | 是否正在跟随路径 |
| `IsOnLink` | `bool` | 是否正在链接上 |
| `IsMovingOnSegment` | `bool` | 是否在线段上移动 |
| `HasValidPosition` | `bool` | 当前位置是否有效(在线段上) |
| `Height` | `float` | 代理高度 |
| `MaxSlopeAngle` | `float` | 最大坡度角 |
| `CurrentSegmentNormal` | `Vector2` | 当前线段法线 |
| `PathGoal` | `Vector2` | 当前路径目标 |
| `Position` | `Vector2` | 代理位置 |
| `CurrentNavTagVector` | `int` | 当前位置的 NavTag |
| `TimeOnLink` | `float` | 在当前链接上经过的时间 |
### 4.4 事件
| 事件 | 参数 | 触发时机 |
|------|------|---------|
| `OnStartLinkTraversal` | `NavAgent, PathSegment` | 开始穿越链接 |
| `OnLinkTraversal` | `NavAgent, PathSegment, float t` | 链接穿越每帧回调t: 0→1 |
| `OnStartSegmentTraversal` | `NavAgent, PathSegment` | 开始线段移动 |
| `OnSegmentTraversal` | `NavAgent, PathSegment, float t` | 线段移动每帧回调 |
| `OnFailedToFindPath` | — | 寻路失败 |
| `OnStop` | — | 停止移动 |
| `OnReachedGoal` | — | 到达目标 |
| `OnStartFollowingNewPath` | — | 开始跟随新路径 |
### 4.5 Inspector 配置
| 字段 | 默认值 | 说明 |
|------|--------|------|
| `height` | 1 | 代理高度(影响净空检查) |
| `maxSlopeAngle` | 60 | 最大可行走坡度角 |
| `autoRepathIntervall` | 1 | 自动重新寻路间隔(秒) |
| `maximumDistanceToPathStart` | ∞ | 到路径起点的最大距离 |
| `linkTraversalCostMultipliers[]` | — | 各链接类型代价乘数 |
| `navTagTraversalCostMultipliers[]` | — | 各 NavTag 代价乘数 |
| `allowCloseEnoughPath` | `false` | 允许"近似到达"路径 |
| `enableDebugMessages` | `false` | 启用调试日志 |
---
## 5. NavSurface 详解
### 5.1 核心概念
NavSurface 是导航数据的来源。它扫描子对象上的 **Collider2D**,沿碰撞体表面生成导航线段,并在烘焙完成后将数据添加到全局 NavGraph。
关键特性:
- **自动烘焙**:编辑器中或运行时均可烘焙
- **移动平台**烘焙数据以本地坐标存储NavSurface Transform 移动时路径跟随
- **动态加载**`OnEnable` 添加到 NavGraph`OnDisable` 移除
### 5.2 公共属性
| 属性 | 类型 | 说明 |
|------|------|------|
| `WorldBounds` | `Rect` | 世界空间包围盒 |
| `MaxClearance` | `float` | 最大净空检查高度 |
| `MinClearance` | `float` | 最小净空(低于此值的线段被移除) |
| `CellSize` | `float` | 净空检查精度 |
| `ColliderMask` | `LayerMask` | 碰撞体过滤层 |
| `TotalLineLength` | `float` | 所有线段总长度 |
| `MaxSlopeAngle` | `float` | 最大坡度角 |
| `LocalToWorldMatrix` | `Matrix4x4` | 本地到世界矩阵 |
### 5.3 公共方法
| 方法 | 说明 |
|------|------|
| `IEnumerator Bake()` | 运行时烘焙(协程) |
| `Vector2 LocalToWorld(Vector2 pos)` | 本地坐标转世界坐标 |
| `Vector2 WorldToLocal(Vector2 pos)` | 世界坐标转本地坐标 |
### 5.4 事件
| 事件 | 说明 |
|------|------|
| `OnBakingCompleted` | 烘焙完成 |
| `OnReadyToPathfind` | 已添加到 NavGraph可以开始寻路 |
| `OnRemovedFromPathfinding` | 已从 NavGraph 移除 |
### 5.5 Inspector 烘焙配置
| 字段 | 默认值 | 说明 |
|------|--------|------|
| `maxClearance` | 1.8 | 障碍检查最大高度 |
| `minClearance` | 0.1 | 净空低于此值的线段被移除 |
| `cellSize` | 0.1 | 障碍检查精度(越小越精确但越慢) |
| `includedColliders` | ~0 | 参与烘焙的层 |
| `onlyStaticColliders` | false | 仅包含静态碰撞体 |
| `maxSlopeAngle` | 180 | 移除超过此角度的线段 |
| `smallestDistanceYouCareAbout` | 0.1 | RDP 线简化容差 |
| `minSegmentLength` | 0.1 | 最小线段长度 |
### 5.6 使用示例
```
场景层级结构:
NavSurface (GameObject)
├── Ground (BoxCollider2D)
├── Platform_1 (BoxCollider2D)
├── Platform_2 (PolygonCollider2D)
└── Wall (BoxCollider2D)
```
> **注意**: NavSurface 只扫描 **子对象** 上的 Collider2D自身上的碰撞体不会被烘焙。
---
## 6. NavLink 与 NavLinkCluster
### 6.1 NavLink — 单个导航链接
NavLink 连接两个点,代表跳跃、下落、攀爬等需要特殊处理的路径段。
#### 可视化类型
| 类型 | 说明 |
|------|------|
| `Linear` | 直线 |
| `QuadradticBezier` | 二次贝塞尔曲线 |
| `Projectile` | 抛物线 |
| `Teleport` | 传送(瞬移) |
| `None` | 无可视化 |
| `TransformBasedMovement` | 使用 TransformBasedMovement 的默认处理 |
#### 公共属性
| 属性 | 类型 | 说明 |
|------|------|------|
| `GoalWorldPosition` | `Vector2` | 目标世界位置(可读写) |
| `StartWorldPosition` | `Vector2` | 起点世界位置(可读写) |
| `IsBidirectional` | `bool` | 是否双向 |
| `IsAddedToWorld` | `bool` | 是否已添加到 NavGraph |
#### 公共方法
| 方法 | 说明 |
|------|------|
| `UpdateMapping()` | 位置改变后更新链接映射 |
| `SetStartToGoalLinkTraversable(bool)` | 设置正向可穿越性 |
| `SetGoalToStartLinkTraversable(bool)` | 设置反向可穿越性 |
#### 共有基类属性 (BaseNavLink)
| 属性 | 说明 |
|------|------|
| `LinkType` | 链接类型索引corner=0, jump=1, fall=2, teleport=3, climb=4, elevator=5 |
| `Clearance` | 允许通过的最大代理高度 |
| `AvgWaitTime` | 平均等待时间(影响寻路代价) |
| `CostOverride` | 代价覆盖≤0 使用距离计算) |
| `NavTag` | NavTag 标记 |
| `MaxTraversableDistance` | 最大可穿越距离 |
| `autoMap` | 自动映射到 NavGraph |
### 6.2 NavLinkCluster — 链接集群
NavLinkCluster 适用于多点互联场景,如 **电梯**(多层站点)、**梯子**(多个停靠点)。
内部为每对点自动生成一个链接。
#### LinkPoint 结构
```csharp
struct LinkPoint {
Vector2 point;
PointTraversalType traversalType; // Exit | Entry | Both
}
```
#### 公共方法
| 方法 | 说明 |
|------|------|
| `UpdateMapping()` | 更新所有链接映射 |
| `SetLinksTraversable(Func<Vector2, Vector2, bool>)` | 根据起终点函数设置各链接可穿越性 |
#### 使用场景
```csharp
// 电梯到达某层时,只允许穿越与该层相关的链接
navLinkCluster.SetLinksTraversable((start, goal) =>
Mathf.Abs(start.y - currentFloor.position.y) < 0.1f ||
Mathf.Abs(goal.y - currentFloor.position.y) < 0.1f);
// 离开时禁用所有链接
navLinkCluster.SetLinksTraversable((start, goal) => false);
```
---
## 7. 辅助组件
### 7.1 NavAreaMarker — 区域标记
**要求**: `RectTransform`
将矩形区域内的所有导航线段标记为指定 NavTag。常用于标记水域、危险区域、门区域等。
| 属性/方法 | 说明 |
|-----------|------|
| `NavTag` | NavTag 索引 |
| `MarkerColor` | 标记颜色 |
| `updateAfterTimeOfNoMovement` | 停止移动后更新延迟 |
| `updateAfterTime` | 最大更新间隔 |
| `UpdateMappings()` | 手动更新映射 |
**门控模式**: 通过启用/禁用 NavAreaMarker 来控制门的通行:
```csharp
// 开门 → 禁用标记 → 代理可通过
navAreaMarker.enabled = false;
// 关门 → 启用标记 → 区域被标记为不可通行 NavTag
navAreaMarker.enabled = true;
```
### 7.2 NavSegmentSubstractor — 线段裁剪器
**要求**: `RectTransform`
在烘焙时从指定矩形区域移除线段。支持角度过滤:
| 字段 | 说明 |
|------|------|
| `fromAngle` | 最小角度过滤 |
| `toAngle` | 最大角度过滤 |
### 7.3 DynamicObstacle — 动态障碍物
空标记组件。附加到 GameObject 上,使其在 NavSurface 烘焙时被忽略。
### 7.4 IVelocityProvider — 速度接口
```csharp
interface IVelocityProvider {
Vector2 WorldVelocity { get; }
}
```
用于 `Follower` 组件的目标预测功能。在跟随目标上实现此接口即可。
---
## 8. 全局系统 — PBWorld 与 Settings
### 8.1 PBWorld — 全局单例
自动实例化,`DontDestroyOnLoad`。管理全局 NavGraph 和寻路调度。
#### 静态 API
```csharp
// === 点映射 ===
// 映射世界点到最近导航位置
static bool TryMapPoint(Vector2 position, out NavSegmentPositionPointer pointer)
// 自定义搜索半径
static bool TryMapPoint(Vector2 position, float searchRadius, out NavSegmentPositionPointer pointer)
// 确保映射位置对代理可通行
static bool TryMapPoint(Vector2 position, float searchRadius, NavAgent agent, out NavSegmentPositionPointer pointer)
// === 随机 ===
static Vector2 GetRandomPointOnGraph() // 返回图上随机点
// === 寻路 ===
static void PathTo(PathRequest pathRequest) // 入队异步寻路请求
// === 空间查询 ===
static List<NavSubsegmentPointer> BoxCast(Rect rect, float rotation, float fromAngle, float toAngle)
// === 图变化监听 ===
static INavGraphChangeSource NavGraphChangeSource // 订阅 OnGraphChange 事件
```
#### NavGraphChange 事件枚举
| 值 | 说明 |
|----|------|
| `NavSurfaceAdded` | NavSurface 添加 |
| `NavSurfaceRemoved` | NavSurface 移除 |
| `NavLinkAdded` | NavLink 添加 |
| `NavLinkRemoved` | NavLink 移除 |
| `SegmentModifierAdded` | 线段修改器添加 |
| `SegmentModifierRemoved` | 线段修改器移除 |
| `NavLinkMoved` | NavLink 位置变更 |
### 8.2 NavSegmentPositionPointer
导航位置指针结构体,用于表示一个在导航图上的有效位置:
| 属性 | 类型 | 说明 |
|------|------|------|
| `Position` | `Vector2` | 世界位置 |
| `Normal` | `Vector2` | 法线 |
| `IsValid()` | `bool` | 是否有效 |
| `IsInvalid()` | `bool` | 是否无效 |
---
## 9. 路径与寻路请求
### 9.1 PathRequest — 寻路请求
| 枚举 | 值 |
|------|-----|
| **RequestState** | `Draft`, `Pending`, `Finished`, `Failed` |
| **RequestFailReason** | `CouldntMapStart`, `CouldntMapGoal`, `MappedStartChanged`, `AllMappedGoalsChanged`, `NoPathFromStartToGoal`, `WorldWasDestroyed`, `ToFarFromStart` |
| 属性 | 类型 | 说明 |
|------|------|------|
| `Status` | `RequestState` | 请求状态 |
| `FailReason` | `RequestFailReason` | 失败原因 |
| `Path` | `Path` | 计算出的路径 |
| `start` | `NavSegmentPositionPointer` | 起点 |
| `goals` | `IList<NavSegmentPositionPointer>` | 目标列表 |
| `client` | `NavAgent` | 请求代理 |
### 9.2 Path — 路径
| 属性/方法 | 说明 |
|-----------|------|
| `Current` | 当前路径片段 (`PathSegment`) |
| `NextSegment` | 下一个片段 |
| `HasNext` | 是否有下一个 |
| `Goal` | 目标位置 |
| `Start` | 起点位置 |
| `totalCosts` | 总代价 |
| `MoveNext()` | 前进到下一片段 |
| `AllPathPoints()` | 获取所有路径点列表 |
| `RemainingPathPoints()` | 获取剩余路径点列表 |
### 9.3 PathSegment — 路径片段
| 属性/方法 | 说明 |
|-----------|------|
| `Next` | 下一个片段 |
| `LinkStart` | 链接起点(世界坐标,随 NavSurface 移动) |
| `LinkEnd` | 链接终点 |
| `Normal` | 线段法线 |
| `Tangent` | 线段切线 |
| `Point` | 线段上的参考点 |
| `link` | 关联的链接实例 |
| `owner` | 所属 NavSurface |
| `GetTagVector(float t)` | 获取位置的 NavTag |
| `DistanceAlongSegment(Vector2 pos)` | 投影距离 |
---
## 10. 预置行为脚本
PathBerserker2d 提供 8 个即用的 NavAgent 行为脚本:
| 组件 | 说明 | 典型用途 |
|------|------|---------|
| **MouseWalker** | 鼠标点击处寻路 | 调试、点击移动原型 |
| **PatrolWalker** | 按顺序巡逻路线,循环 | 巡逻敌人 |
| **RandomWalker** | 持续随机行走 | NPC 闲逛 |
| **MultiGoalWalker** | 寻路到多个目标中最近的一个 | 收集物搜索 |
| **Follower** | 跟随目标 Transform支持目标预测 | 追踪型敌人 |
| **KeepGrounded** | 通过 Raycast 检测移动平台并 parent | 移动平台上的代理 |
| **AdjustRotation** | 旋转代理匹配线段法线方向 | 沿曲面行走的方向适配 |
| **FootStepSounds** | 根据 NavTag 播放不同脚步声 | 环境音效 |
### 使用方式
直接将这些脚本添加到带有 NavAgent 的 GameObject 上即可。它们自动获取同 GameObject 上的 NavAgent 引用。
---
## 11. TransformBasedMovement 移动系统
### 11.1 概述
`TransformBasedMovement` 是 PathBerserker2d 提供的默认移动实现。它订阅 NavAgent 的事件,在回调中直接修改 Transform 位置,并在完成后调用 `CompleteSegmentTraversal()` / `CompleteLinkTraversal()` 通知 NavAgent。
### 11.2 FeatureFlags
```csharp
[Flags] enum FeatureFlags {
SegmentMovement = 1, // 线段移动
JumpLinks = 2, // 跳跃链接
CornerLinks = 4, // 拐角链接
FallLinks = 8, // 下落链接
TeleportLinks = 16, // 传送链接
ClimbLinks = 32, // 攀爬链接
ElevatorLinks = 64, // 电梯链接
OtherLinks = 128, // 其他链接
}
```
### 11.3 配置属性
| 字段 | 默认值 | 说明 |
|------|--------|------|
| `movementSpeed` | 5 | 线段移动速度 (unit/s) |
| `cornerSpeed` | 100 | 拐角链接速度 (degrees/s) |
| `jumpSpeed` | 5 | 跳跃速度 (unit/s) |
| `fallSpeed` | 5 | 下落速度 (unit/s) |
| `climbSpeed` | 5 | 攀爬速度 (unit/s) |
| `enableAgentRotation` | true | 是否旋转代理朝向移动方向 |
| `enabledFeatures` | 全部启用 | 控制由本组件处理的功能 |
### 11.4 自定义移动组件
如果 `TransformBasedMovement` 不满足需求(例如需要物理驱动移动),可以创建自定义移动组件:
```csharp
public class CustomMovement : MonoBehaviour
{
NavAgent navAgent;
void Awake() => navAgent = GetComponent<NavAgent>();
void OnEnable()
{
navAgent.OnStartSegmentTraversal += HandleSegmentStart;
navAgent.OnSegmentTraversal += HandleSegmentMove;
navAgent.OnStartLinkTraversal += HandleLinkStart;
navAgent.OnLinkTraversal += HandleLinkMove;
}
void HandleSegmentMove(NavAgent agent, PathSegment seg, float t)
{
// 自定义线段移动逻辑
// ...
// 完成后:
agent.CompleteSegmentTraversal();
}
void HandleLinkMove(NavAgent agent, PathSegment seg, float t)
{
// 自定义链接穿越逻辑
// ...
// 完成后:
agent.CompleteLinkTraversal();
}
}
```
---
## 12. Demo 场景与使用模式
### 12.1 Demo 场景一览
| 场景 | 展示内容 |
|------|---------|
| `breakable_wall` | 可破坏墙壁 + 寻路 |
| `door` | NavAreaMarker 门控 |
| `elevator` | NavLinkCluster 电梯系统 |
| `follow_target` | Follower 跟随行为 |
| `ladder` | 攀爬链接 |
| `moving_platform` | 移动平台导航 |
| `multi_surface` | 多 NavSurface 场景 |
| `procedural_endless_jumper` | 运行时烘焙 |
| `runtime_bake` | 运行时动态烘焙 |
| `simple_patrol` | 基础巡逻 |
| `teleport` | 传送链接 |
| `threesixtydegree` | 360° 全向导航 |
| `trafficlight` | 交通灯 + 等待逻辑 |
| `tricky_jump` | 复杂跳跃场景 |
### 12.2 常用使用模式
#### 模式 1最简单的寻路
```csharp
// GoalWalker 模式:检查距离 + IsIdle → PathTo
void Update()
{
if (Vector2.Distance(goal.position, navAgent.transform.position) > 0.5f
&& (navAgent.IsIdle || goal.hasChanged))
{
goal.hasChanged = false;
navAgent.PathTo(goal.position);
}
}
```
#### 模式 2电梯 — 动态链接切换
```csharp
// 电梯到达某层 → 启用相关链接
navLinkCluster.SetLinksTraversable((start, goal) =>
Mathf.Abs(start.y - levels[nextLevel].position.y) < 0.1f ||
Mathf.Abs(goal.y - levels[nextLevel].position.y) < 0.1f);
// 电梯离开 → 禁用所有链接
navLinkCluster.SetLinksTraversable((start, goal) => false);
```
#### 模式 3门 — NavAreaMarker 控制
```csharp
// 开门:禁用 NavAreaMarker → 区域不再有阻挡标记
navAreaMarker.enabled = false;
// 关门:启用 NavAreaMarker → 门区域被标记为不可通行 NavTag
navAreaMarker.enabled = true;
```
#### 模式 4移动平台
```csharp
// MovingPlatform 是 NavSurface 的子对象
// 只需移动 NavSurface 的 Transform导航数据自动跟随
transform.Translate(velocity * Time.deltaTime);
```
#### 模式 5运行时烘焙
```csharp
// 在运行时触发烘焙
StartCoroutine(navSurface.Bake());
// 监听烘焙完成
navSurface.OnBakingCompleted += () => Debug.Log("Bake done!");
navSurface.OnReadyToPathfind += () => Debug.Log("Ready for pathfinding!");
```
---
## 13. Corgi 引擎集成
PathBerserker2d 附带 **CorgiBasedMovement** 组件,替代 `TransformBasedMovement` 以对接 Corgi Engine 的角色控制系统。
### 13.1 核心映射
| 导航行为 | Corgi 映射 |
|---------|-----------|
| 线段移动 | `CharacterHorizontalMovement.SetHorizontalMove()` |
| 跳跃链接 | `CharacterJump.JumpStart()` |
| 攀爬链接 | `CharacterLadder`(反射调用) |
| 传送链接 | 直接设置 Position |
| 拐角链接 | 直接 `CompleteLinkTraversal()` |
| 卡住检测 | `fallbackTeleportDelay` 秒后传送 |
### 13.2 Corgi AI Actions
| 脚本 | 说明 |
|------|------|
| `AIActionPBMoveTowardsTarget` | 移向目标 |
| `AIActionPBMoveTowardsClosestTarget` | 移向最近目标 |
| `AIActionPBMoveTowardsRandomPathableTarget` | 移向可达随机目标 |
| `AIActionPBPatrol` | 巡逻 |
| `AIDecisionPBHasReachedGoal` | 判断是否到达 |
| `AIDecisionPBPathfindingFailed` | 判断寻路失败 |
> **注意**: 本项目使用自研架构而非 Corgi Engine此集成仅作参考。
---
## 14. 全局设置参考
**位置**: `Edit > Project Settings > PathBerserker2d`
**资源文件**: `Assets/PathBerserker2d/Resources/PathBerserker2dSettings`
### 14.1 设置项
| 属性 | 类型 | 当前值 | 说明 |
|------|------|--------|------|
| `PointMappingDistance` | float | 0.2 | 点映射最大距离(性能关键,应尽量小) |
| `PathfinderThreadCount` | int | 4 | 寻路线程数WebGL 强制 = 1 |
| `InitiateUpdateInterval` | float | 0.1s | NavGraph 更新间隔 |
| `DrawGraphWhilePlaying` | bool | — | 运行时绘制导航图 |
| `NavSurfaceLineWidth` | float | — | 线宽 |
| `UsePolygonCollider2dPathsForBaking` | bool | — | 多边形碰撞体烘焙方式 |
### 14.2 内置链接类型
| 索引 | 名称 | 说明 |
|------|------|------|
| 0 | `corner` | 拐角(自动生成) |
| 1 | `jump` | 跳跃 |
| 2 | `fall` | 下落 |
| 3 | `teleport` | 传送 |
| 4 | `climb` | 攀爬 |
| 5 | `elevator` | 电梯 |
### 14.3 内置 NavTag
| 名称 | 用途 |
|------|------|
| `default` | 默认 |
| `water` | 水域 |
| `lava` | 岩浆 |
| `grass` | 草地 |
| `concrete` | 混凝土 |
| `dirt` | 泥土 |
---
## 15. 与 BaseGames 架构集成方案
### 15.1 防腐层原则
根据 BaseGames 架构总纲(`00_Architecture_Overview.md`)第 3 条设计理念:
> **防腐层隔离**: 业务代码永远不直接调用 PathBerserker2d 等第三方 API。所有第三方交互通过 `Adapters/` 层代理。
因此 PathBerserker2d 的所有功能必须通过 **适配器层** 暴露给业务系统。
### 15.2 适配器设计
```
Layer 3 — Adapters/
└── NavAgentAdapter.cs # PathBerserker2d NavAgent 的防腐包装
业务层调用链:
EnemyAI (Layer 5) → NavAgentAdapter (Layer 3) → NavAgent (第三方)
```
#### NavAgentAdapter 接口建议
```csharp
namespace BaseGames.Adapters
{
/// <summary>
/// PathBerserker2d NavAgent 的防腐层适配器。
/// 业务代码通过此接口进行寻路,不直接引用 PathBerserker2d API。
/// </summary>
public interface INavigation
{
bool PathTo(Vector2 goal);
bool PathTo(params Vector2[] goals);
void Stop();
void ForceStop();
bool IsIdle { get; }
bool IsFollowingPath { get; }
bool CanReach(Vector2 goal);
Vector2 CurrentPosition { get; }
event Action OnReachedGoal;
event Action OnPathFailed;
event Action OnStopped;
}
}
```
### 15.3 与其他系统的集成点
| BaseGames 系统 | 集成方式 |
|----------------|---------|
| **敌人 AI (20_Enemy_AI)** | 行为树通过 `INavigation` 接口调用寻路 |
| **环境系统 (11_Environment)** | MovingPlatform 使用 NavSurface 子对象模式 |
| **环境系统 (11_Environment)** | BreakableWall 销毁后触发 NavSurface 重新烘焙 |
| **环境系统 (11_Environment)** | TeleportGate 对应 NavLinkteleport 类型) |
| **物理动画 (08_Physics)** | 自定义移动组件桥接 `IPhysicsBody` 与 NavAgent 事件 |
| **标签系统 (02_GameTag)** | NavTag 可映射为 GameTagSO 用于环境效果触发 |
### 15.4 自定义移动组件集成
由于 BaseGames 使用自研物理系统 (`IPhysicsBody` / `RaycastBody2D`),不应使用默认的 `TransformBasedMovement`。需要创建桥接移动组件:
```csharp
namespace BaseGames.Adapters
{
public class PhysicsBasedNavMovement : MonoBehaviour
{
// 订阅 NavAgent 事件
// 将移动指令转发给 IPhysicsBody
// 通过 IPhysicsBody 驱动实际移动
// 移动完成后调用 CompleteSegmentTraversal() / CompleteLinkTraversal()
}
}
```
---
## 16. 性能分析与优化建议
### 16.1 性能特性
| 方面 | 评估 |
|------|------|
| **寻路** | 多线程 A\*,不阻塞主线程。线程数可配置(默认 4 |
| **烘焙** | 协程方式,可分帧执行。复杂场景烘焙较重 |
| **内存** | 线段数据以本地坐标存储,紧凑 |
| **空间索引** | B2DynamicTreeBox2D 动态树),查询高效 |
| **WebGL** | 自动降级为单线程协程模式 |
### 16.2 优化建议
| 建议 | 说明 |
|------|------|
| **减小 `PointMappingDistance`** | 当前 0.2,越小映射查询越快 |
| **调整 `autoRepathIntervall`** | 不要过于频繁1-2 秒通常足够 |
| **合理设置 `cellSize`** | 烘焙精度越高越慢0.1 适合大多数情况 |
| **分区管理 NavSurface** | 按房间/区域拆分 NavSurface按需启用/禁用 |
| **限制同时寻路数** | 大量代理时排队处理,避免请求洪峰 |
| **善用 `onlyStaticColliders`** | 排除动态碰撞体可加速烘焙 |
---
## 17. 优缺点总结
### 17.1 优势
| 优势 | 说明 |
|------|------|
| **原生 2D 支持** | 基于线段而非面片,天然适合 2D 横版游戏 |
| **完整源码** | 所有代码可见,便于调试和定制 |
| **链接系统丰富** | 内置 6 种链接类型,覆盖跳跃/下落/攀爬/电梯/传送/拐角 |
| **动态世界** | 运行时烘焙、移动平台、动态链接切换 |
| **多线程寻路** | 不阻塞主线程,支持多代理并发 |
| **事件驱动架构** | 移动实现完全解耦,易于替换为自定义移动系统 |
| **预置行为丰富** | 8 个即用行为脚本覆盖常见场景 |
| **移动平台支持** | 本地坐标体系自动跟随 NavSurface Transform 变化 |
### 17.2 不足
| 不足 | 说明 |
|------|------|
| **文档较简陋** | 官方文档以 Demo 为主,缺少系统性 API 文档 |
| **无官方维护迹象** | Asset Store 版本更新不频繁 |
| **烘焙仅支持 Collider2D** | 不支持 Tilemap 直接烘焙(需转换为 Collider |
| **链接需手动放置** | 跳跃/攀爬等链接需要手动在编辑器中放置 |
| **360° 模式限制** | 虽支持 360° 但主要为横版优化 |
| **缺少动态避障** | 无 RVO / ORCA 等动态避障实现 |
| **NavTag 数量有限** | 内置 6 个 NavTag扩展需修改设置 |
### 17.3 技术评分
| 维度 | 评分 (1-10) | 说明 |
|------|-------------|------|
| 2D 适配度 | **9** | 专为 2D 设计,线段导航非常贴合横版场景 |
| API 设计 | **8** | 事件驱动架构清晰,易于扩展 |
| 文档质量 | **5** | Demo 丰富但系统文档不足 |
| 性能 | **8** | 多线程寻路 + B2DynamicTree 空间索引 |
| 易用性 | **7** | 基础使用简单,高级功能学习曲线稍陡 |
| 可维护性 | **7** | 完整源码可见,但注释较少 |
| 扩展性 | **8** | 事件驱动 + 接口隔离,易接入自定义系统 |
| **综合** | **7.4** | 适合 2D Metroidvania 项目的可靠寻路方案 |
---
## 18. 总结与建议
### 18.1 总体评价
PathBerserker2d 是目前 Unity 生态中少有的 **专为 2D 横版设计** 的寻路方案。其基于线段的导航模型天然适配平台跳跃类游戏,多线程异步寻路保证了性能,事件驱动架构提供了良好的扩展性。
对于 BaseGames 这样的 2D Metroidvania 项目,它提供了:
- **完整的 2D 导航能力**:地面行走、跳跃、下落、攀爬、电梯、传送
- **移动平台支持**:天然适配关卡中的动态地形
- **与自定义物理系统的解耦**:通过事件驱动架构可桥接 `IPhysicsBody`
### 18.2 集成建议
1. **严格通过适配器层访问** — 遵循 BaseGames 防腐层原则,创建 `NavAgentAdapter` / `INavigation` 接口
2. **替换 TransformBasedMovement** — 创建 `PhysicsBasedNavMovement` 桥接 `IPhysicsBody`
3. **按房间分区 NavSurface** — 配合关卡加载系统,按需启用/禁用导航区域
4. **NavTag 映射 GameTagSO** — 统一标签体系,复用效果系统触发逻辑
5. **行为树集成** — 通过 `INavigation` 接口在行为树节点中调用寻路
### 18.3 注意事项
- 可破坏墙壁销毁后需触发 NavSurface 重新烘焙
- 电梯/升降台需使用 NavLinkCluster 配合动态可穿越性控制
- 大量 AI 代理场景需排队管理寻路请求避免性能瓶颈
- WebGL 构建时自动降级为单线程,需额外测试性能