diff --git a/Assets/01_Scenes/GameScene.unity b/Assets/01_Scenes/GameScene.unity index 5842a6b..c818816 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:df589de703853ce0d54bf577261b352b30ff6ab7c5e114ef76b34e1b4f3e036e -size 52274 +oid sha256:6ab3de3510ee7344cd93232b2d75034babd912117045b9a1eafa81030f312d38 +size 52364 diff --git a/Assets/02_Scripts/Combat/ActionData.cs b/Assets/02_Scripts/Combat/ActionData.cs index cdead20..f2d5a45 100644 --- a/Assets/02_Scripts/Combat/ActionData.cs +++ b/Assets/02_Scripts/Combat/ActionData.cs @@ -40,4 +40,13 @@ public class ActionData : ScriptableObject public float HitPositionCorrectionDuration = 0.08f; public bool CorrectHitTargetY; public string HitReactionAnimationState; + + [Header("Grab")] + public bool IsGrab; + public Vector2 GrabOffset = new Vector2(0.6f, 0f); + public AnimationCurve GrabOffsetXCurve = AnimationCurve.Linear(0f, 1f, 1f, 1f); + public AnimationCurve GrabOffsetYCurve = AnimationCurve.Linear(0f, 1f, 1f, 1f); + public string GrabbedAnimationState; + public float GrabSearchRadius = 2f; + public float GrabRange = 0.5f; } diff --git a/Assets/02_Scripts/Combat/AttackHitbox.cs b/Assets/02_Scripts/Combat/AttackHitbox.cs index fe58e50..4d81bc8 100644 --- a/Assets/02_Scripts/Combat/AttackHitbox.cs +++ b/Assets/02_Scripts/Combat/AttackHitbox.cs @@ -4,6 +4,8 @@ [RequireComponent(typeof(CircleCollider2D))] public class AttackHitbox : MonoBehaviour { + public event System.Action OnHit; + private CircleCollider2D _collider; private int _damage; private Vector2 _hitVelocity; @@ -75,6 +77,7 @@ private void TryDamage(Collider2D other) _alreadyHit.Add(target); Vector2? targetPosition = GetCorrectionTargetPosition(other); target.TakeDamage(_damage, _hitVelocity, _hitReactionState, targetPosition, _correctHitTargetY, _hitPositionSolidMask, _hitPositionCorrectionDuration); + OnHit?.Invoke(target); } private Vector2? GetCorrectionTargetPosition(Collider2D other) diff --git a/Assets/02_Scripts/Enemy/Enemy.cs b/Assets/02_Scripts/Enemy/Enemy.cs index 680da23..86e6f73 100644 --- a/Assets/02_Scripts/Enemy/Enemy.cs +++ b/Assets/02_Scripts/Enemy/Enemy.cs @@ -37,6 +37,9 @@ public class Enemy : MonoBehaviour, IDamageable private float _hitPositionCorrectionDuration; private Vector2 _hitPositionCorrectionStart; private Vector2 _hitPositionCorrectionTarget; + private bool _isGrabbed; + private Vector2 _grabTargetPosition; + private int _grabSolidMask; private void Awake() { @@ -66,6 +69,14 @@ private void FixedUpdate() { if (_rb != null) { + if (_isGrabbed) + { + _rb.linearVelocity = Vector2.zero; + _rb.MovePosition(_grabTargetPosition); + _lastVelocity = Vector2.zero; + return; + } + ApplySmoothHitPositionCorrection(); _lastVelocity = _rb.linearVelocity; } @@ -75,6 +86,8 @@ public void TakeDamage(int amount, Vector2 hitVelocity = default, string hitReac { if (_currentHealth <= 0) return; + _isGrabbed = false; + _currentHealth -= amount; Debug.Log($"{name} 피격: -{amount} (HP: {_currentHealth}/{_maxHealth})"); @@ -109,6 +122,39 @@ public void TakeDamage(int amount, Vector2 hitVelocity = default, string hitReac Die(); } + public void BeginGrab(string grabbedAnimationState, int solidMask) + { + if (_currentHealth <= 0) return; + + _isGrabbed = true; + _grabSolidMask = solidMask; + _isHitPositionCorrecting = false; + _hitReactionTimer = 0f; + _lastVelocity = Vector2.zero; + + if (_rb != null) + { + _rb.linearVelocity = Vector2.zero; + _grabTargetPosition = _rb.position; + } + + if (_anim != null && !string.IsNullOrEmpty(grabbedAnimationState)) + _anim.Play(grabbedAnimationState); + } + + public void UpdateGrabPosition(Vector2 position) + { + if (!_isGrabbed || _rb == null) return; + + _rb.linearVelocity = Vector2.zero; + _grabTargetPosition = GetSafeHitTargetPosition(position, _grabSolidMask); + } + + public void EndGrab() + { + _isGrabbed = false; + } + private void OnCollisionEnter2D(Collision2D collision) { UpdateGroundedState(collision); diff --git a/Assets/02_Scripts/Input/GameInput.cs b/Assets/02_Scripts/Input/GameInput.cs index 43f38c8..7e851e2 100644 --- a/Assets/02_Scripts/Input/GameInput.cs +++ b/Assets/02_Scripts/Input/GameInput.cs @@ -154,6 +154,15 @@ public @GameInput() ""processors"": """", ""interactions"": """", ""initialStateCheck"": false + }, + { + ""name"": ""GrabSmash"", + ""type"": ""Button"", + ""id"": ""c42f3219-3aca-4678-8a86-b74565af3747"", + ""expectedControlType"": """", + ""processors"": """", + ""interactions"": """", + ""initialStateCheck"": false } ], ""bindings"": [ @@ -277,6 +286,17 @@ public @GameInput() ""action"": ""BackDash"", ""isComposite"": false, ""isPartOfComposite"": false + }, + { + ""name"": """", + ""id"": ""8635811d-29ac-4047-a3f7-d7a72f7e362e"", + ""path"": ""/g"", + ""interactions"": """", + ""processors"": """", + ""groups"": """", + ""action"": ""GrabSmash"", + ""isComposite"": false, + ""isPartOfComposite"": false } ] } @@ -292,6 +312,7 @@ public @GameInput() m_Player_Dash = m_Player.FindAction("Dash", throwIfNotFound: true); m_Player_Roll = m_Player.FindAction("Roll", throwIfNotFound: true); m_Player_BackDash = m_Player.FindAction("BackDash", throwIfNotFound: true); + m_Player_GrabSmash = m_Player.FindAction("GrabSmash", throwIfNotFound: true); } ~@GameInput() @@ -379,6 +400,7 @@ public int FindBinding(InputBinding bindingMask, out InputAction action) private readonly InputAction m_Player_Dash; private readonly InputAction m_Player_Roll; private readonly InputAction m_Player_BackDash; + private readonly InputAction m_Player_GrabSmash; /// /// Provides access to input actions defined in input action map "Player". /// @@ -419,6 +441,10 @@ public struct PlayerActions /// public InputAction @BackDash => m_Wrapper.m_Player_BackDash; /// + /// Provides access to the underlying input action "Player/GrabSmash". + /// + public InputAction @GrabSmash => m_Wrapper.m_Player_GrabSmash; + /// /// Provides access to the underlying input action map instance. /// public InputActionMap Get() { return m_Wrapper.m_Player; } @@ -465,6 +491,9 @@ public void AddCallbacks(IPlayerActions instance) @BackDash.started += instance.OnBackDash; @BackDash.performed += instance.OnBackDash; @BackDash.canceled += instance.OnBackDash; + @GrabSmash.started += instance.OnGrabSmash; + @GrabSmash.performed += instance.OnGrabSmash; + @GrabSmash.canceled += instance.OnGrabSmash; } /// @@ -497,6 +526,9 @@ private void UnregisterCallbacks(IPlayerActions instance) @BackDash.started -= instance.OnBackDash; @BackDash.performed -= instance.OnBackDash; @BackDash.canceled -= instance.OnBackDash; + @GrabSmash.started -= instance.OnGrabSmash; + @GrabSmash.performed -= instance.OnGrabSmash; + @GrabSmash.canceled -= instance.OnGrabSmash; } /// @@ -586,5 +618,12 @@ public interface IPlayerActions /// /// void OnBackDash(InputAction.CallbackContext context); + /// + /// Method invoked when associated input action "GrabSmash" is either , or . + /// + /// + /// + /// + void OnGrabSmash(InputAction.CallbackContext context); } } diff --git a/Assets/02_Scripts/Managers/InputManager.cs b/Assets/02_Scripts/Managers/InputManager.cs index 9722f66..d62a485 100644 --- a/Assets/02_Scripts/Managers/InputManager.cs +++ b/Assets/02_Scripts/Managers/InputManager.cs @@ -15,6 +15,7 @@ public class InputManager : MonoBehaviour, GameInput.IPlayerActions public event Action OnDash_Event; public event Action OnRoll_Event; public event Action OnBackDash_Event; + public event Action OnGrabSmash_Event; private void Awake() { @@ -77,4 +78,9 @@ public void OnBackDash(InputAction.CallbackContext ctx) OnBackDash_Event?.Invoke(); } + public void OnGrabSmash(InputAction.CallbackContext ctx) + { + if (ctx.phase == InputActionPhase.Started) + OnGrabSmash_Event?.Invoke(); + } } diff --git a/Assets/02_Scripts/Player/PlayerController.cs b/Assets/02_Scripts/Player/PlayerController.cs index 34e9c08..f534369 100644 --- a/Assets/02_Scripts/Player/PlayerController.cs +++ b/Assets/02_Scripts/Player/PlayerController.cs @@ -35,6 +35,7 @@ public class PlayerController : MonoBehaviour [SerializeField] private ComboNode _dashRootNode; [SerializeField] private ComboNode _rollRootNode; [SerializeField] private ComboNode _backDashRootNode; + [SerializeField] private ComboNode _grabSmashRootNode; private readonly Dictionary _motionCooldownTimers = new(); private readonly List _motionCooldownKeys = new(); @@ -59,11 +60,14 @@ public class PlayerController : MonoBehaviour private CancellationTokenSource _attackCts; private CancellationTokenSource _motionCts; private CancellationTokenSource _animationSpeedCts; + private CancellationTokenSource _actionVelocityCts; + private bool _isMotionActive; private ActionData _lastAttackGizmoData; [SerializeField] private float _hitGizmoFadeDuration = 0.5f; private ActionData _lastHitData; private Vector2 _lastHitCenter; private float _lastHitTime = -1f; + private Enemy _lastHitEnemy; [Header("Debug")] [SerializeField] private bool _showAttackDebug = true; @@ -83,6 +87,7 @@ private void Awake() _spriteRenderer = GetComponentInChildren(); _bodyColliders = GetComponentsInChildren(); EnsureAttackHitbox(); + _attackHitbox.OnHit += OnAttackHit; } private void Start() @@ -94,6 +99,7 @@ private void Start() InputManager.Instance.OnDash_Event += OnDashInput; InputManager.Instance.OnRoll_Event += OnRollInput; InputManager.Instance.OnBackDash_Event += OnBackDashInput; + InputManager.Instance.OnGrabSmash_Event += OnGrabSmashInput; } private void OnDestroy() @@ -107,14 +113,16 @@ private void OnDestroy() InputManager.Instance.OnDash_Event -= OnDashInput; InputManager.Instance.OnRoll_Event -= OnRollInput; InputManager.Instance.OnBackDash_Event -= OnBackDashInput; + InputManager.Instance.OnGrabSmash_Event -= OnGrabSmashInput; } - _attackCts?.Cancel(); - _attackCts?.Dispose(); - _motionCts?.Cancel(); - _motionCts?.Dispose(); - _animationSpeedCts?.Cancel(); - _animationSpeedCts?.Dispose(); + CancelAndDispose(ref _attackCts); + CancelAndDispose(ref _motionCts); + CancelAndDispose(ref _animationSpeedCts); + CancelAndDispose(ref _actionVelocityCts); + + if (_attackHitbox != null) + _attackHitbox.OnHit -= OnAttackHit; } private void FixedUpdate() @@ -198,9 +206,13 @@ private void OnJumpInput() private void OnDashInput() => ExecuteMotionNode(_dashRootNode); private void OnRollInput() => ExecuteMotionNode(_rollRootNode); private void OnBackDashInput() => ExecuteMotionNode(_backDashRootNode); + private void OnGrabSmashInput() => ExecuteAttackNode(_grabSmashRootNode); private void HandleComboInput(ComboInputType input) { + if (_isMotionActive && !CanTransitionFromCurrentNode(input)) + return; + if (_attackCooldownTimer > 0f) { float elapsed = Time.time - _attackStartTime; @@ -257,6 +269,16 @@ private void ExecuteMotionNode(ComboNode root) _comboWindowTimer = root.ComboWindow; } + private void ExecuteAttackNode(ComboNode root) + { + if (root == null || root.Action == null) return; + if (_attackCooldownTimer > 0f) return; + + PerformAttack(root.Action); + _currentNode = root; + _comboWindowTimer = root.ComboWindow; + } + private void UpdateMotionCooldowns() { if (_motionCooldownTimers.Count == 0) return; @@ -288,8 +310,8 @@ private void SetMotionCooldown(ActionData data) private async void PerformAttack(ActionData data, bool preserveHorizontalVelocity = false) { - _attackCts?.Cancel(); - _attackCts?.Dispose(); + CancelMotion(); + CancelAndDispose(ref _attackCts); _attackCts = CancellationTokenSource.CreateLinkedTokenSource(destroyCancellationToken); CancellationToken token = _attackCts.Token; @@ -301,15 +323,17 @@ private async void PerformAttack(ActionData data, bool preserveHorizontalVelocit ClearActionLocks(); PlayActionAnimation(data); - if (data.HasMotion) - ApplyActionVelocity(data); + PlayActionVelocity(data); LockMovementIfNeeded(data, preserveHorizontalVelocity, true); LockFacingIfNeeded(data); try { - await HitRoutine(data, token); + if (data.IsGrab) + await GrabRoutine(data, token); + else + await HitRoutine(data, token); } catch (System.OperationCanceledException) { @@ -323,15 +347,16 @@ private async void PerformMotion(ActionData data) // 대시/구르기 같은 모션은 공격을 끊고 새로운 콤보 노드가 된다. CancelAttack(); - _motionCts?.Cancel(); - _motionCts?.Dispose(); + CancelAndDispose(ref _motionCts); _motionCts = CancellationTokenSource.CreateLinkedTokenSource(destroyCancellationToken); CancellationToken token = _motionCts.Token; SetMotionCooldown(data); + _isMotionActive = true; ClearActionLocks(); FaceMotionDirection(data); PlayActionAnimation(data); + PlayActionVelocity(data); LockMovementIfNeeded(data); LockFacingIfNeeded(data); @@ -341,11 +366,15 @@ private async void PerformMotion(ActionData data) await MotionRoutine(data, token); completed = true; } - catch (System.OperationCanceledException) { } + catch (System.OperationCanceledException) + { + _isMotionActive = false; + } if (completed) { StopActionVelocity(data); + _isMotionActive = false; ClearActionLocks(); PlayIdleAnimation(); } @@ -359,9 +388,6 @@ private async Awaitable MotionRoutine(ActionData data, CancellationToken token) while (ShouldKeepActionPlaying(data, elapsed, duration)) { token.ThrowIfCancellationRequested(); - float normalizedTime = GetActionNormalizedTime(data, elapsed, duration); - ApplyActionVelocity(data, normalizedTime); - await Awaitable.NextFrameAsync(token); elapsed += Time.deltaTime; } @@ -369,13 +395,34 @@ private async Awaitable MotionRoutine(ActionData data, CancellationToken token) private void CancelAttack() { - _attackCts?.Cancel(); + CancelAndDispose(ref _attackCts); + CancelAndDispose(ref _actionVelocityCts); _attackCooldownTimer = 0f; _pendingInput = null; _attackHitbox?.Deactivate(); ClearActionLocks(); } + private void CancelMotion() + { + _isMotionActive = false; + CancelAndDispose(ref _motionCts); + CancelAndDispose(ref _actionVelocityCts); + } + + private bool CanTransitionFromCurrentNode(ComboInputType input) + { + if (_comboWindowTimer <= 0f || _currentNode == null) return false; + + foreach (var transition in _currentNode.Transitions) + { + if (transition.Trigger == input && transition.Next != null && transition.Next.Action != null) + return true; + } + + return false; + } + private async Awaitable HitRoutine(ActionData data, CancellationToken token) { float attackStartTime = Time.time; @@ -402,6 +449,101 @@ private async Awaitable HitRoutine(ActionData data, CancellationToken token) PlayIdleAnimation(); } + private async Awaitable GrabRoutine(ActionData data, CancellationToken token) + { + Enemy target = FindGrabTarget(data); + if (target == null) + { + await WaitForActionEnd(data, token); + ClearActionLocks(); + PlayIdleAnimation(); + return; + } + + target.BeginGrab(data.GrabbedAnimationState, _groundLayer.value); + + try + { + float elapsed = 0f; + float duration = GetActionDuration(data); + while (ShouldKeepActionPlaying(data, elapsed, duration)) + { + token.ThrowIfCancellationRequested(); + float normalizedTime = GetActionNormalizedTime(data, elapsed, duration); + target.UpdateGrabPosition(GetGrabWorldPosition(data, normalizedTime)); + + await Awaitable.NextFrameAsync(token); + elapsed += Time.deltaTime; + } + + target.EndGrab(); + target.TakeDamage(data.Damage, GetHitVelocity(data.HitVelocity), data.HitReactionAnimationState); + } + catch (System.OperationCanceledException) + { + target.EndGrab(); + throw; + } + + ClearActionLocks(); + PlayIdleAnimation(); + } + + private Enemy FindGrabTarget(ActionData data) + { + Vector2 playerPosition = _rb != null ? _rb.position : (Vector2)transform.position; + float grabRangeSqr = data.GrabRange * data.GrabRange; + + if (_lastHitEnemy != null && _lastHitEnemy.isActiveAndEnabled && + GetEnemySqrDistance(_lastHitEnemy, playerPosition) <= grabRangeSqr) + { + return _lastHitEnemy; + } + + Collider2D[] hits = Physics2D.OverlapCircleAll(transform.position, data.GrabSearchRadius, _enemyLayer); + Enemy closest = null; + float closestSqrDistance = float.PositiveInfinity; + + foreach (var hit in hits) + { + Enemy enemy = hit.GetComponentInParent(); + if (enemy == null) continue; + + float sqrDistance = GetEnemySqrDistance(enemy, playerPosition); + if (sqrDistance > grabRangeSqr) continue; + if (sqrDistance >= closestSqrDistance) continue; + + closest = enemy; + closestSqrDistance = sqrDistance; + } + + return closest; + } + + private float GetEnemySqrDistance(Enemy enemy, Vector2 playerPosition) + { + if (enemy == null) return float.PositiveInfinity; + return ((Vector2)enemy.transform.position - playerPosition).sqrMagnitude; + } + + private Vector2 GetGrabWorldPosition(ActionData data, float normalizedTime) + { + float facing = _spriteRenderer != null && _spriteRenderer.flipX ? -1f : 1f; + Vector2 playerPosition = _rb != null ? _rb.position : (Vector2)transform.position; + Vector2 offset = data.GrabOffset; + float offsetXMultiplier = data.GrabOffsetXCurve != null ? data.GrabOffsetXCurve.Evaluate(normalizedTime) : 1f; + float offsetYMultiplier = data.GrabOffsetYCurve != null ? data.GrabOffsetYCurve.Evaluate(normalizedTime) : 1f; + offset.x *= facing * offsetXMultiplier; + offset.y *= offsetYMultiplier; + return playerPosition + offset; + } + + private void OnAttackHit(IDamageable target) + { + if (target is Enemy enemy) + _lastHitEnemy = enemy; + } + private void ActivateAttackHitbox(ActionData data) { Vector2 localPosition = GetAttackLocalPosition(data.Offset); @@ -475,7 +617,7 @@ private void LockMovementIfNeeded(ActionData data, bool preserveHorizontalVeloci if (!forceLock && data.CanMoveDuringAction) return; // 이동 없는 공격은 직전 프레임의 걷기 속도를 이어받지 않게 한다. - if (!preserveHorizontalVelocity && !data.HasMotion) + if (!preserveHorizontalVelocity && data.Velocity == Vector2.zero) _rb.linearVelocity = new Vector2(0f, _rb.linearVelocity.y); _inputLockTimer = GetActionLockDuration(data); @@ -493,7 +635,8 @@ private void PlayIdleAnimation() { if (_anim == null) return; - _animationSpeedCts?.Cancel(); + CancelAndDispose(ref _animationSpeedCts); + CancelAndDispose(ref _actionVelocityCts); _anim.speed = 1f; if (!string.IsNullOrEmpty(_idleAnimationState)) _anim.Play(_idleAnimationState); @@ -503,8 +646,7 @@ private void PlayActionAnimation(ActionData data) { if (_anim == null) return; - _animationSpeedCts?.Cancel(); - _animationSpeedCts?.Dispose(); + CancelAndDispose(ref _animationSpeedCts); _animationSpeedCts = CancellationTokenSource.CreateLinkedTokenSource(destroyCancellationToken); _anim.speed = GetAnimationSpeed(data, 0f); @@ -517,6 +659,36 @@ private void PlayActionAnimation(ActionData data) ApplyAnimationSpeedCurve(data, _animationSpeedCts.Token); } + private void PlayActionVelocity(ActionData data) + { + CancelAndDispose(ref _actionVelocityCts); + + if (data.Velocity == Vector2.zero) return; + + _actionVelocityCts = CancellationTokenSource.CreateLinkedTokenSource(destroyCancellationToken); + ApplyActionVelocityCurve(data, _actionVelocityCts.Token); + } + + private async void ApplyActionVelocityCurve(ActionData data, CancellationToken token) + { + float elapsed = 0f; + float duration = GetActionDuration(data); + + try + { + while (ShouldKeepActionPlaying(data, elapsed, duration)) + { + token.ThrowIfCancellationRequested(); + float normalizedTime = GetActionNormalizedTime(data, elapsed, duration); + ApplyActionVelocity(data, normalizedTime); + + await Awaitable.NextFrameAsync(token); + elapsed += Time.deltaTime; + } + } + catch (System.OperationCanceledException) { } + } + private async void ApplyAnimationSpeedCurve(ActionData data, CancellationToken token) { float duration = GetActionDuration(data); @@ -702,6 +874,20 @@ private void ClearActionLocks() _facingLockAction = null; } + private void CancelAndDispose(ref CancellationTokenSource cts) + { + if (cts == null) return; + + try + { + cts.Cancel(); + } + catch (System.ObjectDisposedException) { } + + cts.Dispose(); + cts = null; + } + private float GetActionDuration(ActionData data) { return Mathf.Max(data.MotionDuration, data.HitTiming + data.HitDuration, 0.01f); @@ -722,7 +908,7 @@ private bool ShouldKeepActionPlaying(ActionData data, float elapsed, float durat private float GetActionNormalizedTime(ActionData data, float elapsed, float duration) { - if (!HasActionAnimation(data)) + if (!ShouldUseAnimationLength(data)) return Mathf.Clamp01(elapsed / duration); AnimatorStateInfo stateInfo = _anim.GetCurrentAnimatorStateInfo(0); diff --git a/Assets/03_Character/WhiteMan/Animations/Animators/PlayerAnimator.controller b/Assets/03_Character/WhiteMan/Animations/Animators/PlayerAnimator.controller index 106d456..0ad6e54 100644 --- a/Assets/03_Character/WhiteMan/Animations/Animators/PlayerAnimator.controller +++ b/Assets/03_Character/WhiteMan/Animations/Animators/PlayerAnimator.controller @@ -702,6 +702,32 @@ AnimatorState: m_MirrorParameter: m_CycleOffsetParameter: m_TimeParameter: +--- !u!1102 &-4203153932972611291 +AnimatorState: + serializedVersion: 6 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: GrabSmash + 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: 7c16f79e8404ac7478767cd96faaeb72, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: --- !u!1102 &-3985368095300963852 AnimatorState: serializedVersion: 6 @@ -3121,6 +3147,9 @@ AnimatorStateMachine: - serializedVersion: 1 m_State: {fileID: 6644522863449380584} m_Position: {x: 1140, y: -100, z: 0} + - serializedVersion: 1 + m_State: {fileID: -4203153932972611291} + m_Position: {x: 5280, y: 90, z: 0} m_ChildStateMachines: [] m_AnyStateTransitions: [] m_EntryTransitions: [] diff --git a/Assets/03_Character/WhiteMan/Animations/GrabSmash.anim b/Assets/03_Character/WhiteMan/Animations/GrabSmash.anim new file mode 100644 index 0000000..4bedd42 --- /dev/null +++ b/Assets/03_Character/WhiteMan/Animations/GrabSmash.anim @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea9be8c13e689ef32d2aa3172a93a0bdc98a7db1759cb72e1bb4651c33c94012 +size 7670 diff --git a/Assets/03_Character/WhiteMan/Animations/GrabSmash.anim.meta b/Assets/03_Character/WhiteMan/Animations/GrabSmash.anim.meta new file mode 100644 index 0000000..ab9126a --- /dev/null +++ b/Assets/03_Character/WhiteMan/Animations/GrabSmash.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7c16f79e8404ac7478767cd96faaeb72 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/05_Data/Attack/GrabGroundSmash.asset b/Assets/05_Data/Attack/GrabGroundSmash.asset new file mode 100644 index 0000000..42dc8b0 --- /dev/null +++ b/Assets/05_Data/Attack/GrabGroundSmash.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:838beed0510fe457207fdeba3d7b9592aac4f1cf30ce9dc22c975e39c0ed3832 +size 3897 diff --git a/Assets/05_Data/Attack/GrabGroundSmash.asset.meta b/Assets/05_Data/Attack/GrabGroundSmash.asset.meta new file mode 100644 index 0000000..0ec22e7 --- /dev/null +++ b/Assets/05_Data/Attack/GrabGroundSmash.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c35c8bd4e0ad2cf41ab1af35fc81bbc0 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/05_Data/Attack/GrabSmash.asset b/Assets/05_Data/Attack/GrabSmash.asset new file mode 100644 index 0000000..8cedf49 --- /dev/null +++ b/Assets/05_Data/Attack/GrabSmash.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:794004611318acd88042f79bafe49a6de4048f2308dfecb1281d3e4827f1160a +size 3888 diff --git a/Assets/05_Data/Attack/GrabSmash.asset.meta b/Assets/05_Data/Attack/GrabSmash.asset.meta new file mode 100644 index 0000000..e7f18ef --- /dev/null +++ b/Assets/05_Data/Attack/GrabSmash.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e3de0ce9283788a4c9a4a9486a1122e7 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/05_Data/Combo/Combo_GrabSmash.asset b/Assets/05_Data/Combo/Combo_GrabSmash.asset new file mode 100644 index 0000000..95d8881 --- /dev/null +++ b/Assets/05_Data/Combo/Combo_GrabSmash.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:067350b97a6ea6b61d89a891266149f4813cc219160ceea751b4a8dcb5b52d7f +size 574 diff --git a/Assets/05_Data/Combo/Combo_GrabSmash.asset.meta b/Assets/05_Data/Combo/Combo_GrabSmash.asset.meta new file mode 100644 index 0000000..28e7fbc --- /dev/null +++ b/Assets/05_Data/Combo/Combo_GrabSmash.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c9a3bb08b50045a4395d19af97aab515 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/11_Input/GameInput.inputactions b/Assets/11_Input/GameInput.inputactions index 52e3dd0..2029264 100644 --- a/Assets/11_Input/GameInput.inputactions +++ b/Assets/11_Input/GameInput.inputactions @@ -68,6 +68,15 @@ "processors": "", "interactions": "", "initialStateCheck": false + }, + { + "name": "GrabSmash", + "type": "Button", + "id": "c42f3219-3aca-4678-8a86-b74565af3747", + "expectedControlType": "", + "processors": "", + "interactions": "", + "initialStateCheck": false } ], "bindings": [ @@ -191,6 +200,17 @@ "action": "BackDash", "isComposite": false, "isPartOfComposite": false + }, + { + "name": "", + "id": "8635811d-29ac-4047-a3f7-d7a72f7e362e", + "path": "/g", + "interactions": "", + "processors": "", + "groups": "", + "action": "GrabSmash", + "isComposite": false, + "isPartOfComposite": false } ] }