using Sirenix.OdinInspector; using UnityEngine; namespace DDD { public class RestaurantCharacterInteraction : MonoBehaviour, IInteractor, IEventHandler { [SerializeField, ReadOnly] protected Collider[] _nearColliders = new Collider[10]; protected IInteractable _nearestInteractable; protected IInteractable _previousInteractable; protected IInteractable _interactingTarget; protected float _interactHeldTime; protected bool _isInteracting; protected float _interactionRadius = 1f; protected LayerMask _interactionLayerMask = (LayerMask)(-1); protected virtual void Start() { } protected virtual void Update() { _nearestInteractable = GetNearestInteractable(); if (_nearestInteractable != _previousInteractable) { _previousInteractable = _nearestInteractable; OnNearestInteractableChanged(_nearestInteractable); } if (_isInteracting) { if (_nearestInteractable != _interactingTarget) { ResetInteractionState(); return; } _interactHeldTime += Time.deltaTime; float requiredHoldTime = _interactingTarget.GetRequiredHoldTime(); float ratio = Mathf.Clamp01(_interactHeldTime / requiredHoldTime); if (_interactHeldTime >= requiredHoldTime) { _isInteracting = false; _interactingTarget.OnInteracted(this); _interactingTarget = null; OnInteractionCompleted(); } OnInteractionHoldProgress(ratio); } } protected virtual void OnDestroy() { } protected virtual void OnNearestInteractableChanged(IInteractable newTarget) { } protected virtual void OnInteractionHoldProgress(float ratio) { } protected virtual void OnInteractionCompleted() { } protected void ResetInteractionState() { _isInteracting = false; _interactHeldTime = 0f; OnInteractionHoldProgress(0f); _interactingTarget = null; } protected IInteractable GetNearestInteractable() { int nearColliderCount = Physics.OverlapSphereNonAlloc(transform.position, _interactionRadius, _nearColliders, _interactionLayerMask, QueryTriggerInteraction.Collide); IInteractable nearest = null; float minSqrDistance = float.MaxValue; for (int i = 0; i < nearColliderCount; i++) { var interactable = _nearColliders[i].GetComponent(); if (interactable == null || !interactable.CanInteract()) continue; float sqrDistance = (interactable.GetInteractableGameObject().transform.position - transform.position).sqrMagnitude; if (sqrDistance < minSqrDistance) { minSqrDistance = sqrDistance; nearest = interactable; } } return nearest; } public virtual void Invoke(RestaurantInteractionEvent evt) { } public GameObject GetInteractorGameObject() => gameObject; } }