refactor(editor): 脚手架改用 GroundNavigator,移除寻路库引用与导航面放置工具

This commit is contained in:
2026-07-28 11:45:24 +08:00
parent 219fe46719
commit 4455062ee9
10 changed files with 14 additions and 695 deletions
@@ -24,7 +24,6 @@
"BaseGames.Feedback",
"BaseGames.Dialogue",
"BaseGames.Progression",
"PathBerserker2d",
"Unity.Cinemachine",
"Kybernetik.Animancer",
"BaseGames.Animation",
@@ -1,86 +0,0 @@
using System.Reflection;
using UnityEditor;
using UnityEngine;
using PathBerserker2d;
namespace BaseGames.Editor
{
/// <summary>
/// 快捷键:BaseGames → Tools → Bake All NavSurfacesCtrl+Shift+B
/// 烘焙当前场景中所有 PathBerserker2d NavSurface 的导航网格。
/// 等效于在每个 NavSurface Inspector 中逐一点击 "Bake"。
/// </summary>
public static class NavSurfaceBakeShortcut
{
// NavSurface.StartBakeJob() 和 NavSurface.BakeJob 均为 internal,通过反射访问。
private static readonly MethodInfo s_startBakeJobMethod =
typeof(NavSurface).GetMethod("StartBakeJob", BindingFlags.NonPublic | BindingFlags.Instance);
private static readonly PropertyInfo s_bakeJobProp =
typeof(NavSurface).GetProperty("BakeJob", BindingFlags.NonPublic | BindingFlags.Instance);
[MenuItem("BaseGames/Scene/Bake All NavSurfaces %#b", priority = 100)]
public static void BakeAll()
{
var surfaces = Object.FindObjectsByType<NavSurface>(FindObjectsSortMode.None);
if (surfaces.Length == 0)
{
Debug.Log("[NavSurfaceBake] 当前场景没有找到 NavSurface 组件。");
return;
}
int count = 0;
foreach (var surface in surfaces)
{
if (surface == null) continue;
s_startBakeJobMethod?.Invoke(surface, null);
EditorApplication.update -= MakeWatcher(surface);
EditorApplication.update += MakeWatcher(surface);
count++;
}
Debug.Log($"[NavSurfaceBake] 开始烘焙 {count} 个 NavSurface……");
}
[MenuItem("BaseGames/Scene/Bake All NavSurfaces %#b", validate = true)]
private static bool BakeAllValidate()
{
// 仅在非 Play Mode 时可用(NavSurface.Bake 仅支持编辑器模式)
return !Application.isPlaying;
}
// ── 每个 NavSurface 独立监听烘焙完成 ──────────────────────────────
private static EditorApplication.CallbackFunction MakeWatcher(NavSurface surface)
{
EditorApplication.CallbackFunction watcher = null;
watcher = () =>
{
if (surface == null)
{
EditorApplication.update -= watcher;
return;
}
var bakeJob = s_bakeJobProp?.GetValue(surface);
if (bakeJob == null)
{
EditorApplication.update -= watcher;
return;
}
bool isFinished = (bool)bakeJob.GetType()
.GetProperty("IsFinished")!.GetValue(bakeJob);
if (isFinished)
{
EditorApplication.update -= watcher;
EditorUtility.SetDirty(surface);
float totalTime = (float)bakeJob.GetType()
.GetProperty("TotalBakeTime")!.GetValue(bakeJob);
Debug.Log($"[NavSurfaceBake] ✓ {surface.name} 烘焙完成({totalTime} ms");
}
};
return watcher;
}
}
}
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 291f3b33fb176b8469ebaaa8afa317ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -21,7 +21,6 @@ using BaseGames.Player;
using BaseGames.Player.States;
using BaseGames.Skills;
using BaseGames.World;
using PathBerserker2d;
using Unity.Cinemachine;
using UnityEditor;
using UnityEngine;
@@ -303,10 +302,7 @@ namespace BaseGames.Editor
EnemyBase enemyBase = GetOrAddComponent<EnemyBase>(go);
EnemyStats enemyStats = GetOrAddComponent<EnemyStats>(go);
EnemyMovement movement = GetOrAddComponent<EnemyMovement>(go);
GetOrAddComponent<EnemyNavAgent>(go);
NavAgent navAgent = GetOrAddComponent<NavAgent>(go);
AssignBool(navAgent, "allowCloseEnoughPath", true); // 追击近似目标(玩家常不在导航段上)寻路到最近可达点
GetOrAddComponent<TransformBasedMovement>(go); // required by EnemyNavAgent [RequireComponent]
GetOrAddComponent<BaseGames.Enemies.Navigation.GroundNavigator>(go); // 地面导航(直接移动,无寻路图)
PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go);
var (hurtBox, bodyContact, contactHitBox) =
@@ -405,7 +401,7 @@ namespace BaseGames.Editor
SetupPerceptionSystemSlots(sensorHub, new[] { "aggro", "attack_melee", "attack_range", "los" }, report);
report.Add("填写 _bossId。");
report.Add("★ 挂载 BossSkillExecutor 并指定 BossSkillSO 列表(技能执行层)NavAgent 需手工添加。");
report.Add("★ 挂载 BossSkillExecutor 并指定 BossSkillSO 列表(技能执行层)。");
report.Add("★ AI(BrainGraph):先写一个 [AiDefinition(\"<id>\")] 的 AiScript 子类," +
"再挂 EnemyAiBrain 组件并把 _definitionId 设为该 id。");
report.Add("多阶段 Boss 可在此 GameObject 上继续 AddComponent 阶段切换控制器。");
@@ -448,10 +444,7 @@ namespace BaseGames.Editor
EnemyBase enemyBase = GetOrAddComponent<EnemyBase>(go);
EnemyStats enemyStats = GetOrAddComponent<EnemyStats>(go);
EnemyMovement movement = GetOrAddComponent<EnemyMovement>(go);
GetOrAddComponent<EnemyNavAgent>(go);
NavAgent navAgent = GetOrAddComponent<NavAgent>(go);
AssignBool(navAgent, "allowCloseEnoughPath", true); // 追击近似目标(玩家常不在导航段上)寻路到最近可达点
GetOrAddComponent<TransformBasedMovement>(go); // required by EnemyNavAgent [RequireComponent]
GetOrAddComponent<BaseGames.Enemies.Navigation.GroundNavigator>(go); // 地面导航(直接移动,无寻路图)
PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go);
var (hurtBox, bodyContact, contactHitBox) =
@@ -545,9 +538,7 @@ namespace BaseGames.Editor
EnemyBase enemyBase = GetOrAddComponent<EnemyBase>(go);
EnemyStats enemyStats = GetOrAddComponent<EnemyStats>(go);
EnemyMovement movement = GetOrAddComponent<EnemyMovement>(go);
GetOrAddComponent<EnemyNavAgent>(go);
GetOrAddComponent<NavAgent>(go);
GetOrAddComponent<TransformBasedMovement>(go); // required by EnemyNavAgent [RequireComponent]
GetOrAddComponent<BaseGames.Enemies.Navigation.GroundNavigator>(go); // 地面导航(直接移动,无寻路图)
PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go);
// HurtBox + ContactDamageZone(身体接触伤害常驻开启;HurtBox 悬挂期外关闭见下)
@@ -647,10 +638,7 @@ namespace BaseGames.Editor
EnemyBase enemyBase = GetOrAddComponent<EnemyBase>(go);
EnemyStats enemyStats = GetOrAddComponent<EnemyStats>(go);
EnemyMovement movement = GetOrAddComponent<EnemyMovement>(go);
GetOrAddComponent<EnemyNavAgent>(go);
NavAgent navAgent = GetOrAddComponent<NavAgent>(go);
AssignBool(navAgent, "allowCloseEnoughPath", true); // 追击近似目标(玩家常不在导航段上)寻路到最近可达点
GetOrAddComponent<TransformBasedMovement>(go); // required by EnemyNavAgent [RequireComponent]
GetOrAddComponent<BaseGames.Enemies.Navigation.GroundNavigator>(go); // 地面导航(直接移动,无寻路图)
PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go);
var (hurtBox, bodyContact, contactHitBox) =
@@ -739,9 +727,7 @@ namespace BaseGames.Editor
EnemyStats enemyStats = GetOrAddComponent<EnemyStats>(go);
EnemyFeedback feedback = GetOrAddComponent<EnemyFeedback>(go);
EnemyMovement movement = GetOrAddComponent<EnemyMovement>(go);
GetOrAddComponent<EnemyNavAgent>(go);
GetOrAddComponent<NavAgent>(go);
GetOrAddComponent<TransformBasedMovement>(go); // required by EnemyNavAgent [RequireComponent]
GetOrAddComponent<BaseGames.Enemies.Navigation.GroundNavigator>(go); // 地面导航(直接移动,无寻路图)
PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go);
var (hurtBox, bodyContact, contactHitBox) =
@@ -870,9 +856,7 @@ namespace BaseGames.Editor
EnemyBase enemyBase = GetOrAddComponent<EnemyBase>(go);
EnemyStats enemyStats = GetOrAddComponent<EnemyStats>(go);
EnemyMovement movement = GetOrAddComponent<EnemyMovement>(go);
GetOrAddComponent<EnemyNavAgent>(go);
GetOrAddComponent<NavAgent>(go);
GetOrAddComponent<TransformBasedMovement>(go); // required by EnemyNavAgent [RequireComponent]
GetOrAddComponent<BaseGames.Enemies.Navigation.GroundNavigator>(go); // 地面导航(直接移动,无寻路图)
PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go);
var (hurtBox, bodyContact, contactHitBox) =
@@ -982,10 +966,7 @@ namespace BaseGames.Editor
EnemyBase enemyBase = GetOrAddComponent<EnemyBase>(go);
EnemyStats enemyStats = GetOrAddComponent<EnemyStats>(go);
EnemyMovement movement = GetOrAddComponent<EnemyMovement>(go);
GetOrAddComponent<EnemyNavAgent>(go);
NavAgent navAgent = GetOrAddComponent<NavAgent>(go);
AssignBool(navAgent, "allowCloseEnoughPath", true); // 追击近似目标(玩家常不在导航段上)寻路到最近可达点
GetOrAddComponent<TransformBasedMovement>(go); // required by EnemyNavAgent [RequireComponent]
GetOrAddComponent<BaseGames.Enemies.Navigation.GroundNavigator>(go); // 地面导航(直接移动,无寻路图)
PhysicsPerceptionSystem sensorHub = GetOrAddComponent<PhysicsPerceptionSystem>(go);
var (hurtBox, bodyContact, contactHitBox) =
@@ -1086,9 +1067,7 @@ namespace BaseGames.Editor
EnemyStats bossStats = GetOrAddComponent<EnemyStats>(go);
EnemyFeedback feedback = GetOrAddComponent<EnemyFeedback>(go);
EnemyMovement movement = GetOrAddComponent<EnemyMovement>(go);
GetOrAddComponent<EnemyNavAgent>(go);
GetOrAddComponent<NavAgent>(go);
GetOrAddComponent<TransformBasedMovement>(go); // required by EnemyNavAgent [RequireComponent]
GetOrAddComponent<BaseGames.Enemies.Navigation.GroundNavigator>(go); // 地面导航(直接移动,无寻路图)
BossSkillExecutor skillExec = GetOrAddComponent<BossSkillExecutor>(go);
ChaoFengFloatController floatCtrl = GetOrAddComponent<ChaoFengFloatController>(go);
ChaoFengKnockdownCounter knockdown = GetOrAddComponent<ChaoFengKnockdownCounter>(go);
@@ -2082,22 +2061,6 @@ namespace BaseGames.Editor
MarkDirtyAndLog("Tilemap Ground", gridGo, report);
}
[MenuItem("BaseGames/Scene/Place/Nav Surface", priority = 170)]
public static void PlaceNavSurface()
{
var report = new List<string>();
GameObject go = new GameObject("NavSurface");
Undo.RegisterCreatedObjectUndo(go, "Place Nav Surface");
go.transform.position = GetDropPosition();
GetOrAddComponent<NavSurface>(go);
report.Add("NavSurface 已添加。在 Inspector 中点击 Bake 生成导航网格。");
Selection.activeGameObject = go;
MarkDirtyAndLog("Nav Surface", go, report);
}
[MenuItem("BaseGames/Scene/Place/Obstacle (Static)", priority = 190)]
public static void PlaceObstacle()
{
@@ -2185,14 +2148,14 @@ namespace BaseGames.Editor
col = box;
break;
}
AlignColliderBottomToPivot(col); // 底部对齐 y=0:原点=脚底,供 NavAgent 检测原点落在导航线上
AlignColliderBottomToPivot(col); // 底部对齐 y=0:原点=脚底
return col;
}
/// <summary>
/// 把碰撞体底部对齐到 transform 原点(y=0)——原点即脚底。
/// PathBerserker2d NavAgent 用 transform 原点(0,0)判定是否在导航段上,故所有创建物的碰撞体底部须在 y=0
/// 否则站到地面时原点悬在导航线上方,导致 HasValidPosition=False、寻路失效
/// 所有创建物的碰撞体底部对齐 y=0(原点=脚底),使地面探测与站位判定以脚底为基准
/// 否则站到地面时原点悬在地面上方,导致地面检测/落点判定出现偏差
/// </summary>
private static void AlignColliderBottomToPivot(Collider2D col)
{
@@ -15,7 +15,6 @@ using BaseGames.UI.Splash;
using BaseGames.World;
using BaseGames.World.Map;
using BaseGames.World.Streaming;
using PathBerserker2d;
using Unity.Cinemachine;
using UnityEditor;
using UnityEditor.SceneManagement;
@@ -764,10 +763,6 @@ namespace BaseGames.Editor
groundRb.bodyType = RigidbodyType2D.Static;
GetOrAddComponent<CompositeCollider2D>(groundTileGo);
// NavSurface for PathBerserker2d
GameObject navGo = GetOrCreateChild(envGroup, "NavSurface").gameObject;
GetOrAddComponent<NavSurface>(navGo);
// ── [Transitions] ──────────────────────────────────────────────
GetOrCreateChild(root.transform, "[Transitions]");
@@ -813,7 +808,7 @@ namespace BaseGames.Editor
else
report.Add($"MapRoomDataSO 已存在({roomSoPath}),请确认其 RoomId / Exits 与场景保持同步。");
report.Add("绑定 LensConfig SO 后单击 Inspector 中「从可视区域更新限位区域」计算正确的 BoxCollider。");
report.Add("使用 Tile Palette 在 Ground Tilemap 上绘制地形,然后在 NavSurface Inspector 中点击 Bake。");
report.Add("使用 Tile Palette 在 Ground Tilemap 上绘制地形。");
report.Add("[Transitions] 子节点下使用 BaseGames/Scene/Place/Room Transition 添加过渡点。");
MarkDirtyAndLog("Game Room 脚手架", root, report);