Files
zeling_v2/Assets/_Game/Scripts/World/InteractableDetector.cs
Joywayer 68d4c699ae 修复内容:
PlayerMovement:新增 _facingLocked 字段 + LockFacing(bool) 方法;UpdateFacing() 锁定时直接返回
WallSlideState:OnStateEnter 调用 LockFacing(true) + FlipFacing(_wallDir);OnStateExit 调用 LockFacing(false) 解锁
WallJumpState:OnStateEnter 保险性再调一次 LockFacing(false);WallJumpAway/Toward 同步写入 _inputVelocityX,确保解锁后 UpdateFacing 朝向正确(背墙跳 = 离墙方向,对墙跳 = 朝墙方向)
2026-05-22 10:48:52 +08:00

108 lines
3.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using BaseGames.Core.Events;
using BaseGames.Input;
using UnityEngine;
namespace BaseGames.World
{
/// <summary>
/// 挂在 Player 上,检测附近可交互物,驱动 UI 提示显示/隐藏。
/// 通过 InputReaderSO.InteractEvent 绑定交互输入。
/// </summary>
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;
// 预分配检测缓冲区,避免 OverlapCircleAll 每帧 GC 分配
private readonly Collider2D[] _overlapBuffer = new Collider2D[16];
// Collider → IInteractable 缓存,避免 FindNearest 每帧重复 GetComponentInParent
private readonly Dictionary<Collider2D, IInteractable> _componentCache = new();
private void OnEnable()
{
_inputReader.InteractEvent += TryInteract;
}
private void OnDisable()
{
_inputReader.InteractEvent -= TryInteract;
_componentCache.Clear(); // 清理缓存,防止跨场景持有旧引用
}
private void Update()
{
int count = Physics2D.OverlapCircleNonAlloc(
transform.position, _detectRadius, _overlapBuffer, _interactableLayer);
_nearest = FindNearest(_overlapBuffer, count);
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, int count)
{
IInteractable best = null;
float bestSqrDist = float.MaxValue;
for (int i = 0; i < count; i++)
{
var col = hits[i];
if (col == null) continue;
// 查缓存,未命中时才调用 GetComponentInParent避免每帧反射开销
if (!_componentCache.TryGetValue(col, out var interactable))
{
interactable = col.GetComponentInParent<IInteractable>();
_componentCache[col] = interactable;
}
if (interactable == null || !interactable.CanInteract) continue;
// 用 sqrMagnitude 比较距离,省去 Distance 的 sqrt 开销
float sqrDist = ((Vector2)transform.position - (Vector2)col.transform.position).sqrMagnitude;
if (sqrDist < bestSqrDist)
{
bestSqrDist = sqrDist;
best = interactable;
}
}
return best;
}
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.cyan;
Gizmos.DrawWireSphere(transform.position, _detectRadius);
}
}
}