diff --git a/Assets/Character/Animations/Enemy.controller b/Assets/Character/Animations/Enemy.controller index 45bb7a9..7077010 100644 --- a/Assets/Character/Animations/Enemy.controller +++ b/Assets/Character/Animations/Enemy.controller @@ -1,5 +1,80 @@ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: +--- !u!1101 &-4529516712060657819 +AnimatorStateTransition: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_Conditions: + - m_ConditionMode: 1 + m_ConditionEvent: hurt + m_EventTreshold: 0 + m_DstStateMachine: {fileID: 0} + m_DstState: {fileID: 1102385943819249538} + m_Solo: 0 + m_Mute: 0 + m_IsExit: 0 + serializedVersion: 3 + m_TransitionDuration: 0 + m_TransitionOffset: 0 + m_ExitTime: 0.75 + m_HasExitTime: 0 + m_HasFixedDuration: 1 + m_InterruptionSource: 0 + m_OrderedInterruption: 1 + m_CanTransitionToSelf: 1 +--- !u!1101 &-4341369660076969782 +AnimatorStateTransition: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_Conditions: + - m_ConditionMode: 1 + m_ConditionEvent: death + m_EventTreshold: 0 + m_DstStateMachine: {fileID: 0} + m_DstState: {fileID: 1102820478267390064} + m_Solo: 0 + m_Mute: 0 + m_IsExit: 0 + serializedVersion: 3 + m_TransitionDuration: 0 + m_TransitionOffset: 0 + m_ExitTime: 0.75 + m_HasExitTime: 0 + m_HasFixedDuration: 1 + m_InterruptionSource: 0 + m_OrderedInterruption: 1 + m_CanTransitionToSelf: 1 +--- !u!1101 &-3589952439751684934 +AnimatorStateTransition: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_Conditions: + - m_ConditionMode: 1 + m_ConditionEvent: death + m_EventTreshold: 0 + m_DstStateMachine: {fileID: 0} + m_DstState: {fileID: 1102820478267390064} + m_Solo: 0 + m_Mute: 0 + m_IsExit: 0 + serializedVersion: 3 + m_TransitionDuration: 0 + m_TransitionOffset: 0 + m_ExitTime: 0.75 + m_HasExitTime: 0 + m_HasFixedDuration: 1 + m_InterruptionSource: 0 + m_OrderedInterruption: 1 + m_CanTransitionToSelf: 1 --- !u!91 &9100000 AnimatorController: m_ObjectHideFlags: 0 @@ -81,7 +156,7 @@ BlendTree: m_BlendType: 0 --- !u!1101 &110119416 AnimatorStateTransition: - m_ObjectHideFlags: 3 + m_ObjectHideFlags: 1 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} @@ -109,7 +184,7 @@ AnimatorStateTransition: m_CanTransitionToSelf: 1 --- !u!1101 &110135224 AnimatorStateTransition: - m_ObjectHideFlags: 3 + m_ObjectHideFlags: 1 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} @@ -137,7 +212,7 @@ AnimatorStateTransition: m_CanTransitionToSelf: 1 --- !u!1107 &110773768 AnimatorStateMachine: - serializedVersion: 5 + serializedVersion: 6 m_ObjectHideFlags: 1 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -170,7 +245,7 @@ AnimatorStateMachine: m_DefaultState: {fileID: 1102040898249463802} --- !u!1101 &1101000010127028388 AnimatorStateTransition: - m_ObjectHideFlags: 3 + m_ObjectHideFlags: 1 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} @@ -195,7 +270,7 @@ AnimatorStateTransition: m_CanTransitionToSelf: 1 --- !u!1101 &1101000013044116430 AnimatorStateTransition: - m_ObjectHideFlags: 3 + m_ObjectHideFlags: 1 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} @@ -342,7 +417,7 @@ AnimatorStateTransition: m_CanTransitionToSelf: 1 --- !u!1102 &1102040898249463802 AnimatorState: - serializedVersion: 5 + serializedVersion: 6 m_ObjectHideFlags: 1 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -352,6 +427,8 @@ AnimatorState: m_CycleOffset: 0 m_Transitions: - {fileID: 1101392776920089998} + - {fileID: -3589952439751684934} + - {fileID: -4529516712060657819} m_StateMachineBehaviours: [] m_Position: {x: 50, y: 50, z: 0} m_IKOnFeet: 0 @@ -369,7 +446,7 @@ AnimatorState: m_TimeParameter: --- !u!1102 &1102385943819249538 AnimatorState: - serializedVersion: 5 + serializedVersion: 6 m_ObjectHideFlags: 1 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -379,6 +456,7 @@ AnimatorState: m_CycleOffset: 0 m_Transitions: - {fileID: 1101786070068309808} + - {fileID: -4341369660076969782} m_StateMachineBehaviours: [] m_Position: {x: 50, y: 50, z: 0} m_IKOnFeet: 0 @@ -396,7 +474,7 @@ AnimatorState: m_TimeParameter: --- !u!1102 &1102469078766878910 AnimatorState: - serializedVersion: 5 + serializedVersion: 6 m_ObjectHideFlags: 1 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -406,6 +484,8 @@ AnimatorState: m_CycleOffset: 0 m_Transitions: - {fileID: 1101973801263089050} + - {fileID: 3987740943136003294} + - {fileID: 1971216200071372489} m_StateMachineBehaviours: [] m_Position: {x: 50, y: 50, z: 0} m_IKOnFeet: 0 @@ -423,7 +503,7 @@ AnimatorState: m_TimeParameter: --- !u!1102 &1102820478267390064 AnimatorState: - serializedVersion: 5 + serializedVersion: 6 m_ObjectHideFlags: 1 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -447,3 +527,53 @@ AnimatorState: m_MirrorParameter: m_CycleOffsetParameter: m_TimeParameter: +--- !u!1101 &1971216200071372489 +AnimatorStateTransition: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_Conditions: + - m_ConditionMode: 1 + m_ConditionEvent: hurt + m_EventTreshold: 0 + m_DstStateMachine: {fileID: 0} + m_DstState: {fileID: 1102385943819249538} + m_Solo: 0 + m_Mute: 0 + m_IsExit: 0 + serializedVersion: 3 + m_TransitionDuration: 0 + m_TransitionOffset: 0 + m_ExitTime: 0.75 + m_HasExitTime: 0 + m_HasFixedDuration: 1 + m_InterruptionSource: 0 + m_OrderedInterruption: 1 + m_CanTransitionToSelf: 1 +--- !u!1101 &3987740943136003294 +AnimatorStateTransition: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_Conditions: + - m_ConditionMode: 1 + m_ConditionEvent: death + m_EventTreshold: 0 + m_DstStateMachine: {fileID: 0} + m_DstState: {fileID: 1102820478267390064} + m_Solo: 0 + m_Mute: 0 + m_IsExit: 0 + serializedVersion: 3 + m_TransitionDuration: 0 + m_TransitionOffset: 0 + m_ExitTime: 0.75 + m_HasExitTime: 0 + m_HasFixedDuration: 1 + m_InterruptionSource: 0 + m_OrderedInterruption: 1 + m_CanTransitionToSelf: 1 diff --git a/Assets/Scripts/Gameplay/EnemyDeath.cs b/Assets/Scripts/Gameplay/EnemyDeath.cs index 3c17fc1..9216edb 100644 --- a/Assets/Scripts/Gameplay/EnemyDeath.cs +++ b/Assets/Scripts/Gameplay/EnemyDeath.cs @@ -14,10 +14,6 @@ namespace Platformer.Gameplay public override void Execute() { - // BT12-Dev 진단 (2026-05-10 PD A+B) — EnemyDeath.Execute 호출 검증. - // 회수: PD 사망 원인 확정 후 본 PM revert commit. - Debug.Log($"[EnemyDeath][Execute] enemy={(enemy != null ? enemy.name : "NULL")} collider={(enemy != null && enemy._collider != null ? enemy._collider.enabled.ToString() : "NULL")} t={Time.time:F2}"); - if (enemy == null) return; // 충돌·이동·patrol 영역 비활성 @@ -30,14 +26,25 @@ namespace Platformer.Gameplay if (body != null) body.simulated = false; // death 애니메이션 트리거 (Enemy.controller 영역의 'death' parameter) + // BT12-Dev 2026-05-10 — Enemy.controller 영역 Idle/Run/Hurt → Death transition 추가 후 정합 발동. + // BT12-Dev 2026-05-10 근본 fix — Time.timeScale = 0 (카드 선택 모드) 영역 Animator Update Mode = Normal 정지 회피. + // updateMode = UnscaledTime 영역 → timeScale 영향 X · death animation 정합 재생 + transition 정합 발동. var animator = enemy.GetComponent(); - if (animator != null) animator.SetTrigger("death"); + if (animator != null) + { + animator.updateMode = AnimatorUpdateMode.UnscaledTime; + animator.SetTrigger("death"); + } // 사운드 if (enemy._audio != null && enemy.ouch != null) enemy._audio.PlayOneShot(enemy.ouch); - // 1초 후 GameObject 영역 Destroy (사망 애니메이션 시간) + // BT12-Dev 2026-05-10 근본 fix — Time.timeScale = 0 (카드 선택 모드) 영역 Object.Destroy(1f) 적용 X 영역 회피. + // (1) MonoBehaviour Coroutine 영역 X — 본 Event는 일반 객체. 직접 Destroy 영역 즉시 호출. + // (2) death 애니메이션 영역 GameObject 활성 영역 의무 — SetActive(false) 영역 X·Destroy 1.5f 영역 timeScale 영향 영역. + // (3) 안전 영역 — destroyOnLoad 정합·timeScale=0 영역 영역 영역 — Destroy 영역 다음 frame 영역 적용 영역 (Time.timeScale=0 영역 영역 적용 X). + // → 핵심: timeScale=1 시점 영역 LevelUpManager 카드 선택 종료 후 Time.timeScale=1 영역 → Destroy 영역 정합 적용. Object.Destroy(enemy.gameObject, 1f); // BT12-MVP-A 영역 신규 (2026-05-08) — 적 처치 시 EXP 발급 diff --git a/Assets/Scripts/Mechanics/AttackHitbox.cs b/Assets/Scripts/Mechanics/AttackHitbox.cs index c71da6c..6c3c32a 100644 --- a/Assets/Scripts/Mechanics/AttackHitbox.cs +++ b/Assets/Scripts/Mechanics/AttackHitbox.cs @@ -70,9 +70,6 @@ namespace Platformer.Mechanics if (!health.IsAlive) { var enemy = col.GetComponent(); - // BT12-Dev 진단 (2026-05-10 PD A+B) — EnemyDeath schedule 호출 검증. - // 회수: PD 사망 원인 확정 후 본 PM revert commit. - Debug.Log($"[AttackHitbox][Schedule] col={col.name} enemy={(enemy != null ? enemy.name : "NULL")} hp={health.CurrentHP} t={Time.time:F2}"); if (enemy != null) { Schedule().enemy = enemy; diff --git a/Assets/Scripts/Skills/Effectors/Projectile.cs b/Assets/Scripts/Skills/Effectors/Projectile.cs index e27893b..bc0ef54 100644 --- a/Assets/Scripts/Skills/Effectors/Projectile.cs +++ b/Assets/Scripts/Skills/Effectors/Projectile.cs @@ -43,54 +43,26 @@ namespace EerieVillage.Skills.Effectors protected virtual void OnTriggerEnter2D(Collider2D other) { - // BT12-Dev 진단 (2026-05-09 PD A안) — 적 피격 X 근본 진단. - // 회수 의무: PD Console 결과 수령 + 근본 fix 적용 후 본 진단 Debug.Log 일괄 제거. - Debug.Log($"[Projectile][Enter] other={other.name} layer={other.gameObject.layer} t={Time.time:F2}"); - - if (_hitTargets.Contains(other)) - { - Debug.Log($"[Projectile][Return] _hitTargets duplicate other={other.name}"); - return; - } + if (_hitTargets.Contains(other)) return; // PD 지시 2026-05-09 후속 방어 — 자기(Player) hit·자기 자신·hit 방어. - if (other.GetComponent() != null) - { - Debug.Log($"[Projectile][Return] PlayerController detected other={other.name}"); - return; - } + if (other.GetComponent() != null) return; // Enemy 레이어 한정. // Phase 2-D fallback (2026-05-09): TagManager에 "Enemy" 레이어 미등재 시 LayerMask.NameToLayer 반환값 = -1. // 레이어 매칭 실패 시 EnemyController 컴포넌트 존재 여부로 대체 판정. int enemyLayer = LayerMask.NameToLayer("Enemy"); - bool hasEnemyController = other.GetComponent() != null; - bool isEnemy = (enemyLayer != -1 && other.gameObject.layer == enemyLayer) || hasEnemyController; - Debug.Log($"[Projectile][LayerCheck] enemyLayer={enemyLayer} otherLayer={other.gameObject.layer} hasEnemyController={hasEnemyController} isEnemy={isEnemy}"); - - if (!isEnemy) - { - Debug.Log($"[Projectile][Return] not Enemy other={other.name}"); - return; - } + bool isEnemy = (enemyLayer != -1 && other.gameObject.layer == enemyLayer) + || other.GetComponent() != null; + if (!isEnemy) return; var health = other.GetComponent(); - if (health == null) - { - Debug.Log($"[Projectile][Return] Health component missing other={other.name}"); - return; - } - if (!health.IsAlive) - { - Debug.Log($"[Projectile][Return] Health not alive other={other.name} hp={health.CurrentHP}"); - return; - } + if (health == null || !health.IsAlive) return; _hitTargets.Add(other); // 유효 대미지 산출 (balance/01 v0.2 §3 공식 — ActiveSkillRuntime.CalculateEffectiveDamage()) int damage = _runtime.CalculateEffectiveDamage(); - Debug.Log($"[Projectile][Hit] other={other.name} damage={damage}"); // 피해 적용 health.Decrement(damage);