2026-06-18 많은 플레이어 수정사항
This commit is contained in:
132
Assets/02_Scripts/Interaction/InteractionDetector.cs
Normal file
132
Assets/02_Scripts/Interaction/InteractionDetector.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
// 플레이어에 붙는다. 매 프레임 반경(_detectRadius) 안의 InteractionObject를 OverlapSphere로 찾아
|
||||
// 가장 가까운 대상을 Current로 노출한다. Interact 키를 누르면 그 대상과 상호작용한다.
|
||||
// 지속형 상호작용(앉기 등) 중에는 대상을 잠가, 다음 키 입력이 종료로 가게 한다.
|
||||
//
|
||||
// 트리거 이벤트가 아니라 물리 쿼리(OverlapSphere)라 Rigidbody/트리거/충돌 매트릭스 설정이 필요 없다.
|
||||
// 의자 등 대상에 Collider만 있으면 감지된다.
|
||||
public class InteractionDetector : MonoBehaviour, ISceneInitializable
|
||||
{
|
||||
[SerializeField] private PlayerController _player;
|
||||
[SerializeField] private float _detectRadius = 1.5f; // 감지 반경(m)
|
||||
[SerializeField] private LayerMask _detectLayers = ~0; // 감지할 레이어 (기본: 전체)
|
||||
[SerializeField] private bool _debugLog = true; // 콘솔에 감지/입력 로그 (문제 진단용)
|
||||
|
||||
private InteractionObject _current; // 가장 가까운 상호작용 대상 (없으면 null)
|
||||
private InteractionObject _active; // 진행 중인 지속형 상호작용 (앉아있는 의자 등). 있으면 대상 잠금
|
||||
private readonly Collider[] _hits = new Collider[16]; // OverlapSphereNonAlloc 결과 버퍼
|
||||
|
||||
// UI 프롬프트용: 상호작용 가능한 대상이 바뀔 때 호출 (null이면 "대상 없음")
|
||||
public event Action<InteractionObject> OnInteractableChanged;
|
||||
|
||||
public InteractionObject Current => _current; // 현재 상호작용 가능한 대상
|
||||
public bool HasActiveInteraction => _active != null;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (_player == null) _player = GetComponentInParent<PlayerController>();
|
||||
}
|
||||
|
||||
private void Start() => Subscribe(); // SceneLoadManager가 없는 씬에서도 동작하도록 폴백 구독
|
||||
|
||||
// 씬 로드 후 SceneLoadManager가 호출 (있을 때)
|
||||
public void OnSceneLoaded() => Subscribe();
|
||||
|
||||
// 중복 없이 InputManager 구독 (Start/OnSceneLoaded 양쪽에서 호출돼도 안전)
|
||||
private void Subscribe()
|
||||
{
|
||||
if (InputManager.Instance == null)
|
||||
{
|
||||
Debug.LogWarning("[Interaction] InputManager.Instance가 없어 Interact 입력을 구독하지 못했습니다.", this);
|
||||
return;
|
||||
}
|
||||
InputManager.Instance.OnInteract_Event -= HandleInteract;
|
||||
InputManager.Instance.OnInteract_Event += HandleInteract;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (InputManager.Instance != null)
|
||||
InputManager.Instance.OnInteract_Event -= HandleInteract;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// 진행 중인 상호작용이 있으면 대상 고정(잠금) — 가장 가까운 대상 갱신 안 함
|
||||
if (_active != null) return;
|
||||
UpdateCurrent();
|
||||
}
|
||||
|
||||
// 반경 안에서 가장 가까운 InteractionObject를 골라 Current 갱신 (바뀌면 이벤트 발생)
|
||||
private void UpdateCurrent()
|
||||
{
|
||||
InteractionObject nearest = null;
|
||||
float best = float.MaxValue;
|
||||
Vector3 p = transform.position;
|
||||
|
||||
// 마스크가 Nothing(0)이면 전체 레이어로 (인스펙터에서 미설정 시 안전장치)
|
||||
int mask = _detectLayers.value == 0 ? ~0 : _detectLayers.value;
|
||||
|
||||
// 트리거 콜라이더도 잡도록 QueryTriggerInteraction.Collide
|
||||
int count = Physics.OverlapSphereNonAlloc(p, _detectRadius, _hits, mask, QueryTriggerInteraction.Collide);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Collider c = _hits[i];
|
||||
if (c == null) continue;
|
||||
|
||||
// 콜라이더가 자식이어도 부모 쪽까지 탐색
|
||||
InteractionObject obj = c.GetComponentInParent<InteractionObject>();
|
||||
if (obj == null) continue;
|
||||
|
||||
float d = (obj.transform.position - p).sqrMagnitude;
|
||||
if (d < best) { best = d; nearest = obj; }
|
||||
}
|
||||
|
||||
if (nearest != _current)
|
||||
{
|
||||
_current = nearest;
|
||||
if (_debugLog) Debug.Log($"[Interaction] 대상 변경: {(nearest ? nearest.name : "none")}", this);
|
||||
OnInteractableChanged?.Invoke(_current); // UI 프롬프트 갱신용
|
||||
}
|
||||
}
|
||||
|
||||
// Interact 키 입력 처리
|
||||
private void HandleInteract()
|
||||
{
|
||||
if (_debugLog)
|
||||
Debug.Log($"[Interaction] 키 입력. active={(_active ? _active.name : "none")}, current={(_current ? _current.name : "none")}");
|
||||
|
||||
if (_player == null)
|
||||
{
|
||||
Debug.LogWarning("[Interaction] Player 참조가 없습니다. (Awake 자동탐색 실패)", this);
|
||||
return;
|
||||
}
|
||||
|
||||
// 1) 진행 중인 상호작용이 있으면 그 대상에게 (보통 종료/일어서기)
|
||||
if (_active != null)
|
||||
{
|
||||
_active.Interact(_player);
|
||||
if (!_active.IsInteracting) _active = null; // 종료됨 → 다음 Update에서 _current 재계산
|
||||
return;
|
||||
}
|
||||
|
||||
// 2) 아니면 가장 가까운 대상과 새로 상호작용 시작
|
||||
if (_current == null) return;
|
||||
_current.Interact(_player);
|
||||
if (_current.IsInteracting)
|
||||
{
|
||||
_active = _current; // 지속형이면 대상 잠금
|
||||
_current = null;
|
||||
OnInteractableChanged?.Invoke(null); // 잠금 중엔 프롬프트 숨김
|
||||
}
|
||||
}
|
||||
|
||||
// 씬 뷰에서 감지 반경 시각화 (선택 시)
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
Gizmos.color = Color.cyan;
|
||||
Gizmos.DrawWireSphere(transform.position, _detectRadius);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user