2026-05-18 버그 수정

This commit is contained in:
2026-05-18 17:59:13 +09:00
parent 67cedd8ad2
commit 80cf41af2a
19 changed files with 596 additions and 17 deletions

View File

@@ -82,7 +82,9 @@ public class PlayerController : MonoBehaviour
private CancellationTokenSource _motionCts;
private CancellationTokenSource _animationSpeedCts;
private CancellationTokenSource _actionVelocityCts;
private bool _isAttackActive;
private bool _isMotionActive;
private float _actionDirection = 1f;
private ActionData _lastAttackGizmoData;
[SerializeField] private float _hitGizmoFadeDuration = 0.5f;
private ActionData _lastHitData;
@@ -161,10 +163,10 @@ private void FixedUpdate()
ExecuteBufferedInputIfReady();
TickComboWindow();
if (!IsMovementLocked())
if (!IsMovementLocked() && !IsActionActive())
_rb.linearVelocity = new Vector2(_moveInputX * _moveSpeed, _rb.linearVelocity.y);
if (!IsFacingLocked())
if (!IsFacingLocked() && !IsActionActive())
UpdateFacingFromMoveInput();
ApplyGravity();
@@ -201,7 +203,7 @@ private void TickComboWindow()
private void OnMoveInput(Vector2 value)
{
_moveInputX = value.x == 0f ? 0f : Mathf.Sign(value.x);
if (_facingLockTimer <= 0f)
if (_facingLockTimer <= 0f && !IsActionActive())
UpdateFacingFromMoveInput();
}
@@ -268,6 +270,7 @@ private void ExecuteComboInput(ComboInputType input)
{
if (transition.Trigger != input) continue;
if (transition.Next == null || transition.Next.Action == null) continue;
if (!CanPerformAction(transition.Next.Action)) continue;
ApplyForwardStep(transition.ForwardStep, transition.ForwardStepDuration);
PerformAttack(transition.Next.Action, transition.ForwardStep > 0f);
@@ -286,12 +289,21 @@ private void ExecuteComboInput(ComboInputType input)
_ => null
};
if (root == null || root.Action == null) return;
if (!CanPerformAction(root.Action)) return;
PerformAttack(root.Action);
_currentNode = root;
_comboWindowTimer = root.ComboWindow;
}
private bool CanPerformAction(ActionData data)
{
if (data == null) return false;
// 잡기 액션은 사정 거리 안에 적이 있어야만 실행. 없으면 입력 자체를 캔슬.
if (data.IsGrab && FindGrabTarget(data) == null) return false;
return true;
}
private void ExecuteMotionNode(ComboNode root)
{
if (root == null || root.Action == null) return;
@@ -336,14 +348,17 @@ private async void PerformAttack(ActionData data, bool preserveHorizontalVelocit
CancelMotion();
CancelAndDispose(ref _attackCts);
_attackCts = CancellationTokenSource.CreateLinkedTokenSource(destroyCancellationToken);
CancellationTokenSource currentAttackCts = _attackCts;
CancellationToken token = _attackCts.Token;
_attackCooldownTimer = data.Cooldown;
_isAttackActive = true;
_lastAttackGizmoData = data;
_attackStartTime = Time.time;
_hitFired = false;
ClearActionLocks();
CaptureActionDirection(data);
PlayActionAnimation(data);
PlayActionVelocity(data);
@@ -362,6 +377,9 @@ private async void PerformAttack(ActionData data, bool preserveHorizontalVelocit
{
_attackHitbox?.Deactivate();
}
if (_attackCts == currentAttackCts)
_isAttackActive = false;
}
private async void PerformMotion(ActionData data)
@@ -375,12 +393,13 @@ private async void PerformMotion(ActionData data)
CancellationToken token = _motionCts.Token;
SetMotionCooldown(data);
_isMotionActive = true;
ClearActionLocks();
CaptureActionDirection(data);
_isMotionActive = true;
FaceMotionDirection(data);
PlayActionAnimation(data);
PlayActionVelocity(data);
LockMovementIfNeeded(data);
LockMovementIfNeeded(data, false, true);
LockFacingIfNeeded(data);
bool completed = false;
@@ -432,6 +451,7 @@ private async void PerformGroundPound()
_hitFired = false;
ClearActionLocks();
CaptureActionDirection(_groundPoundData);
FaceMotionDirection(_groundPoundData);
PlayActionAnimation(_groundPoundData);
LockMovementIfNeeded(_groundPoundData);
@@ -512,6 +532,7 @@ private async void PerformGroundPound()
private void CancelAttack()
{
_isAttackActive = false;
CancelAndDispose(ref _attackCts);
CancelAndDispose(ref _actionVelocityCts);
_attackCooldownTimer = 0f;
@@ -527,6 +548,11 @@ private void CancelMotion()
CancelAndDispose(ref _actionVelocityCts);
}
private bool IsActionActive()
{
return _isAttackActive || _isMotionActive || _isGroundPounding;
}
private bool CanTransitionFromCurrentNode(ComboInputType input)
{
if (_comboWindowTimer <= 0f || _currentNode == null) return false;
@@ -672,6 +698,7 @@ private void ActivateAttackHitbox(ActionData data)
_lastHitTime = Time.time;
_hitFired = true;
Debug.Log($"[Activate] t={Time.time:F3} action={GetActionName(data)} hitDuration={data.HitDuration:F3} motionActive={_isMotionActive} groundPounding={_isGroundPounding}");
Vector2 sourcePosition = _rb != null ? _rb.position : (Vector2)transform.position;
_attackHitbox.Activate(data, localPosition, hitVelocity, sourcePosition, hitTargetPosition, data.CorrectHitTargetY, _groundLayer.value, _enemyLayer);
}
@@ -737,8 +764,17 @@ private void LockMovementIfNeeded(ActionData data, bool preserveHorizontalVeloci
if (!preserveHorizontalVelocity && data.Velocity == Vector2.zero)
_rb.linearVelocity = new Vector2(0f, _rb.linearVelocity.y);
_inputLockTimer = GetActionLockDuration(data);
_movementLockAction = ShouldUseAnimationLength(data) ? data : null;
if (forceLock)
{
// 액션 길이 전체를 보장: 애니메이션 길이와 무관하게 MotionDuration만큼 잠금.
_inputLockTimer = Mathf.Max(data.MotionDuration, 0.02f);
_movementLockAction = null;
}
else
{
_inputLockTimer = GetActionLockDuration(data);
_movementLockAction = ShouldUseAnimationLength(data) ? data : null;
}
}
private void LockFacingIfNeeded(ActionData data)
@@ -889,21 +925,25 @@ private float GetAnimationSpeed(ActionData data, float normalizedTime)
private float GetMotionDirection(ActionData data)
{
if (data.UseInputDirection && _moveInputX != 0f)
return _moveInputX;
return _spriteRenderer != null && _spriteRenderer.flipX ? -1f : 1f;
return _actionDirection;
}
private void FaceMotionDirection(ActionData data)
{
if (_spriteRenderer == null) return;
if (!data.UseInputDirection || _moveInputX == 0f) return;
_spriteRenderer.flipX = _moveInputX < 0f;
_spriteRenderer.flipX = _actionDirection < 0f;
_facingLockTimer = 0f;
}
private void CaptureActionDirection(ActionData data)
{
if (data != null && data.UseInputDirection && _moveInputX != 0f)
_actionDirection = _moveInputX;
else
_actionDirection = _spriteRenderer != null && _spriteRenderer.flipX ? -1f : 1f;
}
private Vector2 GetHitVelocity(Vector2 hitVelocity)
{
float facing = _spriteRenderer != null && _spriteRenderer.flipX ? -1f : 1f;