Refactor interaction prompt system to use world space prompts
- Removed the InteractPromptWidget from HUD and its references in HUDController. - Introduced IInteractPromptView interface for world space interaction prompts. - Implemented WorldInteractPrompt class to manage display of interaction prompts in world space. - Updated InteractableDetector to handle showing/hiding of world space prompts based on player proximity to interactable objects. - Created a new prefab for UI_WorldInteractPrompt to facilitate the new interaction prompt system.
This commit is contained in:
@@ -1,21 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
using BaseGames.Core.Events;
|
||||
using BaseGames.Core.Interaction;
|
||||
using BaseGames.Input;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.World
|
||||
{
|
||||
/// <summary>
|
||||
/// 挂在 Player 上,检测附近可交互物,驱动 UI 提示显示/隐藏。
|
||||
/// 挂在 Player 上,检测附近可交互物,并驱动其世界空间提示(<see cref="IInteractPromptView"/>)显隐。
|
||||
/// 通过 InputReaderSO.InteractEvent 绑定交互输入。
|
||||
/// <para>
|
||||
/// 提示表现是「每个交互物自带」的世界空间子节点(跟随物体、可单独配样式),
|
||||
/// 本组件只负责「检测最近可交互物」并通知它显示/隐藏,不持有任何 UI。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public class InteractableDetector : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private float _detectRadius = 1.5f;
|
||||
[SerializeField] private LayerMask _interactableLayer;
|
||||
[SerializeField] private InputReaderSO _inputReader;
|
||||
[SerializeField] private InteractPromptEventChannelSO _onShowInteractPrompt;
|
||||
[SerializeField] private VoidEventChannelSO _onHideInteractPrompt;
|
||||
[SerializeField] private float _detectRadius = 1.5f;
|
||||
[SerializeField] private LayerMask _interactableLayer;
|
||||
[SerializeField] private InputReaderSO _inputReader;
|
||||
|
||||
private IInteractable _nearest;
|
||||
private IInteractable _previousNearest;
|
||||
@@ -26,6 +28,9 @@ namespace BaseGames.World
|
||||
// Collider → IInteractable 缓存,避免 FindNearest 每帧重复 GetComponentInParent
|
||||
private readonly Dictionary<Collider2D, IInteractable> _componentCache = new();
|
||||
|
||||
// IInteractable → 其世界空间提示视图缓存(可能为 null,表示该物体未配置提示)
|
||||
private readonly Dictionary<IInteractable, IInteractPromptView> _promptCache = new();
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_inputReader.InteractEvent += TryInteract;
|
||||
@@ -34,7 +39,12 @@ namespace BaseGames.World
|
||||
private void OnDisable()
|
||||
{
|
||||
_inputReader.InteractEvent -= TryInteract;
|
||||
// 离场前隐藏当前提示,防止跨场景残留
|
||||
ResolvePrompt(_previousNearest)?.Hide();
|
||||
_componentCache.Clear(); // 清理缓存,防止跨场景持有旧引用
|
||||
_promptCache.Clear();
|
||||
_previousNearest = null;
|
||||
_nearest = null;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
@@ -48,13 +58,13 @@ namespace BaseGames.World
|
||||
if (_previousNearest != null)
|
||||
{
|
||||
_previousNearest.OnPlayerExitRange();
|
||||
_onHideInteractPrompt?.Raise();
|
||||
ResolvePrompt(_previousNearest)?.Hide();
|
||||
}
|
||||
|
||||
if (_nearest != null)
|
||||
{
|
||||
_nearest.OnPlayerEnterRange(transform);
|
||||
_onShowInteractPrompt?.Raise(new InteractPromptEvent("Interact", _nearest.InteractPrompt));
|
||||
ResolvePrompt(_nearest)?.Show(_nearest.InteractPrompt);
|
||||
}
|
||||
|
||||
_previousNearest = _nearest;
|
||||
@@ -98,6 +108,17 @@ namespace BaseGames.World
|
||||
return best;
|
||||
}
|
||||
|
||||
/// <summary>解析交互物的世界空间提示视图(在其子节点上查找一次并缓存;无则记 null)。</summary>
|
||||
private IInteractPromptView ResolvePrompt(IInteractable interactable)
|
||||
{
|
||||
if (interactable == null) return null;
|
||||
if (_promptCache.TryGetValue(interactable, out var view)) return view;
|
||||
|
||||
view = (interactable as Component)?.GetComponentInChildren<IInteractPromptView>(true);
|
||||
_promptCache[interactable] = view;
|
||||
return view;
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
Gizmos.color = Color.cyan;
|
||||
|
||||
Reference in New Issue
Block a user