using Unity.Netcode; using Unity.Netcode.Components; using UnityEngine; using UnityEngine.InputSystem; using System.Collections.Generic; namespace Northbound { /// /// 상호작용 대상 없이 실행 가능한 액션들을 관리 /// public class PlayerActionSystem : NetworkBehaviour { [Header("Actions")] public List actionComponents = new List(); [Header("Animation")] public bool playAnimations = true; private Dictionary _actions = new Dictionary(); 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(); _networkAnimator = GetComponent(); _networkPlayerController = GetComponent(); } 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 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(); } } }