2026-05-18 공격에 의한 이동량 초기화
This commit is contained in:
@@ -14,7 +14,7 @@ public class AttackHitbox : MonoBehaviour
|
||||
private void Awake()
|
||||
{
|
||||
_collider = GetComponent<CircleCollider2D>();
|
||||
// The player body does not collide with enemies; this trigger is the only attack contact.
|
||||
// 플레이어 몸체는 적과 물리 충돌하지 않으므로, 공격 판정은 이 트리거만 사용한다.
|
||||
_collider.isTrigger = true;
|
||||
_collider.enabled = false;
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public void Activate(ActionData data, Vector2 localPosition, Vector2 hitVelocity
|
||||
_alreadyHit.Clear();
|
||||
_collider.enabled = true;
|
||||
|
||||
// Catch enemies already inside the hitbox on the same frame it opens.
|
||||
// 판정이 켜진 순간 이미 범위 안에 있던 적도 같은 프레임에 잡아낸다.
|
||||
ScanImmediateOverlap();
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ private void TryDamage(Collider2D other)
|
||||
{
|
||||
if ((_targetLayer.value & (1 << other.gameObject.layer)) == 0) return;
|
||||
|
||||
// Hurtboxes may live on child objects, while damage handling usually lives on the root.
|
||||
// 피격 콜라이더는 자식에 있고, 데미지 처리는 루트에 있을 수 있다.
|
||||
if (!other.TryGetComponent<IDamageable>(out var target))
|
||||
target = other.GetComponentInParent<IDamageable>();
|
||||
if (target == null) return;
|
||||
|
||||
@@ -73,13 +73,18 @@ public void TakeDamage(int amount, Vector2 hitVelocity = default, string hitReac
|
||||
if (_anim != null && !string.IsNullOrEmpty(hitReactionAnimationState))
|
||||
_anim.Play(hitReactionAnimationState);
|
||||
|
||||
// HitVelocity is an immediate launch/knockback velocity, not an additive force.
|
||||
// 새 피격이 반응 속도를 전부 결정하므로, 이전 튕김/넉백 속도는 먼저 제거한다.
|
||||
if (_rb != null)
|
||||
{
|
||||
_hitReactionTimer = 0f;
|
||||
_rb.linearVelocity = Vector2.zero;
|
||||
_lastVelocity = Vector2.zero;
|
||||
|
||||
Vector2 nextVelocity = GetHitReactionVelocity(hitVelocity);
|
||||
if (nextVelocity != Vector2.zero)
|
||||
{
|
||||
_rb.linearVelocity = nextVelocity;
|
||||
_lastVelocity = nextVelocity;
|
||||
_hitReactionTimer = _hitReactionDuration;
|
||||
}
|
||||
}
|
||||
@@ -117,13 +122,11 @@ private void OnCollisionExit2D(Collision2D collision)
|
||||
|
||||
private Vector2 GetHitReactionVelocity(Vector2 hitVelocity)
|
||||
{
|
||||
// Airborne follow-up hits pop the enemy with a fixed Y velocity for stable combos.
|
||||
if (_hitReactionTimer <= 0f || _isGrounded)
|
||||
return hitVelocity;
|
||||
// 공중 추가타는 고정된 세로 속도를 쓰되, 이전 물리 속도는 절대 이어받지 않는다.
|
||||
Vector2 nextVelocity = hitVelocity;
|
||||
if (!_isGrounded)
|
||||
nextVelocity.y = _airborneHitYVelocity;
|
||||
|
||||
Vector2 currentVelocity = _rb.linearVelocity;
|
||||
Vector2 nextVelocity = hitVelocity == Vector2.zero ? currentVelocity : hitVelocity;
|
||||
nextVelocity.y = _airborneHitYVelocity;
|
||||
return nextVelocity;
|
||||
}
|
||||
|
||||
@@ -141,7 +144,7 @@ private void UpdateGroundedState(Collision2D collision)
|
||||
|
||||
private void BounceOffWall(Vector2 wallNormal)
|
||||
{
|
||||
// While in hit reaction, side-wall impacts reflect the current knockback.
|
||||
// 피격 반응 중 옆벽에 부딪히면 현재 넉백 속도를 반사한다.
|
||||
Vector2 incomingVelocity = _lastVelocity.sqrMagnitude > _rb.linearVelocity.sqrMagnitude
|
||||
? _lastVelocity
|
||||
: _rb.linearVelocity;
|
||||
|
||||
@@ -114,7 +114,7 @@ private void OnDestroy()
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
// Sample collision probes first; movement, wall slide, and jump decisions all use these flags.
|
||||
// 이동, 벽타기, 점프 판단이 모두 이 값을 쓰므로 충돌 체크를 먼저 갱신한다.
|
||||
_isGrounded = Physics2D.OverlapCircle(_groundCheck.position, _groundCheckRadius, _groundLayer);
|
||||
_isTouchingLeftWall = Physics2D.OverlapCircle(_wallCheckLeft.position, _wallCheckRadius, _groundLayer);
|
||||
_isTouchingRightWall = Physics2D.OverlapCircle(_wallCheckRight.position, _wallCheckRadius, _groundLayer);
|
||||
@@ -142,7 +142,7 @@ private void FixedUpdate()
|
||||
if (IsTouchingWall && !_isGrounded && _rb.linearVelocity.y < -_wallSlideSpeed)
|
||||
_rb.linearVelocity = new Vector2(_rb.linearVelocity.x, -_wallSlideSpeed);
|
||||
|
||||
// Player and Enemy bodies do not physically collide; only ground/walls clamp player velocity.
|
||||
// 플레이어와 적 몸체는 물리 충돌하지 않고, 땅/벽만 캐스트로 이동을 막는다.
|
||||
ClampVelocityToGround();
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ private void HandleComboInput(ComboInputType input)
|
||||
|
||||
private void ExecuteComboInput(ComboInputType input)
|
||||
{
|
||||
// Continue from the current combo node while its window is open.
|
||||
// 콤보 입력 가능 시간이 열려 있으면 현재 노드에서 다음 연계로 이어간다.
|
||||
if (_comboWindowTimer > 0f && _currentNode != null)
|
||||
{
|
||||
foreach (var transition in _currentNode.Transitions)
|
||||
@@ -231,7 +231,7 @@ private void ExecuteComboInput(ComboInputType input)
|
||||
}
|
||||
}
|
||||
|
||||
// No active combo route matched, so start from the input's root node.
|
||||
// 이어갈 연계가 없으면 입력에 맞는 루트 노드부터 새로 시작한다.
|
||||
ComboNode root = input switch
|
||||
{
|
||||
ComboInputType.Punch => _punchRootNode,
|
||||
@@ -318,7 +318,7 @@ private async void PerformMotion(ActionData data)
|
||||
{
|
||||
if (data == null || IsMotionOnCooldown(data)) return;
|
||||
|
||||
// Motions such as dash/roll interrupt attacks and become the new combo node.
|
||||
// 대시/구르기 같은 모션은 공격을 끊고 새로운 콤보 노드가 된다.
|
||||
CancelAttack();
|
||||
_motionCts?.Cancel();
|
||||
_motionCts?.Dispose();
|
||||
@@ -387,7 +387,7 @@ private async Awaitable HitRoutine(ActionData data, CancellationToken token)
|
||||
if (data.HitTiming > 0f)
|
||||
await Awaitable.WaitForSecondsAsync(data.HitTiming, token);
|
||||
|
||||
// Hit windows are represented by a real trigger collider instead of overlap checks.
|
||||
// 타격 가능 시간은 범위 검사 대신 실제 트리거 콜라이더를 켜서 표현한다.
|
||||
ActivateAttackHitbox(data);
|
||||
|
||||
float activeTime = Mathf.Max(data.HitDuration, 0.02f);
|
||||
@@ -416,7 +416,7 @@ private void ActivateAttackHitbox(ActionData data)
|
||||
|
||||
private void EnsureAttackHitbox()
|
||||
{
|
||||
// Allow designers to assign a hitbox, but create one automatically for simple setup.
|
||||
// 인스펙터에서 직접 넣을 수도 있고, 없으면 기본 공격 판정을 자동으로 만든다.
|
||||
if (_attackHitbox != null)
|
||||
{
|
||||
SetAttackHitboxLayer();
|
||||
@@ -439,7 +439,7 @@ private void EnsureAttackHitbox()
|
||||
|
||||
private void SetAttackHitboxLayer()
|
||||
{
|
||||
// The hitbox must not inherit the Player layer, because Player vs Enemy physics is disabled.
|
||||
// 플레이어와 적의 물리 충돌이 꺼져 있으므로 공격 판정은 플레이어 레이어를 쓰면 안 된다.
|
||||
int defaultLayer = LayerMask.NameToLayer("Default");
|
||||
if (defaultLayer >= 0)
|
||||
_attackHitbox.gameObject.layer = defaultLayer;
|
||||
@@ -471,7 +471,7 @@ private void LockMovementIfNeeded(ActionData data, bool preserveHorizontalVeloci
|
||||
{
|
||||
if (data.CanMoveDuringAction) return;
|
||||
|
||||
// Non-moving attacks should not inherit walking velocity from the previous frame.
|
||||
// 이동 없는 공격은 직전 프레임의 걷기 속도를 이어받지 않게 한다.
|
||||
if (!preserveHorizontalVelocity && !data.HasMotion)
|
||||
_rb.linearVelocity = new Vector2(0f, _rb.linearVelocity.y);
|
||||
|
||||
@@ -660,7 +660,7 @@ private float GetClosestHitDistance(Vector2 direction, float distance, int layer
|
||||
useTriggers = false
|
||||
};
|
||||
|
||||
// Cast every non-trigger body collider so high-speed motion cannot tunnel into level geometry.
|
||||
// 빠른 이동이 지형을 뚫지 않도록 트리거가 아닌 몸체 콜라이더를 전부 캐스트한다.
|
||||
float closest = float.PositiveInfinity;
|
||||
for (int i = 0; i < _bodyColliders.Length; i++)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user