ProjectDDD/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantCharacterInteraction.cs
2025-08-06 17:55:29 +09:00

100 lines
3.4 KiB
C#

using Sirenix.OdinInspector;
using UnityEngine;
namespace DDD
{
public class RestaurantCharacterInteraction : MonoBehaviour, IInteractor, IEventHandler<RestaurantInteractionEvent>
{
[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<IInteractable>();
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;
}
}