refactor(editor): reorganize Editor directory and unify menu hierarchy
File directory changes (mirror Scripts/ module structure): - AbilityTypeDrawer.cs → Equipment/ - CharacterWizardWindow.cs → Character/ - FormEditorWindow.cs → Player/ - GMToolWindow.cs → Tools/ - SOManagerWindow.cs → Tools/ - Map/MapRoomDataEditor.cs → World/Map/ - Navigation/ (root) → Enemies/Navigation/ - Achievements/ → Progression/ Menu hierarchy changes (BaseGames/ top-level): - Data/: +Character Wizard (from Tools/), +Boss Skill Sequence (from Tools/) - Addressables/: +Addressable Batch Tool, +Asset Reference Graph, +Validate Address Keys (from Tools/Verification/) - Scene/Setup/: +Boot Flow Wizard, +Scaffold *, +Auto-Open Persistent (from Tools/) - Scene/: +Camera Area Setup (from Camera/), +Bake All NavSurfaces (from Tools/) - Events/: +Event Bus Monitor, +Event Chain Viewer, +Create/Reimport Event Channels (from Tools/) - Tools/Validation/: +Validate All SOs, +Apply/Validate Script Order (from Tools/ flat) - Tools/Maintenance/: +Missing Scripts/*, +Physics2D Layer Matrix/* (from Tools/ flat) Result: BaseGames/Tools/ reduced from 16 flat items to 4 items + 2 submenus Docs: update AssetFolderSpec §12 editor tool table with new menu paths Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
493
Assets/_Game/Scripts/Editor/Tools/GMToolWindow.cs
Normal file
493
Assets/_Game/Scripts/Editor/Tools/GMToolWindow.cs
Normal file
@@ -0,0 +1,493 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using BaseGames.Player;
|
||||
using BaseGames.Player.States;
|
||||
|
||||
namespace BaseGames.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 开发阶段 GM 调试工具窗口(仅 Play Mode 有效)。
|
||||
/// 功能:资源快速填充(灵铢/灵力/魄元)、能力解锁/锁定、形态切换、调试辅助。
|
||||
/// 菜单:BaseGames / Tools / GM Debug Tool
|
||||
/// </summary>
|
||||
public class GMToolWindow : EditorWindow
|
||||
{
|
||||
// ── 菜单 ──────────────────────────────────────────────────────────────
|
||||
[MenuItem("BaseGames/Tools/GM Debug Tool", priority = 2)]
|
||||
public static void Open()
|
||||
{
|
||||
var wnd = GetWindow<GMToolWindow>();
|
||||
wnd.titleContent = new GUIContent("GM Debug Tool");
|
||||
wnd.minSize = new Vector2(320, 500);
|
||||
}
|
||||
|
||||
// ── 资源输入字段 ──────────────────────────────────────────────────────
|
||||
private int _lingZhuAmount = 9999;
|
||||
private int _soulPowerAmount = 100;
|
||||
private int _spiritAmount = 100;
|
||||
|
||||
// ── 折叠状态 ──────────────────────────────────────────────────────────
|
||||
private bool _foldResources = true;
|
||||
private bool _foldJump = true;
|
||||
private bool _foldForms = true;
|
||||
private bool _foldAbilities = false;
|
||||
private bool _foldDebug = true;
|
||||
|
||||
// ── 缓存(避免每帧 FindObjectOfType)─────────────────────────────────
|
||||
private PlayerStats _stats;
|
||||
private FormController _formCtrl;
|
||||
private PlayerController _playerCtrl;
|
||||
private double _lastCacheTime = -10;
|
||||
|
||||
// ── 能力分组定义(与 AbilityTypeDrawer 保持一致)────────────────────
|
||||
private static readonly (string label, AbilityType[] flags)[] AbilityGroups =
|
||||
{
|
||||
("移动能力", new[]
|
||||
{
|
||||
AbilityType.WallCling, AbilityType.WallJump,
|
||||
AbilityType.Dash, AbilityType.Dash,
|
||||
AbilityType.DoubleJump, AbilityType.SuperJump,
|
||||
AbilityType.Swim, AbilityType.Dive,
|
||||
}),
|
||||
("法术能力", new[] { AbilityType.Spell1, AbilityType.Spell2, AbilityType.Spell3 }),
|
||||
("灵魄形态", new[] { AbilityType.SpiritForm, AbilityType.SpiritDash }),
|
||||
("战斗能力", new[] { AbilityType.Parry, AbilityType.ChargeAttack, AbilityType.DownSlash }),
|
||||
("互动能力", new[] { AbilityType.Interact, AbilityType.FastTravel }),
|
||||
("能力强化", new[] { AbilityType.InvincibleDash }),
|
||||
};
|
||||
|
||||
private static readonly string[] AbilityFlagNames =
|
||||
{
|
||||
"贴墙悬挂", "墙跳", "地面冲刺", "空中冲刺", "二段跳", "超级跳", "游泳", "下劈",
|
||||
"法术槽 1", "法术槽 2", "法术槽 3",
|
||||
"灵魄形态", "灵魄冲刺",
|
||||
"弹反", "蓄力攻击", "下斩",
|
||||
"互动", "快速旅行",
|
||||
"无敌冲刺",
|
||||
};
|
||||
|
||||
// ── 样式(懒初始化)──────────────────────────────────────────────────
|
||||
private GUIStyle _headerStyle;
|
||||
private GUIStyle _boxStyle;
|
||||
|
||||
// ── 滚动 ──────────────────────────────────────────────────────────────
|
||||
private Vector2 _scroll;
|
||||
|
||||
// ── EditorWindow 回调 ─────────────────────────────────────────────────
|
||||
|
||||
private void OnEnable() => EditorApplication.playModeStateChanged += OnPlayModeChanged;
|
||||
private void OnDisable() => EditorApplication.playModeStateChanged -= OnPlayModeChanged;
|
||||
|
||||
private void OnPlayModeChanged(PlayModeStateChange state)
|
||||
{
|
||||
_stats = null;
|
||||
_formCtrl = null;
|
||||
_playerCtrl = null;
|
||||
Repaint();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EnsureStyles();
|
||||
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
EditorGUILayout.HelpBox("GM 工具仅在 Play Mode 下有效。\n请先运行游戏。", MessageType.Info);
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshCache();
|
||||
|
||||
if (_stats == null)
|
||||
{
|
||||
EditorGUILayout.HelpBox("场景中未找到 PlayerStats 组件。\n请确认玩家已生成。", MessageType.Warning);
|
||||
if (GUILayout.Button("重新扫描")) _lastCacheTime = -10;
|
||||
return;
|
||||
}
|
||||
|
||||
_scroll = EditorGUILayout.BeginScrollView(_scroll);
|
||||
|
||||
DrawResourceSection();
|
||||
DrawJumpSection();
|
||||
DrawFormSection();
|
||||
DrawAbilitySection();
|
||||
DrawDebugSection();
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
// ── 分区:资源 ────────────────────────────────────────────────────────
|
||||
|
||||
private void DrawResourceSection()
|
||||
{
|
||||
_foldResources = DrawFoldout(_foldResources, "资源快速填充");
|
||||
if (!_foldResources) return;
|
||||
|
||||
EditorGUILayout.BeginVertical(_boxStyle);
|
||||
|
||||
// 灵铢
|
||||
EditorGUILayout.LabelField("灵铢", EditorStyles.boldLabel);
|
||||
EditorGUILayout.LabelField($"当前:{_stats.CurrentLingZhu}", EditorStyles.miniLabel);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
_lingZhuAmount = EditorGUILayout.IntField(_lingZhuAmount, GUILayout.Width(80));
|
||||
if (GUILayout.Button("增加")) _stats.AddLingZhu(_lingZhuAmount);
|
||||
if (GUILayout.Button("设为 9999")) _stats.AddLingZhu(Mathf.Max(0, 9999 - _stats.CurrentLingZhu));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(6);
|
||||
|
||||
// 灵力(SoulPower)
|
||||
EditorGUILayout.LabelField("灵力(技能用)", EditorStyles.boldLabel);
|
||||
EditorGUILayout.LabelField($"当前:{_stats.CurrentSoulPower} / {_stats.MaxSoulPower}", EditorStyles.miniLabel);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
_soulPowerAmount = EditorGUILayout.IntField(_soulPowerAmount, GUILayout.Width(80));
|
||||
if (GUILayout.Button("增加")) _stats.AddSoulPower(_soulPowerAmount);
|
||||
if (GUILayout.Button("填满")) _stats.AddSoulPower(_stats.MaxSoulPower);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(6);
|
||||
|
||||
// 魄元(SpiritPower)
|
||||
EditorGUILayout.LabelField("魄元(魄技能用)", EditorStyles.boldLabel);
|
||||
EditorGUILayout.LabelField($"当前:{_stats.CurrentSpiritPower} / {_stats.MaxSpiritPower}", EditorStyles.miniLabel);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
_spiritAmount = EditorGUILayout.IntField(_spiritAmount, GUILayout.Width(80));
|
||||
if (GUILayout.Button("增加")) _stats.AddSpiritPower(_spiritAmount);
|
||||
if (GUILayout.Button("填满")) _stats.AddSpiritPower(_stats.MaxSpiritPower);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
if (GUILayout.Button("▶ 全部资源填满"))
|
||||
{
|
||||
_stats.AddLingZhu(Mathf.Max(0, 9999 - _stats.CurrentLingZhu));
|
||||
_stats.AddSoulPower(_stats.MaxSoulPower);
|
||||
_stats.AddSpiritPower(_stats.MaxSpiritPower);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
// ── 分区:跳跃快捷 ────────────────────────────────────────────────────
|
||||
|
||||
private void DrawJumpSection()
|
||||
{
|
||||
_foldJump = DrawFoldout(_foldJump, "跳跃能力快捷");
|
||||
if (!_foldJump) return;
|
||||
|
||||
EditorGUILayout.BeginVertical(_boxStyle);
|
||||
|
||||
// ── 当前状态 ──
|
||||
bool hasDoubleJump = _stats.HasAbility(AbilityType.DoubleJump);
|
||||
bool hasDash = _stats.HasAbility(AbilityType.Dash);
|
||||
bool hasWallJump = _stats.HasAbility(AbilityType.WallJump);
|
||||
bool hasWallCling = _stats.HasAbility(AbilityType.WallCling);
|
||||
|
||||
int airJumpsLeft = _playerCtrl != null ? _playerCtrl.AirJumpsLeft : -1;
|
||||
int maxAirJumps = _playerCtrl != null && _playerCtrl.MovConfig != null
|
||||
? _playerCtrl.MovConfig.MaxAirJumps : -1;
|
||||
string airStr = airJumpsLeft >= 0
|
||||
? $"{airJumpsLeft} / {maxAirJumps}"
|
||||
: "N/A";
|
||||
|
||||
EditorGUILayout.LabelField(
|
||||
$"二段跳:{(hasDoubleJump ? "✔ 已解锁" : "✘ 未解锁")} | 腾空剩余:{airStr}",
|
||||
EditorStyles.miniLabel);
|
||||
|
||||
// ── MaxAirJumps 控制 ──
|
||||
if (_playerCtrl != null && _playerCtrl.MovConfig != null)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("最大空中跳跃次数", GUILayout.Width(120));
|
||||
int newMax = EditorGUILayout.IntSlider(
|
||||
_playerCtrl.MovConfig.MaxAirJumps, 1, 5, GUILayout.ExpandWidth(true));
|
||||
if (newMax != _playerCtrl.MovConfig.MaxAirJumps)
|
||||
{
|
||||
_playerCtrl.MovConfig.MaxAirJumps = newMax;
|
||||
EditorUtility.SetDirty(_playerCtrl.MovConfig);
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.LabelField(
|
||||
$" 1=二段跳 2=三段跳 3=四段跳…(需先解锁 DoubleJump 能力)",
|
||||
EditorStyles.miniLabel);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// ── 跳跃系列快捷按钮(每行 2 个)──
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
DrawToggleAbilityBtn(hasDoubleJump, AbilityType.DoubleJump, "二段跳");
|
||||
DrawToggleAbilityBtn(hasDash, AbilityType.Dash, "冲刺(地面+空中)");
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
DrawToggleAbilityBtn(hasWallJump, AbilityType.WallJump, "墙跳");
|
||||
DrawToggleAbilityBtn(hasWallCling, AbilityType.WallCling, "贴墙悬挂");
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
DrawToggleAbilityBtn(_stats.HasAbility(AbilityType.SuperJump), AbilityType.SuperJump, "超级跳");
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// ── 批量快捷 ──
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("解锁全部移动能力"))
|
||||
{
|
||||
foreach (var f in new[]
|
||||
{
|
||||
AbilityType.Dash,
|
||||
AbilityType.DoubleJump, AbilityType.SuperJump,
|
||||
AbilityType.WallCling, AbilityType.WallJump,
|
||||
})
|
||||
_stats.UnlockAbility(f);
|
||||
}
|
||||
if (GUILayout.Button("锁定全部移动能力"))
|
||||
{
|
||||
foreach (var f in new[]
|
||||
{
|
||||
AbilityType.Dash,
|
||||
AbilityType.DoubleJump, AbilityType.SuperJump,
|
||||
AbilityType.WallCling, AbilityType.WallJump,
|
||||
})
|
||||
_stats.LockAbility(f);
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.HelpBox(
|
||||
"MaxAirJumps 修改立即写入 ScriptableObject(持久化)。\n" +
|
||||
"AirJumpsLeft 在角色下次落地时按新值重置。",
|
||||
MessageType.None);
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制一个「已解锁 → 锁定 / 未解锁 → 解锁」的切换按钮。
|
||||
/// </summary>
|
||||
private void DrawToggleAbilityBtn(bool hasIt, AbilityType flag, string label)
|
||||
{
|
||||
GUI.backgroundColor = hasIt ? new Color(0.6f, 1.0f, 0.6f) : new Color(1.0f, 0.85f, 0.6f);
|
||||
string btnText = hasIt ? $"✔ {label}" : $"✘ {label}";
|
||||
if (GUILayout.Button(btnText))
|
||||
{
|
||||
if (hasIt) _stats.LockAbility(flag);
|
||||
else _stats.UnlockAbility(flag);
|
||||
}
|
||||
GUI.backgroundColor = Color.white;
|
||||
}
|
||||
|
||||
// ── 分区:形态 ────────────────────────────────────────────────────────
|
||||
|
||||
private void DrawFormSection()
|
||||
{
|
||||
_foldForms = DrawFoldout(_foldForms, "形态快速切换");
|
||||
if (!_foldForms) return;
|
||||
|
||||
EditorGUILayout.BeginVertical(_boxStyle);
|
||||
|
||||
if (_formCtrl == null)
|
||||
{
|
||||
EditorGUILayout.HelpBox("场景中未找到 FormController 组件。", MessageType.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
string cur = _formCtrl.CurrentForm != null ? _formCtrl.CurrentForm.displayName : "未知";
|
||||
EditorGUILayout.LabelField($"当前形态:{cur}", EditorStyles.boldLabel);
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("天魂")) SwitchForm(FormType.TianHun);
|
||||
if (GUILayout.Button("地魂")) SwitchForm(FormType.DiHun);
|
||||
if (GUILayout.Button("命魂")) SwitchForm(FormType.MingHun);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.HelpBox("提示:切换形态前请确保已解锁灵魄形态(SpiritForm)能力。", MessageType.None);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void SwitchForm(FormType type)
|
||||
{
|
||||
// 确保 SpiritForm 能力已解锁(否则 FSM 可能拒绝形态切换)
|
||||
_stats.UnlockAbility(AbilityType.SpiritForm);
|
||||
_formCtrl.SwitchForm(type);
|
||||
}
|
||||
|
||||
// ── 分区:能力 ────────────────────────────────────────────────────────
|
||||
|
||||
private void DrawAbilitySection()
|
||||
{
|
||||
_foldAbilities = DrawFoldout(_foldAbilities, "能力解锁 / 锁定");
|
||||
if (!_foldAbilities) return;
|
||||
|
||||
EditorGUILayout.BeginVertical(_boxStyle);
|
||||
|
||||
// 快捷全选/全清
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("全部解锁"))
|
||||
{
|
||||
foreach (var (_, flags) in AbilityGroups)
|
||||
foreach (var f in flags)
|
||||
_stats.UnlockAbility(f);
|
||||
}
|
||||
if (GUILayout.Button("全部锁定"))
|
||||
{
|
||||
foreach (var (_, flags) in AbilityGroups)
|
||||
foreach (var f in flags)
|
||||
_stats.LockAbility(f);
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(6);
|
||||
|
||||
// 各分组
|
||||
foreach (var (groupLabel, flags) in AbilityGroups)
|
||||
{
|
||||
EditorGUILayout.LabelField(groupLabel, EditorStyles.boldLabel);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
bool allOn = true;
|
||||
foreach (var f in flags) if (!_stats.HasAbility(f)) { allOn = false; break; }
|
||||
if (GUILayout.Button(allOn ? "全锁" : "全解", GUILayout.Width(42)))
|
||||
{
|
||||
foreach (var f in flags)
|
||||
{
|
||||
if (allOn) _stats.LockAbility(f);
|
||||
else _stats.UnlockAbility(f);
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
int col = 0;
|
||||
foreach (var flag in flags)
|
||||
{
|
||||
bool has = _stats.HasAbility(flag);
|
||||
bool toggled = GUILayout.Toggle(has, FlagDisplayName(flag), GUILayout.Width(128));
|
||||
if (toggled != has)
|
||||
{
|
||||
if (toggled) _stats.UnlockAbility(flag);
|
||||
else _stats.LockAbility(flag);
|
||||
}
|
||||
col++;
|
||||
if (col == 2) { col = 0; EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); }
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.Space(4);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
// ── 分区:调试辅助 ────────────────────────────────────────────────────
|
||||
|
||||
private void DrawDebugSection()
|
||||
{
|
||||
_foldDebug = DrawFoldout(_foldDebug, "调试辅助");
|
||||
if (!_foldDebug) return;
|
||||
|
||||
EditorGUILayout.BeginVertical(_boxStyle);
|
||||
|
||||
// HP
|
||||
EditorGUILayout.LabelField($"HP:{_stats.CurrentHP} / {_stats.MaxHP}", EditorStyles.miniLabel);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("满血")) _stats.FullHeal();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// 弹簧充能
|
||||
EditorGUILayout.LabelField($"弹力充能:{_stats.CurrentSpringCharges} / {_stats.MaxSpringCharges}", EditorStyles.miniLabel);
|
||||
if (GUILayout.Button("恢复全部弹力充能")) _stats.RestoreSpringCharges();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// 无敌模式(God Mode)
|
||||
bool godNow = _stats.IsInvincible; // 仅作参考,GodMode 内部字段
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("开启无敌模式")) _stats.SetGodMode(true);
|
||||
if (GUILayout.Button("关闭无敌模式")) _stats.SetGodMode(false);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// 一键全开(开发快速进入测试状态)
|
||||
GUI.backgroundColor = new Color(0.7f, 1.0f, 0.7f);
|
||||
if (GUILayout.Button("▶ 一键满状态(资源 + 全能力 + 满血)", GUILayout.Height(32)))
|
||||
{
|
||||
_stats.FullHeal();
|
||||
_stats.RestoreSpringCharges();
|
||||
_stats.AddLingZhu(Mathf.Max(0, 9999 - _stats.CurrentLingZhu));
|
||||
_stats.AddSoulPower(_stats.MaxSoulPower);
|
||||
_stats.AddSpiritPower(_stats.MaxSpiritPower);
|
||||
foreach (var (_, flags) in AbilityGroups)
|
||||
foreach (var f in flags)
|
||||
_stats.UnlockAbility(f);
|
||||
}
|
||||
GUI.backgroundColor = Color.white;
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
// ── 工具方法 ──────────────────────────────────────────────────────────
|
||||
|
||||
private void RefreshCache()
|
||||
{
|
||||
if (EditorApplication.timeSinceStartup - _lastCacheTime < 2.0) return;
|
||||
_lastCacheTime = EditorApplication.timeSinceStartup;
|
||||
_stats = FindObjectOfType<PlayerStats>();
|
||||
_formCtrl = FindObjectOfType<FormController>();
|
||||
_playerCtrl = FindObjectOfType<PlayerController>();
|
||||
}
|
||||
|
||||
private bool DrawFoldout(bool state, string label)
|
||||
{
|
||||
EditorGUILayout.Space(4);
|
||||
bool next = EditorGUILayout.Foldout(state, label, true, _headerStyle);
|
||||
return next;
|
||||
}
|
||||
|
||||
private static string FlagDisplayName(AbilityType flag) => flag switch
|
||||
{
|
||||
AbilityType.WallCling => "贴墙悬挂",
|
||||
AbilityType.WallJump => "墙跳",
|
||||
AbilityType.Dash => "冲刺",
|
||||
AbilityType.DoubleJump => "二段跳",
|
||||
AbilityType.SuperJump => "超级跳",
|
||||
AbilityType.Swim => "游泳",
|
||||
AbilityType.Dive => "下劈",
|
||||
AbilityType.Spell1 => "法术槽 1",
|
||||
AbilityType.Spell2 => "法术槽 2",
|
||||
AbilityType.Spell3 => "法术槽 3",
|
||||
AbilityType.SpiritForm => "灵魄形态",
|
||||
AbilityType.SpiritDash => "灵魄冲刺",
|
||||
AbilityType.Parry => "弹反",
|
||||
AbilityType.ChargeAttack => "蓄力攻击",
|
||||
AbilityType.DownSlash => "下斩",
|
||||
AbilityType.Interact => "互动",
|
||||
AbilityType.FastTravel => "快速旅行",
|
||||
AbilityType.InvincibleDash => "无敌冲刺",
|
||||
_ => flag.ToString(),
|
||||
};
|
||||
|
||||
private void EnsureStyles()
|
||||
{
|
||||
if (_headerStyle != null) return;
|
||||
_headerStyle = new GUIStyle(EditorStyles.foldout)
|
||||
{
|
||||
fontStyle = FontStyle.Bold,
|
||||
fontSize = 12,
|
||||
};
|
||||
_boxStyle = new GUIStyle(EditorStyles.helpBox)
|
||||
{
|
||||
padding = new RectOffset(8, 8, 6, 6),
|
||||
};
|
||||
}
|
||||
|
||||
// ── 自动刷新(每秒重绘以显示最新数值)──────────────────────────────
|
||||
private void OnInspectorUpdate() => Repaint();
|
||||
}
|
||||
}
|
||||
11
Assets/_Game/Scripts/Editor/Tools/GMToolWindow.cs.meta
Normal file
11
Assets/_Game/Scripts/Editor/Tools/GMToolWindow.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fe104ad18cf3df743a6edd48b173115f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -15,7 +15,7 @@ namespace BaseGames.Editor
|
||||
// 场景
|
||||
// ──────────────────────────────────────────────
|
||||
|
||||
[MenuItem("BaseGames/Tools/Missing Scripts/Clear In Scene")]
|
||||
[MenuItem("BaseGames/Tools/Maintenance/Missing Scripts/Clear In Scene")]
|
||||
public static void ClearMissingScriptsInScene()
|
||||
{
|
||||
int totalRemoved = 0;
|
||||
@@ -39,7 +39,7 @@ namespace BaseGames.Editor
|
||||
Debug.Log($"[MissingScriptCleaner] 场景完成。共移除 {totalRemoved} 个丢失脚本,影响 {affected.Count} 个 GameObject。");
|
||||
}
|
||||
|
||||
[MenuItem("BaseGames/Tools/Missing Scripts/Find In Scene")]
|
||||
[MenuItem("BaseGames/Tools/Maintenance/Missing Scripts/Find In Scene")]
|
||||
public static void FindMissingScriptsInScene()
|
||||
{
|
||||
int totalFound = 0;
|
||||
@@ -67,7 +67,7 @@ namespace BaseGames.Editor
|
||||
// Prefab 资产
|
||||
// ──────────────────────────────────────────────
|
||||
|
||||
[MenuItem("BaseGames/Tools/Missing Scripts/Clear In All Prefabs")]
|
||||
[MenuItem("BaseGames/Tools/Maintenance/Missing Scripts/Clear In All Prefabs")]
|
||||
public static void ClearMissingScriptsInPrefabs()
|
||||
{
|
||||
int totalRemoved = 0;
|
||||
@@ -107,7 +107,7 @@ namespace BaseGames.Editor
|
||||
Debug.Log($"[MissingScriptCleaner] Prefab 完成。共移除 {totalRemoved} 个丢失脚本,影响 {affectedPrefabs} 个 Prefab。");
|
||||
}
|
||||
|
||||
[MenuItem("BaseGames/Tools/Missing Scripts/Find In All Prefabs")]
|
||||
[MenuItem("BaseGames/Tools/Maintenance/Missing Scripts/Find In All Prefabs")]
|
||||
public static void FindMissingScriptsInPrefabs()
|
||||
{
|
||||
int totalFound = 0;
|
||||
|
||||
@@ -54,14 +54,14 @@ namespace BaseGames.Editor
|
||||
};
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────
|
||||
[MenuItem("BaseGames/Tools/Physics2D Layer Matrix/Check", priority = 210)]
|
||||
[MenuItem("BaseGames/Tools/Maintenance/Physics2D Layer Matrix/Check", priority = 210)]
|
||||
public static void CheckAndPrintReport()
|
||||
{
|
||||
var results = Check();
|
||||
PrintToConsole(results);
|
||||
}
|
||||
|
||||
[MenuItem("BaseGames/Tools/Physics2D Layer Matrix/Auto Fix", priority = 211)]
|
||||
[MenuItem("BaseGames/Tools/Maintenance/Physics2D Layer Matrix/Auto Fix", priority = 211)]
|
||||
public static void FixAndReport()
|
||||
{
|
||||
var results = Check();
|
||||
|
||||
393
Assets/_Game/Scripts/Editor/Tools/SOManagerWindow.cs
Normal file
393
Assets/_Game/Scripts/Editor/Tools/SOManagerWindow.cs
Normal file
@@ -0,0 +1,393 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace BaseGames.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// SO 资产总管理窗口 —— 浏览、搜索并在 Project 窗口定位项目中所有 ScriptableObject 资产。
|
||||
///
|
||||
/// 布局:顶部搜索栏 | 左侧分类列表 | 右侧资产列表(名称 / 类型 / 路径)
|
||||
/// 功能:单击资产行 → Project 窗口 Ping 并选中;双击 → 同上并聚焦 Project 窗口。
|
||||
/// 菜单:BaseGames / Tools / SO Manager (Priority 2)
|
||||
/// </summary>
|
||||
public class SOManagerWindow : EditorWindow
|
||||
{
|
||||
private const string DataRoot = "Assets/_Game/Data";
|
||||
private const string UssPath = "Assets/_Game/Scripts/Editor/UIToolkit/Editor.uss";
|
||||
|
||||
// ── 数据模型 ──────────────────────────────────────────────────────────
|
||||
|
||||
private sealed class CategoryEntry
|
||||
{
|
||||
public string Label;
|
||||
public string Folder; // null = 全部
|
||||
public int Count;
|
||||
}
|
||||
|
||||
private sealed class AssetEntry
|
||||
{
|
||||
public string Name;
|
||||
public string TypeName;
|
||||
public string AssetPath;
|
||||
public ScriptableObject Asset;
|
||||
}
|
||||
|
||||
private readonly List<CategoryEntry> _categories = new();
|
||||
private readonly List<AssetEntry> _allAssets = new();
|
||||
private readonly List<AssetEntry> _filtered = new();
|
||||
|
||||
private int _selectedCatIdx = 0;
|
||||
private string _search = "";
|
||||
|
||||
// ── UI 引用 ───────────────────────────────────────────────────────────
|
||||
|
||||
private ListView _catList;
|
||||
private ListView _assetList;
|
||||
private TextField _searchField;
|
||||
private Label _statusLabel;
|
||||
|
||||
// ── 菜单入口 ──────────────────────────────────────────────────────────
|
||||
|
||||
[MenuItem("BaseGames/Tools/SO Manager", priority = 2)]
|
||||
public static void Open()
|
||||
{
|
||||
var wnd = GetWindow<SOManagerWindow>();
|
||||
wnd.titleContent = new GUIContent("SO Manager",
|
||||
EditorGUIUtility.IconContent("d_ScriptableObject Icon").image);
|
||||
wnd.minSize = new Vector2(680, 420);
|
||||
}
|
||||
|
||||
// ── 生命周期 ──────────────────────────────────────────────────────────
|
||||
|
||||
public void CreateGUI()
|
||||
{
|
||||
var uss = AssetDatabase.LoadAssetAtPath<StyleSheet>(UssPath);
|
||||
if (uss != null) rootVisualElement.styleSheets.Add(uss);
|
||||
|
||||
BuildUI();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void OnFocus() => Refresh();
|
||||
|
||||
// ── UI 构建 ───────────────────────────────────────────────────────────
|
||||
|
||||
private void BuildUI()
|
||||
{
|
||||
rootVisualElement.style.flexDirection = FlexDirection.Column;
|
||||
|
||||
// ─ 顶部工具栏 ──────────────────────────────────────────────────────
|
||||
var toolbar = new VisualElement();
|
||||
toolbar.style.flexDirection = FlexDirection.Row;
|
||||
toolbar.style.paddingLeft = 8;
|
||||
toolbar.style.paddingRight = 8;
|
||||
toolbar.style.paddingTop = 5;
|
||||
toolbar.style.paddingBottom = 5;
|
||||
toolbar.style.borderBottomWidth = 1;
|
||||
toolbar.style.borderBottomColor = new Color(0.15f, 0.15f, 0.15f);
|
||||
toolbar.style.backgroundColor = new Color(0.22f, 0.22f, 0.22f, 0.6f);
|
||||
|
||||
var searchLbl = new Label("搜索:");
|
||||
searchLbl.style.unityTextAlign = TextAnchor.MiddleLeft;
|
||||
searchLbl.style.marginRight = 4;
|
||||
|
||||
_searchField = new TextField();
|
||||
_searchField.style.flexGrow = 1;
|
||||
_searchField.RegisterValueChangedCallback(e =>
|
||||
{
|
||||
_search = e.newValue;
|
||||
ApplyFilter();
|
||||
});
|
||||
|
||||
var refreshBtn = new Button(Refresh) { text = "↻ 刷新" };
|
||||
refreshBtn.style.marginLeft = 8;
|
||||
refreshBtn.style.width = 58;
|
||||
|
||||
toolbar.Add(searchLbl);
|
||||
toolbar.Add(_searchField);
|
||||
toolbar.Add(refreshBtn);
|
||||
rootVisualElement.Add(toolbar);
|
||||
|
||||
// ─ 主体:两栏分割 ──────────────────────────────────────────────────
|
||||
var split = new TwoPaneSplitView(0, 164, TwoPaneSplitViewOrientation.Horizontal);
|
||||
split.style.flexGrow = 1;
|
||||
|
||||
// 左栏:分类列表 ────────────────────────────────────────────────────
|
||||
var leftPane = new VisualElement();
|
||||
leftPane.style.flexDirection = FlexDirection.Column;
|
||||
leftPane.style.minWidth = 100;
|
||||
|
||||
var catHeader = new Label("分类");
|
||||
catHeader.style.paddingLeft = 8;
|
||||
catHeader.style.paddingTop = 5;
|
||||
catHeader.style.paddingBottom = 5;
|
||||
catHeader.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
catHeader.style.borderBottomWidth = 1;
|
||||
catHeader.style.borderBottomColor = new Color(0.22f, 0.22f, 0.22f);
|
||||
catHeader.style.backgroundColor = new Color(0.22f, 0.22f, 0.22f, 0.4f);
|
||||
leftPane.Add(catHeader);
|
||||
|
||||
_catList = new ListView
|
||||
{
|
||||
makeItem = MakeCatItem,
|
||||
bindItem = BindCatItem,
|
||||
selectionType = SelectionType.Single,
|
||||
fixedItemHeight = 26,
|
||||
};
|
||||
_catList.style.flexGrow = 1;
|
||||
_catList.selectionChanged += _ =>
|
||||
{
|
||||
if (_catList.selectedIndex >= 0)
|
||||
{
|
||||
_selectedCatIdx = _catList.selectedIndex;
|
||||
ApplyFilter();
|
||||
}
|
||||
};
|
||||
leftPane.Add(_catList);
|
||||
|
||||
// 右栏:资产列表 ────────────────────────────────────────────────────
|
||||
var rightPane = new VisualElement();
|
||||
rightPane.style.flexDirection = FlexDirection.Column;
|
||||
|
||||
// 列标题行
|
||||
var colHeader = new VisualElement();
|
||||
colHeader.style.flexDirection = FlexDirection.Row;
|
||||
colHeader.style.paddingLeft = 8;
|
||||
colHeader.style.paddingRight = 8;
|
||||
colHeader.style.paddingTop = 4;
|
||||
colHeader.style.paddingBottom = 4;
|
||||
colHeader.style.borderBottomWidth = 1;
|
||||
colHeader.style.borderBottomColor = new Color(0.22f, 0.22f, 0.22f);
|
||||
colHeader.style.backgroundColor = new Color(0.22f, 0.22f, 0.22f, 0.4f);
|
||||
colHeader.Add(MakeHeaderLabel("资产名", true, 0));
|
||||
colHeader.Add(MakeHeaderLabel("类型", false, 170));
|
||||
colHeader.Add(MakeHeaderLabel("路径", true, 0));
|
||||
rightPane.Add(colHeader);
|
||||
|
||||
_assetList = new ListView
|
||||
{
|
||||
makeItem = MakeAssetRow,
|
||||
bindItem = BindAssetRow,
|
||||
selectionType = SelectionType.Single,
|
||||
fixedItemHeight = 22,
|
||||
};
|
||||
_assetList.style.flexGrow = 1;
|
||||
_assetList.selectionChanged += _ => OnAssetPicked();
|
||||
_assetList.itemsChosen += _ => FocusProjectWindow();
|
||||
rightPane.Add(_assetList);
|
||||
|
||||
// 状态栏
|
||||
_statusLabel = new Label("—");
|
||||
_statusLabel.style.paddingLeft = 8;
|
||||
_statusLabel.style.paddingTop = 3;
|
||||
_statusLabel.style.paddingBottom = 3;
|
||||
_statusLabel.style.borderTopWidth = 1;
|
||||
_statusLabel.style.borderTopColor = new Color(0.15f, 0.15f, 0.15f);
|
||||
_statusLabel.style.color = new Color(0.58f, 0.58f, 0.58f);
|
||||
_statusLabel.style.fontSize = 11;
|
||||
rightPane.Add(_statusLabel);
|
||||
|
||||
split.Add(leftPane);
|
||||
split.Add(rightPane);
|
||||
rootVisualElement.Add(split);
|
||||
}
|
||||
|
||||
private static Label MakeHeaderLabel(string text, bool grow, int fixedWidth)
|
||||
{
|
||||
var lbl = new Label(text);
|
||||
lbl.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
lbl.style.overflow = Overflow.Hidden;
|
||||
if (grow) lbl.style.flexGrow = 1;
|
||||
if (fixedWidth > 0) lbl.style.width = fixedWidth;
|
||||
return lbl;
|
||||
}
|
||||
|
||||
// ── 分类列表项 ────────────────────────────────────────────────────────
|
||||
|
||||
private static VisualElement MakeCatItem()
|
||||
{
|
||||
var lbl = new Label();
|
||||
lbl.style.paddingLeft = 10;
|
||||
lbl.style.paddingRight = 6;
|
||||
lbl.style.overflow = Overflow.Hidden;
|
||||
lbl.style.unityTextAlign = TextAnchor.MiddleLeft;
|
||||
return lbl;
|
||||
}
|
||||
|
||||
private void BindCatItem(VisualElement el, int i)
|
||||
{
|
||||
if (i >= _categories.Count) return;
|
||||
var cat = _categories[i];
|
||||
((Label)el).text = $"{cat.Label} ({cat.Count})";
|
||||
}
|
||||
|
||||
// ── 资产列表项 ────────────────────────────────────────────────────────
|
||||
|
||||
private static VisualElement MakeAssetRow()
|
||||
{
|
||||
var row = new VisualElement();
|
||||
row.style.flexDirection = FlexDirection.Row;
|
||||
row.style.alignItems = Align.Center;
|
||||
row.style.paddingLeft = 8;
|
||||
row.style.paddingRight = 8;
|
||||
|
||||
var nameEl = new Label { name = "n" };
|
||||
nameEl.style.flexGrow = 1;
|
||||
nameEl.style.overflow = Overflow.Hidden;
|
||||
nameEl.style.textOverflow = TextOverflow.Ellipsis;
|
||||
|
||||
var typeEl = new Label { name = "t" };
|
||||
typeEl.style.width = 170;
|
||||
typeEl.style.overflow = Overflow.Hidden;
|
||||
typeEl.style.textOverflow = TextOverflow.Ellipsis;
|
||||
typeEl.style.color = new Color(0.52f, 0.80f, 1.00f);
|
||||
typeEl.style.fontSize = 11;
|
||||
|
||||
var pathEl = new Label { name = "p" };
|
||||
pathEl.style.flexGrow = 1;
|
||||
pathEl.style.overflow = Overflow.Hidden;
|
||||
pathEl.style.textOverflow = TextOverflow.Ellipsis;
|
||||
pathEl.style.color = new Color(0.48f, 0.48f, 0.48f);
|
||||
pathEl.style.fontSize = 10;
|
||||
|
||||
row.Add(nameEl);
|
||||
row.Add(typeEl);
|
||||
row.Add(pathEl);
|
||||
return row;
|
||||
}
|
||||
|
||||
private void BindAssetRow(VisualElement el, int i)
|
||||
{
|
||||
if (i >= _filtered.Count) return;
|
||||
var e = _filtered[i];
|
||||
el.Q<Label>("n").text = e.Name;
|
||||
el.Q<Label>("t").text = e.TypeName;
|
||||
// 显示相对于 DataRoot 的路径,去掉文件名本身只保留目录
|
||||
string rel = e.AssetPath.StartsWith(DataRoot + "/")
|
||||
? e.AssetPath.Substring(DataRoot.Length + 1)
|
||||
: e.AssetPath;
|
||||
// 去掉最后的文件名,只显示目录部分
|
||||
string dir = Path.GetDirectoryName(rel)?.Replace('\\', '/') ?? "";
|
||||
el.Q<Label>("p").text = string.IsNullOrEmpty(dir) ? "/" : dir;
|
||||
}
|
||||
|
||||
// ── 资产选中 ──────────────────────────────────────────────────────────
|
||||
|
||||
private void OnAssetPicked()
|
||||
{
|
||||
int idx = _assetList.selectedIndex;
|
||||
if (idx < 0 || idx >= _filtered.Count) return;
|
||||
var asset = _filtered[idx].Asset;
|
||||
if (asset == null) return;
|
||||
EditorGUIUtility.PingObject(asset);
|
||||
Selection.activeObject = asset;
|
||||
}
|
||||
|
||||
private static void FocusProjectWindow()
|
||||
{
|
||||
EditorApplication.ExecuteMenuItem("Window/General/Project");
|
||||
}
|
||||
|
||||
// ── 数据逻辑 ──────────────────────────────────────────────────────────
|
||||
|
||||
private void Refresh()
|
||||
{
|
||||
_allAssets.Clear();
|
||||
|
||||
// 扫描 DataRoot 下所有 ScriptableObject 资产
|
||||
var guids = AssetDatabase.FindAssets("t:ScriptableObject", new[] { DataRoot });
|
||||
foreach (var guid in guids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var asset = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
|
||||
if (asset == null) continue;
|
||||
_allAssets.Add(new AssetEntry
|
||||
{
|
||||
Name = asset.name,
|
||||
TypeName = asset.GetType().Name,
|
||||
AssetPath = path,
|
||||
Asset = asset,
|
||||
});
|
||||
}
|
||||
|
||||
// 构建分类:首项为"全部",其余为 DataRoot 的直接子目录
|
||||
_categories.Clear();
|
||||
_categories.Add(new CategoryEntry
|
||||
{
|
||||
Label = "全部",
|
||||
Folder = null,
|
||||
Count = _allAssets.Count,
|
||||
});
|
||||
|
||||
if (AssetDatabase.IsValidFolder(DataRoot))
|
||||
{
|
||||
foreach (var sub in AssetDatabase.GetSubFolders(DataRoot).OrderBy(f => f))
|
||||
{
|
||||
string folderName = Path.GetFileName(sub);
|
||||
int count = _allAssets.Count(a =>
|
||||
a.AssetPath.StartsWith(sub + "/", StringComparison.Ordinal));
|
||||
if (count == 0) continue;
|
||||
_categories.Add(new CategoryEntry
|
||||
{
|
||||
Label = folderName,
|
||||
Folder = sub,
|
||||
Count = count,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_catList.itemsSource = _categories;
|
||||
_catList.Rebuild();
|
||||
|
||||
int clampedIdx = Mathf.Clamp(_selectedCatIdx, 0, _categories.Count - 1);
|
||||
_catList.SetSelection(clampedIdx);
|
||||
_selectedCatIdx = clampedIdx;
|
||||
ApplyFilter();
|
||||
}
|
||||
|
||||
private void ApplyFilter()
|
||||
{
|
||||
_filtered.Clear();
|
||||
|
||||
string folder = (_selectedCatIdx >= 0 && _selectedCatIdx < _categories.Count)
|
||||
? _categories[_selectedCatIdx].Folder
|
||||
: null;
|
||||
|
||||
IEnumerable<AssetEntry> source = folder == null
|
||||
? _allAssets
|
||||
: _allAssets.Where(a => a.AssetPath.StartsWith(folder + "/", StringComparison.Ordinal));
|
||||
|
||||
foreach (var entry in source)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_search)
|
||||
|| entry.Name.IndexOf(_search, StringComparison.OrdinalIgnoreCase) >= 0
|
||||
|| entry.TypeName.IndexOf(_search, StringComparison.OrdinalIgnoreCase) >= 0)
|
||||
{
|
||||
_filtered.Add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// 同分类内按类型分组,再按名称排序
|
||||
_filtered.Sort((a, b) =>
|
||||
{
|
||||
int cmp = string.Compare(a.TypeName, b.TypeName, StringComparison.OrdinalIgnoreCase);
|
||||
return cmp != 0 ? cmp : string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase);
|
||||
});
|
||||
|
||||
_assetList.itemsSource = _filtered;
|
||||
_assetList.Rebuild();
|
||||
|
||||
int total = folder == null
|
||||
? _allAssets.Count
|
||||
: _allAssets.Count(a => a.AssetPath.StartsWith(folder + "/", StringComparison.Ordinal));
|
||||
_statusLabel.text = string.IsNullOrEmpty(_search)
|
||||
? $"共 {_filtered.Count} 个资产"
|
||||
: $"筛选 {_filtered.Count} / {total} 个资产(搜索:{_search})";
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/_Game/Scripts/Editor/Tools/SOManagerWindow.cs.meta
Normal file
11
Assets/_Game/Scripts/Editor/Tools/SOManagerWindow.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7dd063f0750f2c24cae7c29f40b24a8a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -31,7 +31,7 @@ namespace BaseGames.Editor
|
||||
+ string.Join("\n", errors));
|
||||
}
|
||||
|
||||
[MenuItem("BaseGames/Tools/Validate All ScriptableObjects")]
|
||||
[MenuItem("BaseGames/Tools/Validation/Validate All ScriptableObjects")]
|
||||
public static void ValidateMenu()
|
||||
{
|
||||
var (errors, warnings) = RunAll();
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace BaseGames.Editor
|
||||
new OrderRule("PlayerController", -100),
|
||||
};
|
||||
|
||||
[MenuItem("BaseGames/Tools/Apply Script Execution Order Preset")]
|
||||
[MenuItem("BaseGames/Tools/Validation/Apply Script Execution Order Preset")]
|
||||
public static void ApplyPreset()
|
||||
{
|
||||
int updated = 0;
|
||||
@@ -69,7 +69,7 @@ namespace BaseGames.Editor
|
||||
Debug.Log($"[ScriptExecutionOrderTools] 执行顺序预设应用完成。更新数量: {updated}。");
|
||||
}
|
||||
|
||||
[MenuItem("BaseGames/Tools/Validate Script Execution Order Preset")]
|
||||
[MenuItem("BaseGames/Tools/Validation/Validate Script Execution Order Preset")]
|
||||
public static void ValidatePreset()
|
||||
{
|
||||
var mismatches = new List<string>();
|
||||
|
||||
Reference in New Issue
Block a user