From 6f897a7af730cdd3a47e02496e69e8609b79b8ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=80=9D=E6=B5=B7?= <1464576565@qq.com> Date: Wed, 15 Apr 2026 14:23:30 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Navigation/UXNavigationRuntime.cs | 43 ++++++++++++++++--- .../Navigation/UXNavigationScope.cs | 25 +++++++++-- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/Runtime/UXComponent/Navigation/UXNavigationRuntime.cs b/Runtime/UXComponent/Navigation/UXNavigationRuntime.cs index 031c5a8..5cd32c2 100644 --- a/Runtime/UXComponent/Navigation/UXNavigationRuntime.cs +++ b/Runtime/UXComponent/Navigation/UXNavigationRuntime.cs @@ -23,6 +23,9 @@ namespace UnityEngine.UI private ulong _activationSerial; private bool _missingEventSystemLogged; + private bool _topScopeDirty = true; + private bool _suppressionDirty = true; + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] private static void Bootstrap() { @@ -101,6 +104,8 @@ namespace UnityEngine.UI } _scopes.Add(scope); + _topScopeDirty = true; + _suppressionDirty = true; } internal void UnregisterScope(UXNavigationScope scope) @@ -117,7 +122,17 @@ namespace UnityEngine.UI scope.IsAvailable = false; scope.SetNavigationSuppressed(false); - _scopes.Remove(scope); + + int idx = _scopes.IndexOf(scope); + if (idx >= 0) + { + int last = _scopes.Count - 1; + _scopes[idx] = _scopes[last]; + _scopes.RemoveAt(last); + } + + _topScopeDirty = true; + _suppressionDirty = true; } internal void MarkDiscoveryDirty() @@ -138,13 +153,22 @@ namespace UnityEngine.UI DiscoverScopes(); } - UXNavigationScope newTopScope = FindTopScope(); - if (!ReferenceEquals(_topScope, newTopScope)) + if (_topScopeDirty) { - _topScope = newTopScope; + UXNavigationScope newTopScope = FindTopScope(); + _topScopeDirty = false; + if (!ReferenceEquals(_topScope, newTopScope)) + { + _topScope = newTopScope; + _suppressionDirty = true; + } } - ApplyScopeSuppression(); + if (_suppressionDirty) + { + ApplyScopeSuppression(); + _suppressionDirty = false; + } if (UXInputModeService.CurrentMode == UXInputMode.Gamepad) { @@ -237,6 +261,9 @@ namespace UnityEngine.UI { _scopes[i]?.InvalidateSelectableCache(); } + + _topScopeDirty = true; + _suppressionDirty = true; } } @@ -275,6 +302,8 @@ namespace UnityEngine.UI { scope.ActivationSerial = ++_activationSerial; } + + _suppressionDirty = true; } if (!available) @@ -304,11 +333,13 @@ namespace UnityEngine.UI return false; } - return !IsNavigationSkipped(scope.transform) + return !scope.IsNavigationSkipped && scope.HasAvailableSelectable() && TryGetInteractiveLayerRoot(scope.transform, out _); } + // 保留静态方法用于 DiscoverScopes 中对 LayerRoot / Holder 节点的检测 + // 这些节点无 UXNavigationScope,调用频次低(仅在 dirty 时),无需缓存 private static bool IsNavigationSkipped(Transform current) { return current != null && current.GetComponentInParent(true) != null; diff --git a/Runtime/UXComponent/Navigation/UXNavigationScope.cs b/Runtime/UXComponent/Navigation/UXNavigationScope.cs index 14559de..6d578cd 100644 --- a/Runtime/UXComponent/Navigation/UXNavigationScope.cs +++ b/Runtime/UXComponent/Navigation/UXNavigationScope.cs @@ -33,6 +33,9 @@ namespace UnityEngine.UI private bool _navigationSuppressed; private int _cachedHierarchyDepth = -1; + private bool _cachedIsSkipped; + private bool _isSkippedCacheValid; + internal ulong ActivationSerial { get; set; } internal bool IsAvailable { get; set; } internal bool WasAvailable { get; set; } @@ -47,6 +50,19 @@ namespace UnityEngine.UI public bool RequireSelectionWhenGamepad => _requireSelectionWhenGamepad; public bool BlockLowerScopes => _blockLowerScopes; + internal bool IsNavigationSkipped + { + get + { + if (!_isSkippedCacheValid) + { + _cachedIsSkipped = GetComponentInParent(true) != null; + _isSkippedCacheValid = true; + } + return _cachedIsSkipped; + } + } + internal Canvas Canvas { get @@ -108,6 +124,7 @@ namespace UnityEngine.UI _cachedCanvas = null; _cachedHolder = null; _cachedHierarchyDepth = -1; + _isSkippedCacheValid = false; } #if UNITY_EDITOR @@ -219,12 +236,12 @@ namespace UnityEngine.UI internal void SetNavigationSuppressed(bool suppressed) { - RefreshSelectableCache(); if (_navigationSuppressed == suppressed) { return; } + RefreshSelectableCache(); _navigationSuppressed = suppressed; for (int i = 0; i < _cachedSelectables.Count; i++) { @@ -281,11 +298,11 @@ namespace UnityEngine.UI } _removeBuffer.Clear(); - foreach (var pair in _baselineNavigation) + foreach (Selectable key in _baselineNavigation.Keys) { - if (!_cachedSelectableSet.Contains(pair.Key)) + if (!_cachedSelectableSet.Contains(key)) { - _removeBuffer.Add(pair.Key); + _removeBuffer.Add(key); } }