From 733ea30631bc90f61ffb4fbc78b9e94770b95d44 Mon Sep 17 00:00:00 2001 From: Dal4segno Date: Sun, 18 Jan 2026 21:40:29 +0900 Subject: [PATCH] =?UTF-8?q?=EC=97=B0=EC=86=8D=20=EC=95=A1=EC=85=98=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EB=B0=8F=20=EA=B4=80=EB=A0=A8=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/New Animator Controller.controller | 50 ++++++++++++++---- Assets/Prefabs/MiningActionData.asset | 2 + Assets/Prefabs/Player.prefab | 11 +++- Assets/Scripts/Player/MiningActionData.cs | 2 + Assets/Scripts/Player/PlayerActionData.cs | 7 ++- Assets/Scripts/Player/PlayerActionHandler.cs | 31 +++++++++-- .../Scripts/Player/PlayerNetworkController.cs | 52 ++++++++++++++++++- 7 files changed, 138 insertions(+), 17 deletions(-) diff --git a/Assets/New Animator Controller.controller b/Assets/New Animator Controller.controller index 593a6df..c891108 100644 --- a/Assets/New Animator Controller.controller +++ b/Assets/New Animator Controller.controller @@ -85,6 +85,12 @@ AnimatorController: m_DefaultInt: 0 m_DefaultBool: 0 m_Controller: {fileID: 9100000} + - m_Name: ActionSpeed + m_Type: 1 + m_DefaultFloat: 1 + m_DefaultInt: 0 + m_DefaultBool: 0 + m_Controller: {fileID: 9100000} m_AnimatorLayers: - serializedVersion: 5 m_Name: Base Layer @@ -108,7 +114,8 @@ AnimatorState: m_Name: Locomotion m_Speed: 1 m_CycleOffset: 0 - m_Transitions: [] + m_Transitions: + - {fileID: 5226680357144867497} m_StateMachineBehaviours: [] m_Position: {x: 50, y: 50, z: 0} m_IKOnFeet: 0 @@ -156,7 +163,7 @@ AnimatorState: m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_Name: Melee_1H_Attack_Chop + m_Name: 2H Attack m_Speed: 1 m_CycleOffset: 0 m_Transitions: @@ -166,13 +173,13 @@ AnimatorState: m_IKOnFeet: 0 m_WriteDefaultValues: 1 m_Mirror: 0 - m_SpeedParameterActive: 0 + m_SpeedParameterActive: 1 m_MirrorParameterActive: 0 m_CycleOffsetParameterActive: 0 m_TimeParameterActive: 0 - m_Motion: {fileID: 4043807988371827432, guid: 79b3d1d24644f7d4987c6bdd614dd439, type: 3} + m_Motion: {fileID: -3183232228448455838, guid: 79b3d1d24644f7d4987c6bdd614dd439, type: 3} m_Tag: Attack - m_SpeedParameter: + m_SpeedParameter: ActionSpeed m_MirrorParameter: m_CycleOffsetParameter: m_TimeParameter: @@ -243,14 +250,39 @@ AnimatorStateTransition: m_Mute: 0 m_IsExit: 0 serializedVersion: 3 - m_TransitionDuration: 0.25 + m_TransitionDuration: 0.1 m_TransitionOffset: 0 - m_ExitTime: 0.765625 + m_ExitTime: 0.9 m_HasExitTime: 1 m_HasFixedDuration: 1 m_InterruptionSource: 0 m_OrderedInterruption: 1 m_CanTransitionToSelf: 1 +--- !u!1101 &5226680357144867497 +AnimatorStateTransition: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_Conditions: + - m_ConditionMode: 1 + m_ConditionEvent: Attack + m_EventTreshold: 0 + m_DstStateMachine: {fileID: 0} + m_DstState: {fileID: 1042583441410514574} + m_Solo: 0 + m_Mute: 0 + m_IsExit: 0 + serializedVersion: 3 + m_TransitionDuration: 0.1 + m_TransitionOffset: 0 + m_ExitTime: 0.82954544 + m_HasExitTime: 0 + m_HasFixedDuration: 1 + m_InterruptionSource: 0 + m_OrderedInterruption: 1 + m_CanTransitionToSelf: 1 --- !u!1101 &7110953658697184367 AnimatorStateTransition: m_ObjectHideFlags: 1 @@ -359,6 +391,6 @@ AnimatorStateTransition: m_ExitTime: 0.75 m_HasExitTime: 0 m_HasFixedDuration: 1 - m_InterruptionSource: 0 + m_InterruptionSource: 1 m_OrderedInterruption: 1 - m_CanTransitionToSelf: 1 + m_CanTransitionToSelf: 0 diff --git a/Assets/Prefabs/MiningActionData.asset b/Assets/Prefabs/MiningActionData.asset index e206384..b681dad 100644 --- a/Assets/Prefabs/MiningActionData.asset +++ b/Assets/Prefabs/MiningActionData.asset @@ -15,4 +15,6 @@ MonoBehaviour: actionName: Mining duration: 1 animTrigger: Attack + impactDelay: 0 + baseSpeed: 5 damage: 50 diff --git a/Assets/Prefabs/Player.prefab b/Assets/Prefabs/Player.prefab index a857d8d..4a6ec54 100644 --- a/Assets/Prefabs/Player.prefab +++ b/Assets/Prefabs/Player.prefab @@ -305,7 +305,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} m_Name: m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject - GlobalObjectIdHash: 856270328 + GlobalObjectIdHash: 4268073897 InScenePlacedSourceGlobalObjectIdHash: 0 DeferredDespawnTick: 0 Ownership: 1 @@ -422,7 +422,14 @@ MonoBehaviour: NetworkAnimatorExpanded: 0 AuthorityMode: 0 m_Animator: {fileID: 5870045807328036684} - TransitionStateInfoList: [] + TransitionStateInfoList: + - IsCrossFadeExit: 0 + Layer: 0 + OriginatingState: -2089788878 + DestinationState: -437043462 + TransitionDuration: 0.1 + TriggerNameHash: 1080829965 + TransitionIndex: 0 AnimatorParameterEntries: ParameterEntries: - name: MoveSpeed diff --git a/Assets/Scripts/Player/MiningActionData.cs b/Assets/Scripts/Player/MiningActionData.cs index cf4dc6c..a8b102f 100644 --- a/Assets/Scripts/Player/MiningActionData.cs +++ b/Assets/Scripts/Player/MiningActionData.cs @@ -7,6 +7,8 @@ public class MiningActionData : PlayerActionData public override void ExecuteEffect(GameObject performer, GameObject target) { + if(target == null) return; + if (target.TryGetComponent(out var block)) { // 서버 RPC 호출은 블록 내부의 로직을 그대로 사용합니다. diff --git a/Assets/Scripts/Player/PlayerActionData.cs b/Assets/Scripts/Player/PlayerActionData.cs index a7745ef..53cde1a 100644 --- a/Assets/Scripts/Player/PlayerActionData.cs +++ b/Assets/Scripts/Player/PlayerActionData.cs @@ -4,7 +4,12 @@ public abstract class PlayerActionData : ScriptableObject { public string actionName; public float duration; // 액션 자체가 시간을 가짐 - public string animTrigger; + public string animTrigger; + public float impactDelay; // 애니메이션 원본 길이 기준 타격 지연 시간 + public float baseSpeed = 1f; // 기본 재생 속도 (1.0 = 100%) + + [Header("Repeat Settings")] + public bool canRepeat = true; // [추가] 꾹 눌렀을 때 반복할지 여부 // 대상(target)은 있을 수도 있고(채광), 없을 수도 있음(회복/대쉬) public abstract void ExecuteEffect(GameObject performer, GameObject target); diff --git a/Assets/Scripts/Player/PlayerActionHandler.cs b/Assets/Scripts/Player/PlayerActionHandler.cs index 3abb903..35ef0ad 100644 --- a/Assets/Scripts/Player/PlayerActionHandler.cs +++ b/Assets/Scripts/Player/PlayerActionHandler.cs @@ -44,11 +44,36 @@ public class PlayerActionHandler : NetworkBehaviour private IEnumerator ActionRoutine(PlayerActionData data, GameObject target) { _isBusy = true; - _animator.SetTrigger(data.animTrigger); - data.ExecuteEffect(gameObject, target); // 로직 실행 + // 1. 애니메이션 재생 속도 결정 + // (나중에 캐릭터 스탯이나 버프에 따라 이 값을 추가로 계산할 수 있습니다) + float finalSpeed = data.baseSpeed; + _animator.SetFloat("ActionSpeed", finalSpeed); + + // 2. 애니메이션 실행 + if (!string.IsNullOrEmpty(data.animTrigger)) + _animator.SetTrigger(data.animTrigger); + + // 3. 속도에 맞춰 보정된 타격 지연 시간 계산 + // 공식: 실제 대기 시간 = 설정된 지연 시간 / 재생 속도 + float adjustedImpactDelay = data.impactDelay / finalSpeed; + yield return new WaitForSeconds(adjustedImpactDelay); + + // 4. 타격 효과 실행 + if (target != null) + { + data.ExecuteEffect(gameObject, target); + } + + // 5. 속도에 맞춰 보정된 나머지 시간 계산 + float adjustedTotalDuration = data.duration / finalSpeed; + float remainingTime = adjustedTotalDuration - adjustedImpactDelay; + + if (remainingTime > 0) + { + yield return new WaitForSeconds(remainingTime); + } - yield return new WaitForSeconds(data.duration); _isBusy = false; } } \ No newline at end of file diff --git a/Assets/Scripts/Player/PlayerNetworkController.cs b/Assets/Scripts/Player/PlayerNetworkController.cs index b2e201e..86747eb 100644 --- a/Assets/Scripts/Player/PlayerNetworkController.cs +++ b/Assets/Scripts/Player/PlayerNetworkController.cs @@ -58,6 +58,9 @@ public class PlayerNetworkController : NetworkBehaviour private bool _isGrounded; private bool _isHoldingInteract = false; + private bool _isHoldingAction = false; + private bool _hasExecutedOnce = false; // 단발성 액션이 중복 실행되지 않도록 방지 + // 현재 플레이어가 어떤 행동을 하고 있는지 나타내는 상태 public enum ActionState { Idle, Busy } private ActionState _currentState = ActionState.Idle; @@ -102,8 +105,17 @@ public class PlayerNetworkController : NetworkBehaviour _inputActions.Player.Action.performed += ctx => OnActionInput(); _inputActions.Player.Interact.performed += ctx => OnInteractTap(); // 탭 상호작용 - _inputActions.Player.Interact.started += ctx => _isHoldingInteract = true; - _inputActions.Player.Interact.canceled += ctx => _isHoldingInteract = false; + // started: 버튼을 누르는 순간 즉시 첫 번째 시도 + _inputActions.Player.Action.started += ctx => { + _isHoldingAction = true; + _hasExecutedOnce = false; // 누르기 시작할 때 초기화 + TryExecuteAction(); + }; + + // canceled: 버튼을 떼는 순간 + _inputActions.Player.Action.canceled += ctx => { + _isHoldingAction = false; + }; _inputActions.Player.Select1.performed += ctx => _inventory.ChangeSelectedSlotRpc(0); _inputActions.Player.Select2.performed += ctx => _inventory.ChangeSelectedSlotRpc(1); @@ -147,6 +159,12 @@ public class PlayerNetworkController : NetworkBehaviour _lastRevealTime = Time.time; RevealSurroundings(); } + + // 버튼을 꾹 누르고 있고, 아직 액션이 진행 중이 아닐 때만 반복 체크 + if (_isHoldingAction && !_actionHandler.IsBusy) + { + HandleContinuousAction(); + } } // --- 이동 관련 로직 (기존 유지) --- @@ -210,6 +228,7 @@ public class PlayerNetworkController : NetworkBehaviour else { Debug.Log("조준된 블록이 없음 (하이라이트 확인 필요)"); + _actionHandler.PerformAction(selectedItem.toolAction, null); } } } @@ -465,6 +484,35 @@ public class PlayerNetworkController : NetworkBehaviour return closest; } + private void HandleContinuousAction() + { + ItemData selectedItem = _inventory.GetSelectedItemData(); + if (selectedItem == null || !selectedItem.isTool || selectedItem.toolAction == null) return; + + // [핵심] 반복 가능한 액션일 때만 Update에서 재실행 + if (selectedItem.toolAction.canRepeat) + { + TryExecuteAction(); + } + } + + private void TryExecuteAction() + { + if (_actionHandler.IsBusy) return; + + ItemData selectedItem = _inventory.GetSelectedItemData(); + if (selectedItem != null && selectedItem.isTool && selectedItem.toolAction != null) + { + // 단발성 액션인데 이미 한 번 실행했다면 스킵 + if (!selectedItem.toolAction.canRepeat && _hasExecutedOnce) return; + + GameObject target = _lastHighlightedBlock != null ? _lastHighlightedBlock.gameObject : null; + _actionHandler.PerformAction(selectedItem.toolAction, target); + + _hasExecutedOnce = true; // 실행 기록 저장 + } + } + private void OnDrawGizmos() { if (!Application.isPlaying || !IsOwner) return;