From 9ddb91a0878ef7aa4a721db623a12169caf65f31 Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Wed, 15 Apr 2026 11:26:47 +0700 Subject: [PATCH] Fix raycast to not hit through ui --- .../Scripts/Managers/EC_HostInputFilter.cs | 12 ++++ Assets/PerfectWorld/Scripts/UI/pickupItem.cs | 7 ++ Assets/Scripts/CECUIManager.cs | 70 +++++++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_HostInputFilter.cs b/Assets/PerfectWorld/Scripts/Managers/EC_HostInputFilter.cs index edfce9b093..f8b0964885 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_HostInputFilter.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_HostInputFilter.cs @@ -99,6 +99,18 @@ namespace BrewMonster /// Optional target ID to use instead of raycast (for second-click behavior) / 可选的目标ID,用于替代射线检测(用于第二次点击行为) public void OnMsgLBtnClick(int idTargetOverride = 0) { + // Interactive UI (buttons, fields, scrolls) consumes the click. Full-screen raycast panels alone do not. + // When a non-HUD dialog is open, block world interaction entirely (terrain/NPC/matter) until it is closed. + if (idTargetOverride == 0) + { + if (CECUIManager.IsPointerOverInteractiveUI()) + return; + + var uiMan = EC_Game.GetGameRun()?.GetUIManager(); + if (uiMan != null && uiMan.IsWorldInteractionBlockedByUI()) + return; + } + // 停止自动策略 / Stop auto policy // Note: Auto policy check would go here if implemented diff --git a/Assets/PerfectWorld/Scripts/UI/pickupItem.cs b/Assets/PerfectWorld/Scripts/UI/pickupItem.cs index abc7386815..ec541a8752 100644 --- a/Assets/PerfectWorld/Scripts/UI/pickupItem.cs +++ b/Assets/PerfectWorld/Scripts/UI/pickupItem.cs @@ -84,6 +84,13 @@ public class pickupItem : MonoBehaviour // Check for mouse clicks on cubes if (Input.GetMouseButtonDown(0)) { + if (CECUIManager.IsPointerOverInteractiveUI()) + return; + + var uiMan = EC_Game.GetGameRun()?.GetUIManager(); + if (uiMan != null && uiMan.IsWorldInteractionBlockedByUI()) + return; + HandleCubeClick(); } } diff --git a/Assets/Scripts/CECUIManager.cs b/Assets/Scripts/CECUIManager.cs index e6bf282408..08ac4c1171 100644 --- a/Assets/Scripts/CECUIManager.cs +++ b/Assets/Scripts/CECUIManager.cs @@ -9,6 +9,8 @@ using BrewMonster.Scripts.Managers; using BrewMonster.Scripts.Task.UI; using BrewMonster.Scripts.UI; using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; using TMPro; public class CECUIManager : MonoSingleton @@ -366,6 +368,74 @@ public class CECUIManager : MonoSingleton return null; } + /// + /// Dialog names on that are normal gameplay HUD and must NOT block world raycasts + /// (the stack always has at least Win_Hpmpxp after ). + /// + private static readonly HashSet HudStackDialogNames = new HashSet(StringComparer.Ordinal) + { + "Win_Hpmpxp", + }; + + /// + /// Returns true when a modal / non-HUD dialog is open and world clicks should be ignored. + /// + public bool IsWorldInteractionBlockedByUI() + { + if (_uiStack == null || _uiStack.Count == 0) + return false; + + foreach (var name in _uiStack) + { + if (string.IsNullOrEmpty(name)) + continue; + if (!HudStackDialogNames.Contains(name)) + return true; + } + + return false; + } + + /// + /// True when the pointer hits UI that should consume the click (buttons, fields, scroll views). + /// Unlike , full-screen Images/Text with raycast + /// but no interactive control do not block — so clicks can reach the world for NPC selection. + /// + public static bool IsPointerOverInteractiveUI() + { + var es = EventSystem.current; + if (es == null) + return false; + + Vector2 pos = Input.mousePosition; + if (Input.touchCount > 0) + pos = Input.GetTouch(0).position; + + var ped = new PointerEventData(es) { position = pos }; + var results = new List(); + es.RaycastAll(ped, results); + if (results.Count == 0) + return false; + + foreach (var r in results) + { + var go = r.gameObject; + if (go == null) + continue; + + if (go.GetComponentInParent() != null) + return true; + if (go.GetComponentInParent() != null) + return true; + if (go.GetComponentInParent() != null) + return true; + if (go.GetComponentInParent() != null) + return true; + } + + return false; + } + // public CDlgMessageBox ShowMessageBox(MessageBoxData messageBoxData) // { // var msgBox = GetInGameUIMan().GetDialog("DlgMessageBox") as CDlgMessageBox;