2026-05-22 보스스킬 : 지진
This commit is contained in:
@@ -27,6 +27,13 @@ public class PlayerController : MonoBehaviour,IDamageable
|
||||
private string _activeBaseState; // 현재 재생 중인 locomotion State (중복 Play 방지용)
|
||||
private bool _isInActionAnimation; // 액션 애니메이션 재생 중인지 (locomotion 잠시 양보)
|
||||
|
||||
// 피격
|
||||
[Header("Hit Animation")]
|
||||
[SerializeField] private string _knockbackAnimationState = "Knockback";
|
||||
[SerializeField] private float _knockbackDuration = 1f; // 넉백 경직 시간 (Knockback 클립 길이에 맞춰 설정)
|
||||
private float _hitstunTimer; // 경직 남은 최소 시간
|
||||
private bool _isStunned; // 경직 상태 (공중이면 착지까지 연장)
|
||||
|
||||
// ─── 점프 단계별 애니메이션 ───────────────────────────────────────────
|
||||
// 점프를 4단계(Rise/Mid/Fall/Land)로 분리해서 vy에 따라 자동 전환.
|
||||
[Header("Jump Animation")]
|
||||
@@ -253,19 +260,21 @@ private void FixedUpdate()
|
||||
TickComboWindow(); // 콤보 윈도우 카운트다운
|
||||
|
||||
// 입력 잠금이 없고, (액션 중이 아니거나 액션이 이동을 허용할 때) 좌우 입력으로 velocity 갱신.
|
||||
if (!IsMovementLocked() && (!IsActionActive() || _actionAllowsMovement))
|
||||
// 경직 중에는 이동 입력을 무시한다 (Launch로 받은 속도 + 중력만 작용).
|
||||
if (!IsStunned() && !IsMovementLocked() && (!IsActionActive() || _actionAllowsMovement))
|
||||
{
|
||||
// 백페달이면 느린 속도. 액션 중에도 동일하게 적용 (이동사격 = Down 변형 + CanMoveDuringAction).
|
||||
float moveSpeed = IsBackpedaling() ? _backpedalSpeed : _moveSpeed;
|
||||
_rb.linearVelocity = new Vector2(_moveInputX * moveSpeed, _rb.linearVelocity.y);
|
||||
}
|
||||
|
||||
if (!IsFacingLocked() && (!IsActionActive() || _actionAllowsTurn))
|
||||
if (!IsStunned() && !IsFacingLocked() && (!IsActionActive() || _actionAllowsTurn))
|
||||
UpdateFacingFromMoveInput();
|
||||
|
||||
//중력적용
|
||||
ApplyGravity();
|
||||
ResolveKinematicCollisions();
|
||||
TickHitstun(); // 피격 경직 타이머 카운트다운
|
||||
|
||||
UpdateLocomotionAnimation();
|
||||
|
||||
@@ -307,7 +316,7 @@ private void OnMoveInput(Vector2 value)
|
||||
{
|
||||
_moveInputX = value.x == 0f ? 0f : Mathf.Sign(value.x);
|
||||
_moveInputY = value.y == 0f ? 0f : Mathf.Sign(value.y);
|
||||
if (_facingLockTimer <= 0f && (!IsActionActive() || _actionAllowsTurn))
|
||||
if (!IsStunned() && _facingLockTimer <= 0f && (!IsActionActive() || _actionAllowsTurn))
|
||||
UpdateFacingFromMoveInput();
|
||||
}
|
||||
|
||||
@@ -332,6 +341,8 @@ private bool IsBackpedaling()
|
||||
// 점프 우선순위: 지상 점프 > 공중(2단) 점프
|
||||
private void OnJumpInput()
|
||||
{
|
||||
if (IsStunned()) return; // 경직 중 점프 불가
|
||||
|
||||
if (_isGrounded)
|
||||
{
|
||||
PerformJump();
|
||||
@@ -374,6 +385,8 @@ private void OnGrabSmashInput()
|
||||
// 3) 그 외에는 즉시 ExecuteComboInput으로 발화
|
||||
private void HandleComboInput(ComboInputType input)
|
||||
{
|
||||
if (IsStunned()) return; // 경직 중 공격 불가
|
||||
|
||||
// 콤보 연계(현재 노드의 트랜지션) 가능 여부 — 연계는 애니메이션 중에도 즉시 캔슬-체인.
|
||||
bool canChain = CanTransitionFromCurrentNode(input);
|
||||
|
||||
@@ -509,6 +522,7 @@ private bool IsWeaponRequirementMet(ActionData data)
|
||||
|
||||
private void ExecuteMotionNode(ComboNode root)
|
||||
{
|
||||
if (IsStunned()) return; // 경직 중 대시/구르기 불가
|
||||
if (root == null || root.Action == null) return;
|
||||
if (IsMotionOnCooldown(root.Action)) return;
|
||||
|
||||
@@ -652,6 +666,7 @@ private async Awaitable MotionRoutine(ActionData data, CancellationToken token)
|
||||
|
||||
private async void PerformGroundPound()
|
||||
{
|
||||
if (IsStunned()) return; // 경직 중 그라운드 파운드 불가
|
||||
if (_groundPoundData == null) return;
|
||||
|
||||
CancelAttack();
|
||||
@@ -1531,7 +1546,9 @@ private bool TryGetGroundHit(float checkDistance, out RaycastHit2D closestHit)
|
||||
float inset = Mathf.Min(Mathf.Max(_groundCheckRayInset, 0f), bounds.extents.x);
|
||||
float leftX = bounds.min.x + inset;
|
||||
float rightX = bounds.max.x - inset;
|
||||
float originY = bounds.min.y;
|
||||
float originLift = bounds.size.y + Mathf.Max(_collisionSkinWidth, 0f) + 0.01f;
|
||||
float originY = bounds.min.y + originLift;
|
||||
float rayDistance = checkDistance + originLift;
|
||||
float closestDistance = float.PositiveInfinity;
|
||||
bool hasHit = false;
|
||||
|
||||
@@ -1541,7 +1558,7 @@ private bool TryGetGroundHit(float checkDistance, out RaycastHit2D closestHit)
|
||||
Vector2 origin = new Vector2(Mathf.Lerp(leftX, rightX, t), originY);
|
||||
|
||||
_castResults.Clear();
|
||||
int hitCount = Physics2D.Raycast(origin, Vector2.down, filter, _castResults, checkDistance);
|
||||
int hitCount = Physics2D.Raycast(origin, Vector2.down, filter, _castResults, rayDistance);
|
||||
for (int j = 0; j < hitCount; j++)
|
||||
{
|
||||
RaycastHit2D hit = _castResults[j];
|
||||
@@ -1695,6 +1712,64 @@ private void DrawTimelineBar(ActionData data, float elapsed)
|
||||
GUI.color = Color.white;
|
||||
}
|
||||
|
||||
// 외부(보스 스킬 등)에서 플레이어의 지면 접촉 여부를 조회.
|
||||
public bool IsGrounded => _isGrounded;
|
||||
|
||||
// 외부에서 플레이어를 강제로 띄우거나 밀어낼 때 사용 (지진 스킬 등).
|
||||
// 넉백 = 지정 속도로 띄우고 넉백 모션으로 경직시킨다. 키네마틱 RB라 속도 직접 설정.
|
||||
public void Launch(Vector2 velocity)
|
||||
{
|
||||
if (_rb == null) return;
|
||||
_rb.linearVelocity = velocity;
|
||||
EnterHitstun(_knockbackAnimationState, _knockbackDuration);
|
||||
}
|
||||
|
||||
// 피격 경직 진행 여부. 경직 중에는 이동/방향전환/점프/공격/대시 입력을 모두 잠근다.
|
||||
private bool IsStunned() => _isStunned;
|
||||
|
||||
// 피격 경직(범용). 지정 모션을 재생하고 duration초 동안 입력을 잠근다. 재호출 시 갱신(refresh).
|
||||
// 진행 중이던 플레이어 액션은 취소한다 (피격이 우선).
|
||||
// duration은 호출자가 모션 클립 길이에 맞춰 넘긴다 (넉백은 _knockbackDuration).
|
||||
// 일반 경직 / 넉백 등 종류별로 animationState만 바꿔 호출하면 된다.
|
||||
public void EnterHitstun(string animationState, float duration)
|
||||
{
|
||||
_isStunned = true;
|
||||
_hitstunTimer = duration; // 경직 시간 (재호출 시 갱신/refresh)
|
||||
|
||||
CancelAttack(); // 진행 중이던 공격/모션을 끊는다 (피격이 우선)
|
||||
CancelMotion();
|
||||
|
||||
// 경직 모션은 액션 애니메이션 취급 — _isInActionAnimation으로 locomotion이 못 덮게 막는다.
|
||||
if (_anim != null && !string.IsNullOrEmpty(animationState))
|
||||
{
|
||||
_isInActionAnimation = true;
|
||||
_activeBaseState = null;
|
||||
_anim.speed = 1f;
|
||||
_anim.Play(animationState);
|
||||
_anim.Update(0f);
|
||||
}
|
||||
}
|
||||
|
||||
// 경직 타이머 카운트다운. 클립을 다 재생했고(타이머 종료) + 착지했을 때만 경직을 해제한다.
|
||||
// 넉백으로 공중에 떠 있는 동안엔 경직/모션을 유지 → 점프 모션으로 안 끊긴다.
|
||||
private void TickHitstun()
|
||||
{
|
||||
if (!_isStunned) return;
|
||||
|
||||
if (_hitstunTimer > 0f)
|
||||
_hitstunTimer -= Time.fixedDeltaTime;
|
||||
|
||||
Debug.Log($"_hitstunTimer : {_hitstunTimer}, _isGrounded : {_isGrounded} ");
|
||||
|
||||
if (_hitstunTimer <= 0f && _isGrounded && !IsActionActive())
|
||||
{
|
||||
_isStunned = false;
|
||||
_isInActionAnimation = false;
|
||||
_activeBaseState = null;
|
||||
_hitstunTimer = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public void TakeDamage(int amount, Vector2 hitVelocity = default, string hitReactionAnimationState = null, Vector2? hitTargetPosition = null, bool correctHitTargetY = false, int hitPositionSolidMask = 0, float hitPositionCorrectionDuration = 0, float hitStunDuration = -1f)
|
||||
{
|
||||
if (_health == null || _health.IsDead) return;
|
||||
|
||||
Reference in New Issue
Block a user