197 lines
6.2 KiB
C#
197 lines
6.2 KiB
C#
using Unity.Netcode;
|
|
using UnityEngine;
|
|
using UnityEngine.InputSystem;
|
|
|
|
namespace Northbound
|
|
{
|
|
/// <summary>
|
|
/// 플레이어가 월드의 오브젝트와 상호작용하는 시스템
|
|
/// </summary>
|
|
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<Animator>();
|
|
_equipmentSocket = GetComponent<EquipmentSocket>();
|
|
}
|
|
|
|
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<IInteractable>();
|
|
|
|
if (interactable == null)
|
|
{
|
|
interactable = hit.collider.GetComponentInParent<IInteractable>();
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|