ProjectDDD/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantCharacterInteraction.cs
2025-08-07 17:51:36 +09:00

119 lines
4.1 KiB
C#

using System.Collections.Generic;
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);
private Dictionary<InteractionType, IInteractionSolver> _cachedSolvers = new();
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;
_interactingTarget = null;
_interactHeldTime = 0f;
OnInteractionHoldProgress(0f);
}
protected IInteractable GetNearestInteractable()
{
int colliderCount = Physics.OverlapSphereNonAlloc(transform.position, _interactionRadius, _nearColliders, _interactionLayerMask);
float closestDistance = float.MaxValue;
IInteractable closest = null;
for (int i = 0; i < colliderCount; i++)
{
var col = _nearColliders[i];
if (col.TryGetComponent<IInteractable>(out var interactable) == false || interactable.CanInteract() == false) continue;
var type = interactable.GetInteractionType();
if (ContainTypeToSolver(type, out var solver) == false || solver.CanExecuteInteraction() == false) continue;
float distance = Vector3.Distance(transform.position, col.transform.position);
if (distance < closestDistance)
{
closestDistance = distance;
closest = interactable;
}
}
return closest;
}
public virtual void Invoke(RestaurantInteractionEvent evt) { }
public GameObject GetInteractorGameObject() => gameObject;
private bool ContainTypeToSolver(InteractionType type, out IInteractionSolver solver)
{
if (_cachedSolvers.TryGetValue(type, out solver)) return solver != null;
solver = null;
if (!RestaurantInteractionEventSolvers.TypeToSolver.TryGetValue(type, out var solverType)) return false;
if (!TryGetComponent(solverType, out var component)) return false;
solver = component as IInteractionSolver;
_cachedSolvers[type] = solver;
return solver != null;
}
}
}