using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
namespace Northbound
{
///
/// 플레이어가 월드의 오브젝트와 상호작용하는 시스템
///
public class PlayerInteraction : NetworkBehaviour
{
[Header("Interaction Settings")]
public float interactionRange = 3f;
public LayerMask interactableLayer = ~0;
[Header("Detection")]
public Transform rayOrigin;
public bool useForwardDirection = true;
[Header("Animation")]
public bool playAnimations = true;
public bool useAnimationEvents = true;
public bool blockDuringAnimation = true;
[Header("Equipment")]
public bool useEquipment = true;
[Header("Debug")]
public bool showDebugRay = true;
private PlayerInputActions _inputActions;
private IInteractable _currentInteractable;
private Camera _mainCamera;
private Animator _animator;
private EquipmentSocket _equipmentSocket;
private bool _isInteracting;
private float _interactionStartTime;
private const float INTERACTION_TIMEOUT = 3f;
public bool IsInteracting => _isInteracting;
private void Awake()
{
_animator = GetComponent();
_equipmentSocket = GetComponent();
}
public override void OnNetworkSpawn()
{
base.OnNetworkSpawn();
if (rayOrigin == null)
rayOrigin = transform;
_inputActions = new PlayerInputActions();
_inputActions.Player.Interact.performed += OnInteract;
_inputActions.Enable();
}
public override void OnNetworkDespawn()
{
if (_inputActions != null)
{
_inputActions.Player.Interact.performed -= OnInteract;
_inputActions.Disable();
_inputActions.Dispose();
}
base.OnNetworkDespawn();
}
private void Update()
{
if (!IsOwner) return;
if (_isInteracting && Time.time - _interactionStartTime > INTERACTION_TIMEOUT)
{
_isInteracting = false;
}
DetectInteractable();
}
private void DetectInteractable()
{
Vector3 origin = rayOrigin.position;
Vector3 direction = useForwardDirection ? transform.forward : _mainCamera.transform.forward;
Ray ray = new Ray(origin, direction);
if (showDebugRay)
{
Debug.DrawRay(ray.origin, ray.direction * interactionRange,
_currentInteractable != null ? Color.green : Color.yellow);
}
if (Physics.Raycast(ray, out RaycastHit hit, interactionRange, interactableLayer))
{
IInteractable interactable = hit.collider.GetComponent();
if (interactable == null)
{
interactable = hit.collider.GetComponentInParent();
}
if (interactable != null && interactable.CanInteract(OwnerClientId))
{
_currentInteractable = interactable;
return;
}
}
_currentInteractable = null;
}
private void OnInteract(InputAction.CallbackContext context)
{
if (blockDuringAnimation && IsInteracting)
return;
if (_currentInteractable == null)
return;
var equipmentData = _currentInteractable.GetEquipmentData();
string animTrigger = _currentInteractable.GetInteractionAnimation();
bool hasAnimation = !string.IsNullOrEmpty(animTrigger);
if (playAnimations && _animator != null && hasAnimation)
{
_isInteracting = true;
_interactionStartTime = Time.time;
if (useAnimationEvents)
{
_animator.SetTrigger(animTrigger);
}
else
{
_animator.SetTrigger(animTrigger);
_currentInteractable.Interact(OwnerClientId);
_isInteracting = false;
}
}
else
{
_currentInteractable.Interact(OwnerClientId);
}
}
public void AttachEquipment()
{
if (!IsOwner) return;
if (_currentInteractable == null || !useEquipment) return;
var equipmentData = _currentInteractable.GetEquipmentData();
if (equipmentData == null || !equipmentData.attachOnStart) return;
if (_equipmentSocket == null)
{
Debug.LogWarning("[PlayerInteraction] EquipmentSocket component not found on player");
return;
}
if (equipmentData.equipmentPrefab == null)
{
Debug.LogWarning("[PlayerInteraction] Equipment prefab is null. Assign a prefab in the Resource's EquipmentData in Inspector");
return;
}
_equipmentSocket.AttachToSocket(equipmentData.socketName, equipmentData.equipmentPrefab);
}
public void DetachEquipment()
{
if (!IsOwner) return;
if (_currentInteractable == null || !useEquipment) return;
var equipmentData = _currentInteractable.GetEquipmentData();
if (equipmentData == null || !equipmentData.detachOnEnd) return;
if (_equipmentSocket == null) return;
_equipmentSocket.DetachFromSocket(equipmentData.socketName);
}
public void OnInteractionComplete()
{
_isInteracting = false;
if (_currentInteractable != null)
{
_currentInteractable.Interact(OwnerClientId);
}
}
}
}