diff --git a/Assets/_Game/Scripts/Editor/Enemies/EnemyLocomotionEditor.cs b/Assets/_Game/Scripts/Editor/Enemies/EnemyLocomotionEditor.cs index 8c124d8b..9fc63519 100644 --- a/Assets/_Game/Scripts/Editor/Enemies/EnemyLocomotionEditor.cs +++ b/Assets/_Game/Scripts/Editor/Enemies/EnemyLocomotionEditor.cs @@ -30,8 +30,29 @@ namespace BaseGames.Editor private bool _cachedCanReachValid; private float _cachedMaxSnap = -1f; - private void OnEnable() => EditorApplication.update += ThrottledRepaint; - private void OnDisable() => EditorApplication.update -= ThrottledRepaint; + // 退出 Play 期间:Application.isPlaying 仍为 true 但 PBWorld 已销毁,此时调 CanReach 会 NRE。 + // 收到 ExitingPlayMode 即停止一切运行时查询。 + private bool _exitingPlay; + // 本帧是否绘制运行时区块——在 Layout 事件锁定、Repaint 复用,保证两趟控件数一致(否则 IMGUI 报错)。 + private bool _drawRuntimeThisFrame; + + private void OnEnable() + { + EditorApplication.update += ThrottledRepaint; + EditorApplication.playModeStateChanged += OnPlayModeChanged; + } + + private void OnDisable() + { + EditorApplication.update -= ThrottledRepaint; + EditorApplication.playModeStateChanged -= OnPlayModeChanged; + } + + private void OnPlayModeChanged(PlayModeStateChange s) + { + if (s == PlayModeStateChange.ExitingPlayMode) _exitingPlay = true; + else if (s == PlayModeStateChange.EnteredPlayMode) _exitingPlay = false; + } // 定频请求重绘(不在 OnInspectorGUI 里每帧 Repaint,避免绑定到游戏帧率狂刷) private void ThrottledRepaint() @@ -58,7 +79,12 @@ namespace BaseGames.Editor } GUI.backgroundColor = prevBg; - if (!Application.isPlaying) + // 是否绘制运行时区块的判定只在 Layout 事件更新,Repaint 复用同一结果—— + // 避免退出 Play 那一帧 isPlaying 在 Layout/Repaint 两趟间翻转导致控件数不一致而 IMGUI 报错。 + if (Event.current.type == EventType.Layout) + _drawRuntimeThisFrame = Application.isPlaying && !_exitingPlay; + + if (!_drawRuntimeThisFrame) { EditorGUILayout.Space(4f); EditorGUILayout.HelpBox("进入 Play 模式后此处显示实时巡逻调试信息(按钮在编辑期也可打印静态配置)。", MessageType.None); @@ -251,8 +277,9 @@ namespace BaseGames.Editor Row("到目标距离 / 到达半径", $"{dist:F2} / {loco.DebugArriveRadius:F2}{(arrived ? " (已到达)" : "")}", arrived ? Green : White); - // CanReach 会同步阻塞寻路 → 仅限频重算并缓存 - if (doHeavy && nav != null) + // CanReach 会同步阻塞寻路 → 仅限频重算并缓存。 + // 额外守卫 !_exitingPlay && isPlaying:退出 Play 那一刻 PBWorld 已销毁,此时调会 NRE。 + if (doHeavy && nav != null && !_exitingPlay && Application.isPlaying) { _cachedCanReach = nav.CanReach(goal); _cachedCanReachValid = true;