using BaseGames.Core.Events;
using BaseGames.Input;
using UnityEngine;
namespace BaseGames.World
{
///
/// 挂在 Player 上,检测附近可交互物,驱动 UI 提示显示/隐藏。
/// 通过 InputReaderSO.InteractEvent 绑定交互输入。
///
public class InteractableDetector : MonoBehaviour
{
[SerializeField] private float _detectRadius = 1.5f;
[SerializeField] private LayerMask _interactableLayer;
[SerializeField] private InputReaderSO _inputReader;
[SerializeField] private StringEventChannelSO _onShowInteractPrompt;
[SerializeField] private VoidEventChannelSO _onHideInteractPrompt;
private IInteractable _nearest;
private IInteractable _previousNearest;
private void OnEnable() => _inputReader.InteractEvent += TryInteract;
private void OnDisable() => _inputReader.InteractEvent -= TryInteract;
private void Update()
{
var hits = Physics2D.OverlapCircleAll(transform.position, _detectRadius, _interactableLayer);
_nearest = FindNearest(hits);
if (_nearest != _previousNearest)
{
if (_previousNearest != null)
{
_previousNearest.OnPlayerExitRange();
_onHideInteractPrompt?.Raise();
}
if (_nearest != null)
{
_nearest.OnPlayerEnterRange(transform);
_onShowInteractPrompt?.Raise(_nearest.InteractPrompt);
}
_previousNearest = _nearest;
}
}
private void TryInteract()
{
if (_nearest != null && _nearest.CanInteract)
_nearest.Interact(transform);
}
private IInteractable FindNearest(Collider2D[] hits)
{
IInteractable best = null;
float bestDist = float.MaxValue;
foreach (var col in hits)
{
var interactable = col.GetComponentInParent();
if (interactable == null || !interactable.CanInteract) continue;
float dist = Vector2.Distance(transform.position, col.transform.position);
if (dist < bestDist)
{
bestDist = dist;
best = interactable;
}
}
return best;
}
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.cyan;
Gizmos.DrawWireSphere(transform.position, _detectRadius);
}
}
}