Files
Northbound/Assets/Scripts/PlayerActionSystem.cs

140 lines
4.8 KiB
C#

using Unity.Netcode;
using Unity.Netcode.Components;
using UnityEngine;
using UnityEngine.InputSystem;
using System.Collections.Generic;
namespace Northbound
{
/// <summary>
/// 상호작용 대상 없이 실행 가능한 액션들을 관리
/// </summary>
public class PlayerActionSystem : NetworkBehaviour
{
[Header("Actions")]
public List<MonoBehaviour> actionComponents = new List<MonoBehaviour>();
[Header("Animation")]
public bool playAnimations = true;
private Dictionary<string, IAction> _actions = new Dictionary<string, IAction>();
private Animator _animator;
private NetworkAnimator _networkAnimator;
private NetworkPlayerController _networkPlayerController;
private bool _isInputInitialized = false;
// 로컬 플레이어인지 확인
private bool IsLocalPlayer => _networkPlayerController != null && _networkPlayerController.IsLocalPlayer;
private ulong LocalPlayerId => _networkPlayerController != null ? _networkPlayerController.OwnerPlayerId : OwnerClientId;
private void Awake()
{
_animator = GetComponent<Animator>();
_networkAnimator = GetComponent<NetworkAnimator>();
_networkPlayerController = GetComponent<NetworkPlayerController>();
}
public override void OnNetworkSpawn()
{
// 액션 컴포넌트들을 딕셔너리에 등록 (모든 클라이언트에서)
foreach (var component in actionComponents)
{
if (component is IAction action)
{
_actions[action.GetActionName()] = action;
}
}
// _ownerPlayerId 변경 이벤트 구독
if (_networkPlayerController != null)
{
_networkPlayerController.OnOwnerChanged += OnOwnerPlayerIdChanged;
_networkPlayerController.OnInputInitialized += TryInitializeInput;
}
// 이미 로컬 플레이어면 입력 초기화
TryInitializeInput();
}
private void Start()
{
// Start에서 다시 한번 확인 (이벤트 타이밍 문제 해결)
TryInitializeInput();
}
private void OnOwnerPlayerIdChanged(ulong newOwnerId)
{
TryInitializeInput();
}
private void TryInitializeInput()
{
if (!IsLocalPlayer) return;
if (_isInputInitialized) return; // 이미 초기화됨
if (_networkPlayerController.InputActions == null) return; // 아직 InputActions가 없음
_isInputInitialized = true;
_networkPlayerController.InputActions.Player.Attack.performed += OnAttack;
}
public override void OnNetworkDespawn()
{
if (_networkPlayerController != null)
{
_networkPlayerController.OnOwnerChanged -= OnOwnerPlayerIdChanged;
_networkPlayerController.OnInputInitialized -= TryInitializeInput;
// 입력 이벤트 해제
if (_networkPlayerController.InputActions != null)
{
_networkPlayerController.InputActions.Player.Attack.performed -= OnAttack;
}
}
}
private void OnAttack(InputAction.CallbackContext context)
{
// UI가 열려있으면 액션 실행 안 함
if (UpgradeListPopup.IsOpen) return;
// 플레이어가 죽어있으면 액션 실행 안 함
if (_networkPlayerController != null && _networkPlayerController.IsDead()) return;
ExecuteAction("Attack");
}
public void ExecuteAction(string actionName)
{
if (_actions.TryGetValue(actionName, out IAction action))
{
if (action.CanExecute(LocalPlayerId))
{
string animTrigger = action.GetActionAnimation();
// 서버에서 애니메이션 실행 (동기화를 위해)
if (playAnimations && !string.IsNullOrEmpty(animTrigger))
{
PlayAnimationServerRpc(animTrigger);
}
action.Execute(LocalPlayerId);
}
}
}
[Rpc(SendTo.Server)]
private void PlayAnimationServerRpc(string animTrigger)
{
if (_networkAnimator != null && !string.IsNullOrEmpty(animTrigger))
{
_networkAnimator.SetTrigger(animTrigger);
}
}
override public void OnDestroy()
{
// 입력 정리는 NetworkPlayerController에서 담당
base.OnDestroy();
}
}
}