From 9951aa98b2b66993af4ae969fcfc2f00d13a512a Mon Sep 17 00:00:00 2001 From: dal4segno Date: Thu, 26 Feb 2026 01:20:23 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=EC=9E=85=EB=A0=A5=20=EC=8B=9C=EC=8A=A4?= =?UTF-8?q?=ED=85=9C=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 각자의 입력 처리 로직을 갖지 않고 NetworkPlayerController로부터 받아서 쓰도록 함 --- Assets/Scripts/BuildingPlacement.cs | 61 +++++++++++------------ Assets/Scripts/NetworkPlayerController.cs | 9 ++++ Assets/Scripts/PlayerActionSystem.cs | 29 +++++------ Assets/Scripts/PlayerInteraction.cs | 30 ++++++----- 4 files changed, 65 insertions(+), 64 deletions(-) diff --git a/Assets/Scripts/BuildingPlacement.cs b/Assets/Scripts/BuildingPlacement.cs index 07dc5cd..60b839b 100644 --- a/Assets/Scripts/BuildingPlacement.cs +++ b/Assets/Scripts/BuildingPlacement.cs @@ -34,7 +34,6 @@ namespace Northbound private int currentRotation = 0; // 0-3 private Material[] originalMaterials; private Renderer[] previewRenderers; - private PlayerInputActions _inputActions; // 드래그 건설 관련 private bool isDragging = false; @@ -53,35 +52,29 @@ namespace Northbound { _playerController = GetComponent(); - // 지연 초기화 - 다음 프레임에 체크 - StartCoroutine(InitializeAfterOwnerSet()); + if (_playerController != null) + { + _playerController.OnInputInitialized += TryInitializeInput; + } + + // 이미 로컬 플레이어면 입력 초기화 시도 + TryInitializeInput(); } - private System.Collections.IEnumerator InitializeAfterOwnerSet() + private void TryInitializeInput() { - // _ownerPlayerId가 설정될 때까지 대기 (최대 1초) - float timeout = 1f; - while (_playerController != null && _playerController.OwnerPlayerId == ulong.MaxValue && timeout > 0) - { - yield return null; - timeout -= Time.deltaTime; - } - - // 로컬 플레이어인지 확인 - if (_playerController == null || !_playerController.IsLocalPlayer) - { - yield break; - } + if (_isInitialized) return; // 이미 초기화됨 + if (_playerController == null || !_playerController.IsLocalPlayer) return; + if (_playerController.InputActions == null) return; _isInitialized = true; - _inputActions = new PlayerInputActions(); - _inputActions.Player.ToggleBuildMode.performed += OnToggleBuildMode; - _inputActions.Player.Rotate.performed += OnRotate; - _inputActions.Player.Build.performed += OnBuildPressed; - _inputActions.Player.Build.canceled += OnBuildReleased; - _inputActions.Player.Cancel.performed += OnCancel; - _inputActions.Enable(); + var inputActions = _playerController.InputActions; + inputActions.Player.ToggleBuildMode.performed += OnToggleBuildMode; + inputActions.Player.Rotate.performed += OnRotate; + inputActions.Player.Build.performed += OnBuildPressed; + inputActions.Player.Build.canceled += OnBuildReleased; + inputActions.Player.Cancel.performed += OnCancel; // Create default materials if not assigned if (validMaterial == null) @@ -144,15 +137,19 @@ namespace Northbound public override void OnNetworkDespawn() { - if (_isInitialized && _inputActions != null) + if (_playerController != null) { - _inputActions.Player.ToggleBuildMode.performed -= OnToggleBuildMode; - _inputActions.Player.Rotate.performed -= OnRotate; - _inputActions.Player.Build.performed -= OnBuildPressed; - _inputActions.Player.Build.canceled -= OnBuildReleased; - _inputActions.Player.Cancel.performed -= OnCancel; - _inputActions.Disable(); - _inputActions.Dispose(); + _playerController.OnInputInitialized -= TryInitializeInput; + + if (_isInitialized && _playerController.InputActions != null) + { + var inputActions = _playerController.InputActions; + inputActions.Player.ToggleBuildMode.performed -= OnToggleBuildMode; + inputActions.Player.Rotate.performed -= OnRotate; + inputActions.Player.Build.performed -= OnBuildPressed; + inputActions.Player.Build.canceled -= OnBuildReleased; + inputActions.Player.Cancel.performed -= OnCancel; + } } ClearDragPreviews(); diff --git a/Assets/Scripts/NetworkPlayerController.cs b/Assets/Scripts/NetworkPlayerController.cs index 9ccc57b..c1a1c6c 100644 --- a/Assets/Scripts/NetworkPlayerController.cs +++ b/Assets/Scripts/NetworkPlayerController.cs @@ -65,9 +65,15 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl public ulong OwnerPlayerId => _ownerPlayerId.Value; + // 중앙 집중식 입력 관리 - 다른 컴포넌트에서 참조 + public PlayerInputActions InputActions => _inputActions; + // 소유자 변경 이벤트 public event Action OnOwnerChanged; + // 입력 시스템 초기화 완료 이벤트 + public event Action OnInputInitialized; + void Awake() { _controller = GetComponent(); @@ -141,6 +147,9 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl _inputActions = new PlayerInputActions(); _inputActions.Enable(); + + // 입력 초기화 완료 이벤트 발생 + OnInputInitialized?.Invoke(); } /// diff --git a/Assets/Scripts/PlayerActionSystem.cs b/Assets/Scripts/PlayerActionSystem.cs index 12e50b9..34ed06c 100644 --- a/Assets/Scripts/PlayerActionSystem.cs +++ b/Assets/Scripts/PlayerActionSystem.cs @@ -17,11 +17,11 @@ namespace Northbound [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 _isInputInitialized = false; // 로컬 플레이어인지 확인 private bool IsLocalPlayer => _networkPlayerController != null && _networkPlayerController.IsLocalPlayer; @@ -49,6 +49,7 @@ namespace Northbound if (_networkPlayerController != null) { _networkPlayerController.OnOwnerChanged += OnOwnerPlayerIdChanged; + _networkPlayerController.OnInputInitialized += TryInitializeInput; } // 이미 로컬 플레이어면 입력 초기화 @@ -63,11 +64,11 @@ namespace Northbound private void TryInitializeInput() { if (!IsLocalPlayer) return; - if (_inputActions != null) return; + if (_isInputInitialized) return; // 이미 초기화됨 + if (_networkPlayerController.InputActions == null) return; // 아직 InputActions가 없음 - _inputActions = new PlayerInputActions(); - _inputActions.Player.Attack.performed += OnAttack; - _inputActions.Enable(); + _isInputInitialized = true; + _networkPlayerController.InputActions.Player.Attack.performed += OnAttack; } public override void OnNetworkDespawn() @@ -75,13 +76,13 @@ namespace Northbound if (_networkPlayerController != null) { _networkPlayerController.OnOwnerChanged -= OnOwnerPlayerIdChanged; - } + _networkPlayerController.OnInputInitialized -= TryInitializeInput; - if (_inputActions != null) - { - _inputActions.Player.Attack.performed -= OnAttack; - _inputActions.Disable(); - _inputActions.Dispose(); + // 입력 이벤트 해제 + if (_networkPlayerController.InputActions != null) + { + _networkPlayerController.InputActions.Player.Attack.performed -= OnAttack; + } } } @@ -126,11 +127,7 @@ namespace Northbound override public void OnDestroy() { - if (_inputActions != null) - { - _inputActions.Dispose(); - } - + // 입력 정리는 NetworkPlayerController에서 담당 base.OnDestroy(); } } diff --git a/Assets/Scripts/PlayerInteraction.cs b/Assets/Scripts/PlayerInteraction.cs index 660d2f3..fc11d98 100644 --- a/Assets/Scripts/PlayerInteraction.cs +++ b/Assets/Scripts/PlayerInteraction.cs @@ -33,7 +33,6 @@ namespace Northbound [Header("Worker")] public Worker assignedWorker; - private PlayerInputActions _inputActions; private IInteractable _currentInteractable; private IInteractable _unavailableInteractable; private Camera _mainCamera; @@ -48,6 +47,7 @@ namespace Northbound private NetworkPlayerController _networkPlayerController; private PlayerStats _playerStats; + private bool _isInputInitialized = false; public bool IsInteracting => _isInteracting; public float WorkPower => _playerStats?.GetManpower() ?? 10f; @@ -69,7 +69,7 @@ namespace Northbound { _animator = GetComponent(); _equipmentSocket = GetComponent(); - + if (rayOrigin == null) rayOrigin = transform; @@ -77,6 +77,7 @@ namespace Northbound if (_networkPlayerController != null) { _networkPlayerController.OnOwnerChanged += OnOwnerPlayerIdChanged; + _networkPlayerController.OnInputInitialized += TryInitializeInput; } // 이미 로컬 플레이어면 입력 초기화 @@ -91,12 +92,12 @@ namespace Northbound private void TryInitializeInput() { if (!IsLocalPlayer) return; - if (_inputActions != null) return; + if (_isInputInitialized) return; // 이미 초기화됨 + if (_networkPlayerController.InputActions == null) return; // 아직 InputActions가 없음 + _isInputInitialized = true; _mainCamera = Camera.main; - _inputActions = new PlayerInputActions(); - _inputActions.Player.Interact.performed += OnInteract; - _inputActions.Enable(); + _networkPlayerController.InputActions.Player.Interact.performed += OnInteract; } public override void OnNetworkDespawn() @@ -104,13 +105,13 @@ namespace Northbound if (_networkPlayerController != null) { _networkPlayerController.OnOwnerChanged -= OnOwnerPlayerIdChanged; - } + _networkPlayerController.OnInputInitialized -= TryInitializeInput; - if (_inputActions != null) - { - _inputActions.Player.Interact.performed -= OnInteract; - _inputActions.Disable(); - _inputActions.Dispose(); + // 입력 이벤트 해제 + if (_networkPlayerController.InputActions != null) + { + _networkPlayerController.InputActions.Player.Interact.performed -= OnInteract; + } } } @@ -402,10 +403,7 @@ namespace Northbound public override void OnDestroy() { - if (_inputActions != null) - { - _inputActions.Dispose(); - } + // 입력 정리는 NetworkPlayerController에서 담당 } } } \ No newline at end of file From 20167a8a52fe3229b786f58008ffd02297c8a11b Mon Sep 17 00:00:00 2001 From: dal4segno Date: Thu, 26 Feb 2026 01:37:25 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=EC=9E=85=EB=A0=A5=20=EB=AA=BB=EB=B0=9B?= =?UTF-8?q?=EB=8A=94=20=EB=B2=84=EA=B7=B8=20=EC=B6=94=EA=B0=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20=EC=9E=85=EB=A0=A5=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EA=B0=95=EC=A0=9C=20=EB=B3=B5=EA=B5=AC=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Scripts/BuildingPlacement.cs | 6 +++ Assets/Scripts/NetworkPlayerController.cs | 46 ++++++++++++++++++++++- Assets/Scripts/PlayerActionSystem.cs | 6 +++ Assets/Scripts/PlayerInteraction.cs | 6 +++ 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/BuildingPlacement.cs b/Assets/Scripts/BuildingPlacement.cs index 60b839b..ae435a5 100644 --- a/Assets/Scripts/BuildingPlacement.cs +++ b/Assets/Scripts/BuildingPlacement.cs @@ -61,6 +61,12 @@ namespace Northbound TryInitializeInput(); } + private void Start() + { + // Start에서 다시 한번 확인 (이벤트 타이밍 문제 해결) + TryInitializeInput(); + } + private void TryInitializeInput() { if (_isInitialized) return; // 이미 초기화됨 diff --git a/Assets/Scripts/NetworkPlayerController.cs b/Assets/Scripts/NetworkPlayerController.cs index c1a1c6c..d282c04 100644 --- a/Assets/Scripts/NetworkPlayerController.cs +++ b/Assets/Scripts/NetworkPlayerController.cs @@ -161,6 +161,28 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl _ownerPlayerId.Value = ownerPlayerId; } + /// + /// 입력 강제 복구 (입력이 멈췄을 때 호출) + /// + public void ForceRecoverInput() + { + if (!IsLocalPlayer) return; + + // InputActions가 없으면 초기화 시도 + if (_inputActions == null) + { + TryInitializeLocalPlayer(); + return; + } + + // InputActions가 있지만 비활성화되어 있으면 활성화 + if (!_inputActions.Player.enabled && _currentHealth.Value > 0) + { + _inputActions.Enable(); + Debug.Log("[NetworkPlayerController] 입력 강제 복구 완료"); + } + } + public override void OnNetworkDespawn() { _currentHealth.OnValueChanged -= OnHealthChanged; @@ -182,6 +204,10 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl // 서버 측 이동 입력 저장 private Vector2 _serverMoveInput; + // 입력 복구 체크 + private float _inputRecoveryCheckInterval = 1f; // 1초마다 체크 + private float _inputRecoveryCheckTimer; + void Update() { // 서버에서 체력 자연 회복 처리 @@ -193,8 +219,24 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl // 로컬 플레이어만 입력 처리 if (!IsLocalPlayer) return; - // 입력 시스템이 초기화되지 않았으면 스킵 - if (_inputActions == null) return; + // 입력 시스템이 초기화되지 않았으면 초기화 시도 + if (_inputActions == null) + { + TryInitializeLocalPlayer(); + return; + } + + // 주기적으로 입력 상태 확인 및 자동 복구 (살아있는데 입력이 비활성화된 경우) + _inputRecoveryCheckTimer += Time.deltaTime; + if (_inputRecoveryCheckTimer >= _inputRecoveryCheckInterval) + { + _inputRecoveryCheckTimer = 0f; + if (_currentHealth.Value > 0 && !_inputActions.Player.enabled) + { + Debug.LogWarning("[NetworkPlayerController] 입력이 비활성화되어 있음 - 자동 복구"); + _inputActions.Enable(); + } + } // 죽었으면 이동 불가 if (_currentHealth.Value <= 0) return; diff --git a/Assets/Scripts/PlayerActionSystem.cs b/Assets/Scripts/PlayerActionSystem.cs index 34ed06c..febeabd 100644 --- a/Assets/Scripts/PlayerActionSystem.cs +++ b/Assets/Scripts/PlayerActionSystem.cs @@ -56,6 +56,12 @@ namespace Northbound TryInitializeInput(); } + private void Start() + { + // Start에서 다시 한번 확인 (이벤트 타이밍 문제 해결) + TryInitializeInput(); + } + private void OnOwnerPlayerIdChanged(ulong newOwnerId) { TryInitializeInput(); diff --git a/Assets/Scripts/PlayerInteraction.cs b/Assets/Scripts/PlayerInteraction.cs index fc11d98..6824440 100644 --- a/Assets/Scripts/PlayerInteraction.cs +++ b/Assets/Scripts/PlayerInteraction.cs @@ -84,6 +84,12 @@ namespace Northbound TryInitializeInput(); } + private void Start() + { + // Start에서 다시 한번 확인 (이벤트 타이밍 문제 해결) + TryInitializeInput(); + } + private void OnOwnerPlayerIdChanged(ulong newOwnerId) { TryInitializeInput();