957 lines
34 KiB
Markdown
957 lines
34 KiB
Markdown
# 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 俯视角 RPG(360° 模式)
|
||
- 需要 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 对应 NavLink(teleport 类型) |
|
||
| **物理动画 (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) |
|
||
| **烘焙** | 协程方式,可分帧执行。复杂场景烘焙较重 |
|
||
| **内存** | 线段数据以本地坐标存储,紧凑 |
|
||
| **空间索引** | B2DynamicTree(Box2D 动态树),查询高效 |
|
||
| **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 构建时自动降级为单线程,需额外测试性能
|