diff --git a/Assets/01_Scenes/GameScene.unity b/Assets/01_Scenes/GameScene.unity index 39ca18f..ba00db3 100644 --- a/Assets/01_Scenes/GameScene.unity +++ b/Assets/01_Scenes/GameScene.unity @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4cc99d07b6c5fb4d3458f077f8860975ea99044e1a24f864f0e4d11e3dadde2 -size 52391 +oid sha256:327b584e2b21d874e92693160b232eaa458bc044c34e66d9fa273295aef28301 +size 52829 diff --git a/Assets/02_Scripts/Player/PlayerController.cs b/Assets/02_Scripts/Player/PlayerController.cs index 2f4f059..c740760 100644 --- a/Assets/02_Scripts/Player/PlayerController.cs +++ b/Assets/02_Scripts/Player/PlayerController.cs @@ -6,7 +6,7 @@ public class PlayerController : MonoBehaviour { [Header("Movement")] [SerializeField] private float _moveSpeed = 5f; - [SerializeField] private string _walkAnimationState = "Walk"; + [SerializeField] private string _walkAnimationState = "Run"; private float _moveInputX = 0f; private string _activeBaseState; private bool _isInActionAnimation; @@ -44,6 +44,14 @@ public class PlayerController : MonoBehaviour private ActionData _movementLockAction; private ActionData _facingLockAction; + [Header("Ground Pound")] + [SerializeField] private ActionData _groundPoundData; + [SerializeField] private string _groundPoundFallAnimationState = "GroundSlamLoop"; + [SerializeField] private string _groundPoundImpactAnimationState = "GroundSlamEnd"; + [SerializeField] private float _groundPoundWindupDuration = 0.15f; + [SerializeField] private float _groundPoundFallSpeed = 25f; + private bool _isGroundPounding; + [Header("Motion")] [SerializeField] private ComboNode _dashRootNode; [SerializeField] private ComboNode _rollRootNode; @@ -218,7 +226,15 @@ private void OnJumpInput() private void OnPunchInput() => HandleComboInput(ComboInputType.Punch); private void OnKickInput() => HandleComboInput(ComboInputType.Kick); - private void OnGrabSmashInput() => HandleComboInput(ComboInputType.Grab); + private void OnGrabSmashInput() + { + if (!_isGrounded && _groundPoundData != null) + { + PerformGroundPound(); + return; + } + HandleComboInput(ComboInputType.Grab); + } private void OnDashInput() => ExecuteMotionNode(_dashRootNode); private void OnRollInput() => ExecuteMotionNode(_rollRootNode); private void OnBackDashInput() => ExecuteMotionNode(_backDashRootNode); @@ -400,6 +416,100 @@ private async Awaitable MotionRoutine(ActionData data, CancellationToken token) } } + private async void PerformGroundPound() + { + if (_groundPoundData == null) return; + + CancelAttack(); + CancelAndDispose(ref _motionCts); + _motionCts = CancellationTokenSource.CreateLinkedTokenSource(destroyCancellationToken); + CancellationToken token = _motionCts.Token; + + _isMotionActive = true; + _isGroundPounding = true; + _attackStartTime = Time.time; + _lastAttackGizmoData = _groundPoundData; + _hitFired = false; + + ClearActionLocks(); + FaceMotionDirection(_groundPoundData); + PlayActionAnimation(_groundPoundData); + LockMovementIfNeeded(_groundPoundData); + LockFacingIfNeeded(_groundPoundData); + + float fallSpeed = _groundPoundFallSpeed > 0f + ? _groundPoundFallSpeed + : Mathf.Max(Mathf.Abs(_groundPoundData.Velocity.y), 25f); + + try + { + // 1) 윈드업: 짧게 공중 정지 후 슬램. ApplyGravity는 _isGroundPounding 플래그로 우회. + float windupElapsed = 0f; + while (windupElapsed < _groundPoundWindupDuration && !_isGrounded) + { + token.ThrowIfCancellationRequested(); + _rb.linearVelocity = Vector2.zero; + windupElapsed += Time.deltaTime; + await Awaitable.NextFrameAsync(token); + } + + // 2) 낙하 애니메이션으로 전환 (루프). 윈드업이 끝났을 때만 실행. + if (_anim != null && !string.IsNullOrEmpty(_groundPoundFallAnimationState) && !_isGrounded) + { + CancelAndDispose(ref _animationSpeedCts); + _anim.speed = 1f; + _anim.Play(_groundPoundFallAnimationState); + _anim.Update(0f); + } + + // 3) 지면 닿을 때까지 강제 낙하. + while (!_isGrounded) + { + token.ThrowIfCancellationRequested(); + _rb.linearVelocity = new Vector2(0f, -fallSpeed); + await Awaitable.NextFrameAsync(token); + } + + // 4) 착지 순간 임팩트 애니메이션으로 전환 (낙하 거리 불일치 해소) + if (_anim != null && !string.IsNullOrEmpty(_groundPoundImpactAnimationState)) + { + CancelAndDispose(ref _animationSpeedCts); + _anim.speed = 1f; + _anim.Play(_groundPoundImpactAnimationState); + _anim.Update(0f); + } + + // 착지 임팩트 + if (_groundPoundData.HasHit && _attackHitbox != null) + { + ActivateAttackHitbox(_groundPoundData); + + float hitDuration = Mathf.Max(_groundPoundData.HitDuration, 0.05f); + await Awaitable.WaitForSecondsAsync(hitDuration, token); + + _attackHitbox.Deactivate(); + } + + // 후딜 + float recovery = Mathf.Max(_groundPoundData.MotionDuration, 0.15f); + await Awaitable.WaitForSecondsAsync(recovery, token); + + PlayIdleAnimation(); + } + catch (System.OperationCanceledException) + { + _attackHitbox?.Deactivate(); + } + finally + { + _isGroundPounding = false; + _isMotionActive = false; + ClearActionLocks(); + // 그라운드 파운드 직후 Land 애니메이션 중복 재생 방지 + _wasGroundedLastFrame = _isGrounded; + } + } + private void CancelAttack() { CancelAndDispose(ref _attackCts); @@ -996,6 +1106,9 @@ private async Awaitable WaitForActionEnd(ActionData data, CancellationToken toke private void ApplyGravity() { + // 그라운드 파운드는 _maxFallSpeed에 막히지 않도록 자체 속도를 그대로 유지한다. + if (_isGroundPounding) return; + float vy = _rb.linearVelocity.y; if (_isGrounded && vy <= 0f) diff --git a/Assets/03_Character/WhiteMan/Animations/Animators/PlayerAnimator.controller b/Assets/03_Character/WhiteMan/Animations/Animators/PlayerAnimator.controller index 0ad6e54..f3c0296 100644 --- a/Assets/03_Character/WhiteMan/Animations/Animators/PlayerAnimator.controller +++ b/Assets/03_Character/WhiteMan/Animations/Animators/PlayerAnimator.controller @@ -26,6 +26,32 @@ AnimatorState: m_MirrorParameter: m_CycleOffsetParameter: m_TimeParameter: +--- !u!1102 &-9013432840856765531 +AnimatorState: + serializedVersion: 6 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: GroundSlamEnd + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: 43a3597f5e5a1b646b018598197b92a6, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: --- !u!1102 &-8997814140322552695 AnimatorState: serializedVersion: 6 @@ -832,6 +858,32 @@ AnimatorState: m_MirrorParameter: m_CycleOffsetParameter: m_TimeParameter: +--- !u!1102 &-3426527744796024442 +AnimatorState: + serializedVersion: 6 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: GroundSlamLoop + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: dfe5fd923a41a494399e84b126452f41, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: --- !u!1102 &-3380229326876372857 AnimatorState: serializedVersion: 6 @@ -2076,6 +2128,32 @@ AnimatorState: m_MirrorParameter: m_CycleOffsetParameter: m_TimeParameter: +--- !u!1102 &4277843955758000149 +AnimatorState: + serializedVersion: 6 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: GroundSlamStart + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: 97161820e52e20b40bedabdf43c7d6f5, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: --- !u!1102 &4302945159732726270 AnimatorState: serializedVersion: 6 @@ -3150,6 +3228,15 @@ AnimatorStateMachine: - serializedVersion: 1 m_State: {fileID: -4203153932972611291} m_Position: {x: 5280, y: 90, z: 0} + - serializedVersion: 1 + m_State: {fileID: 4277843955758000149} + m_Position: {x: 2480, y: 440, z: 0} + - serializedVersion: 1 + m_State: {fileID: -3426527744796024442} + m_Position: {x: 2690, y: 440, z: 0} + - serializedVersion: 1 + m_State: {fileID: -9013432840856765531} + m_Position: {x: 2900, y: 440, z: 0} m_ChildStateMachines: [] m_AnyStateTransitions: [] m_EntryTransitions: [] diff --git a/Assets/03_Character/WhiteMan/Animations/GroundSlam.anim b/Assets/03_Character/WhiteMan/Animations/GroundSlam.anim deleted file mode 100644 index a42a865..0000000 --- a/Assets/03_Character/WhiteMan/Animations/GroundSlam.anim +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:22f27187cc71b6c95f55963253dbb4687cb39f04016bad9d9b6e28516bf41add -size 3374 diff --git a/Assets/03_Character/WhiteMan/Animations/GroundSlam_End.anim b/Assets/03_Character/WhiteMan/Animations/GroundSlam_End.anim new file mode 100644 index 0000000..f0abf18 --- /dev/null +++ b/Assets/03_Character/WhiteMan/Animations/GroundSlam_End.anim @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:26e1d0728bb58791819f857ccfa146ba5c90e43fcb0e6987019e9d373b92b34d +size 2852 diff --git a/Assets/03_Character/WhiteMan/Animations/GroundSlam.anim.meta b/Assets/03_Character/WhiteMan/Animations/GroundSlam_End.anim.meta similarity index 100% rename from Assets/03_Character/WhiteMan/Animations/GroundSlam.anim.meta rename to Assets/03_Character/WhiteMan/Animations/GroundSlam_End.anim.meta diff --git a/Assets/03_Character/WhiteMan/Animations/GroundSlam_Loop.anim b/Assets/03_Character/WhiteMan/Animations/GroundSlam_Loop.anim new file mode 100644 index 0000000..e1d3826 --- /dev/null +++ b/Assets/03_Character/WhiteMan/Animations/GroundSlam_Loop.anim @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c299961d1ed44f237e4a5743a3a81dc6cdd55f038902049a5e008c2ed13a2d3 +size 1977 diff --git a/Assets/03_Character/WhiteMan/Animations/GroundSlam_Loop.anim.meta b/Assets/03_Character/WhiteMan/Animations/GroundSlam_Loop.anim.meta new file mode 100644 index 0000000..be85096 --- /dev/null +++ b/Assets/03_Character/WhiteMan/Animations/GroundSlam_Loop.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dfe5fd923a41a494399e84b126452f41 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/03_Character/WhiteMan/Animations/GroundSlam_Start.anim b/Assets/03_Character/WhiteMan/Animations/GroundSlam_Start.anim new file mode 100644 index 0000000..bdf9310 --- /dev/null +++ b/Assets/03_Character/WhiteMan/Animations/GroundSlam_Start.anim @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f13119ae859400fa9f419f1a91a04d655e8c7f9f7a3835b7a1787550ee2446b0 +size 2150 diff --git a/Assets/03_Character/WhiteMan/Animations/GroundSlam_Start.anim.meta b/Assets/03_Character/WhiteMan/Animations/GroundSlam_Start.anim.meta new file mode 100644 index 0000000..43cd6a4 --- /dev/null +++ b/Assets/03_Character/WhiteMan/Animations/GroundSlam_Start.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 97161820e52e20b40bedabdf43c7d6f5 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/05_Data/Attack/GrabGroundSmash.asset b/Assets/05_Data/Attack/GrabGroundSmash.asset deleted file mode 100644 index 42dc8b0..0000000 --- a/Assets/05_Data/Attack/GrabGroundSmash.asset +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:838beed0510fe457207fdeba3d7b9592aac4f1cf30ce9dc22c975e39c0ed3832 -size 3897 diff --git a/Assets/05_Data/Attack/GroundSlam.asset b/Assets/05_Data/Attack/GroundSlam.asset new file mode 100644 index 0000000..82e159d --- /dev/null +++ b/Assets/05_Data/Attack/GroundSlam.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9eee5937d8bb9fd48a15c5de2b7a384bd397886c6661198444b2e42af2da4b78 +size 3899 diff --git a/Assets/05_Data/Attack/GrabGroundSmash.asset.meta b/Assets/05_Data/Attack/GroundSlam.asset.meta similarity index 100% rename from Assets/05_Data/Attack/GrabGroundSmash.asset.meta rename to Assets/05_Data/Attack/GroundSlam.asset.meta