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 PlayerInputActions _inputActions; private Dictionary _actions = new Dictionary(); private Animator _animator; private NetworkAnimator _networkAnimator; private NetworkPlayerController _networkPlayerController; // 로컬 플레이어인지 확인 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; } // 이미 로컬 플레이어면 입력 초기화 TryInitializeInput(); } private void OnOwnerPlayerIdChanged(ulong newOwnerId) { TryInitializeInput(); } private void TryInitializeInput() { if (!IsLocalPlayer) return; if (_inputActions != null) return; _inputActions = new PlayerInputActions(); _inputActions.Player.Attack.performed += OnAttack; _inputActions.Enable(); } public override void OnNetworkDespawn() { if (_networkPlayerController != null) { _networkPlayerController.OnOwnerChanged -= OnOwnerPlayerIdChanged; } if (_inputActions != null) { _inputActions.Player.Attack.performed -= OnAttack; _inputActions.Disable(); _inputActions.Dispose(); } } private void OnAttack(InputAction.CallbackContext context) { 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() { if (_inputActions != null) { _inputActions.Dispose(); } base.OnDestroy(); } } }