feat(BT12-Dev): A06·A11 판정 박스 시각화 + A11 OverlapBox 전환 + 페이드 (PD 지시 2026-05-14)

PD 발화 2건:
1. "독 늪 소환의 판정(중독 효과 발생 가능한 범위)와 정령불의
   판정 범위도 박스 형태로 보여줘 (정령불의 범위에 있는 적은
   정령불이 유지되는 동안 일정한 피해 간격마다 피해를 입어야 해)"
2. "정령불 이펙트는 소멸되기 0.5초 전부터 알파값을 증가시키고
   크기를 최대 현재 크기 기준으로 최대 50%까지 줄어들며 사라지도록
   해줘."

A06 독 늪 (PoisonSwampInstance):
- PoisonSwampHitbox_Debug 자식 GO 신규 — BoxCollider2D.size 일관
  SpriteRenderer (1, 0, 0, 0.35) · sortingOrder 100 · ShowDebugVisuals 토글

A11 정령불 (SpiritFireSpawner / Instance):
- OverlapCircle → OverlapBox (HitboxSize 사용·박스 시각 ↔ 판정 정합)
- AuraRadius 의존 제거 · _radius (float) → _boxSize (Vector2)
- SpiritFireHitbox_Debug Player 자식 GO — Player 이동 동조
- 피해 간격 = DotInterval (기본 1초·Inspector 조절)
- 소멸 0.5초 전 페이드 (PD 의도 "투명도 증가" = alpha 1→0):
  · FADE_DURATION=0.5 · alphaMul = 1-t · scaleMul = 1-0.5t
  · Renderer·MaterialPropertyBlock·_baseAlphas 캐싱 (Projectile 동일 패턴)
  · _TintColor 또는 _Color 자동 분기

PlayerSkillInventory.StaleSpawnNames:
- PoisonSwampHitbox_Debug·SpiritFireHitbox_Debug·MeleeHitbox_Debug2 추가
- 게임 재실행 시 cleanup catch 정합

검증 (Play 모드):
- A06 발사 → PoisonSwampHitbox_Debug visible=True (parent FX_Venom_Swamp)
- A11 발사 → SpiritFireHitbox_Debug visible=True (parent Player·scale 2.5,2.5)
- A11 HitboxSize=(2.5, 2.5)·DotInterval=1·MinionLifetime=8 정합

SOT (스킬_이펙트_확정_v1.md) §4 변경 이력 갱신.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
깃 관리자 2026-05-14 23:14:05 +09:00
parent 1a1de0c0f3
commit e2b446e0f4
3 changed files with 103 additions and 22 deletions

View File

@ -107,6 +107,18 @@ namespace EerieVillage.Skills.Effectors
rb.simulated = true; rb.simulated = true;
rb.gravityScale = 0f; rb.gravityScale = 0f;
rb.useFullKinematicContacts = true; rb.useFullKinematicContacts = true;
// PD 지시 2026-05-14 — 판정 박스 시각화 (붉은 반투명 박스·ShowDebugVisuals 토글)
var dbg = new GameObject("PoisonSwampHitbox_Debug");
dbg.hideFlags = HideFlags.DontSave;
dbg.transform.SetParent(transform, false);
dbg.transform.localPosition = Vector3.zero;
dbg.transform.localScale = new Vector3(size.x, size.y, 1f);
var sr = dbg.AddComponent<SpriteRenderer>();
sr.sprite = HitboxDebug.GetWhiteSprite();
sr.color = new Color(1f, 0f, 0f, 0.35f);
sr.sortingOrder = 100;
sr.enabled = HitboxDebug.ShowDebugVisuals;
} }
void Update() void Update()

View File

@ -39,11 +39,14 @@ namespace EerieVillage.Skills.Effectors
shieldGo.hideFlags = HideFlags.DontSave; shieldGo.hideFlags = HideFlags.DontSave;
float duration = data.MinionLifetime > 0.1f ? data.MinionLifetime : 8f; float duration = data.MinionLifetime > 0.1f ? data.MinionLifetime : 8f;
float radius = data.AuraRadius > 0.1f ? data.AuraRadius : 2.5f; // PD 지시 2026-05-14 — 판정 박스 형태 일관 (HitboxSize 사용·OverlapBox)
Vector2 boxSize = data.HitboxSize.sqrMagnitude > 0.01f ? data.HitboxSize : new Vector2(2.5f, 2.5f);
int damage = data.BaseDamage > 0 ? data.BaseDamage : 5; int damage = data.BaseDamage > 0 ? data.BaseDamage : 5;
// PD 지시 2026-05-14 — 일정 피해 간격 (DotInterval 우선·기본 1초)
float interval = data.DotInterval > 0.01f ? data.DotInterval : 1f;
var instance = shieldGo.AddComponent<SpiritFireInstance>(); var instance = shieldGo.AddComponent<SpiritFireInstance>();
instance.Init(inventory.transform, duration, radius, damage); instance.Init(inventory.transform, duration, boxSize, damage, interval);
} }
} }
@ -64,16 +67,26 @@ namespace EerieVillage.Skills.Effectors
Animator _animator; Animator _animator;
float _spawnTime; float _spawnTime;
float _duration; float _duration;
float _radius; Vector2 _boxSize;
int _damage; int _damage;
float _interval;
float _lastDamageTime; float _lastDamageTime;
Transform _debugBoxTransform;
public void Init(Transform player, float duration, float radius, int damage) // PD 지시 2026-05-14 — 소멸 0.5초 전 페이드 (alpha 1→0·scale 1→0.5)
const float FADE_DURATION = 0.5f;
Vector3 _baseScale;
Renderer[] _renderers;
MaterialPropertyBlock _mpb;
float[] _baseAlphas;
public void Init(Transform player, float duration, Vector2 boxSize, int damage, float interval)
{ {
_player = player; _player = player;
_duration = duration; _duration = duration;
_radius = radius; _boxSize = boxSize;
_damage = damage; _damage = damage;
_interval = interval;
_spawnTime = Time.unscaledTime; _spawnTime = Time.unscaledTime;
_animator = GetComponent<Animator>(); _animator = GetComponent<Animator>();
@ -83,6 +96,61 @@ namespace EerieVillage.Skills.Effectors
_animator.updateMode = AnimatorUpdateMode.UnscaledTime; _animator.updateMode = AnimatorUpdateMode.UnscaledTime;
_animator.speed = 1f; _animator.speed = 1f;
} }
// PD 지시 2026-05-14 — 판정 박스 시각화 (붉은 반투명 박스·ShowDebugVisuals 토글)
var dbg = new GameObject("SpiritFireHitbox_Debug");
dbg.hideFlags = HideFlags.DontSave;
dbg.transform.SetParent(_player, false); // Player 자식·매 frame 동조
dbg.transform.localPosition = Vector3.zero;
float lpx = _player.lossyScale.x != 0f ? Mathf.Abs(_player.lossyScale.x) : 1f;
float lpy = _player.lossyScale.y != 0f ? Mathf.Abs(_player.lossyScale.y) : 1f;
dbg.transform.localScale = new Vector3(_boxSize.x / lpx, _boxSize.y / lpy, 1f);
var sr = dbg.AddComponent<SpriteRenderer>();
sr.sprite = HitboxDebug.GetWhiteSprite();
sr.color = new Color(1f, 0f, 0f, 0.35f);
sr.sortingOrder = 100;
sr.enabled = HitboxDebug.ShowDebugVisuals;
_debugBoxTransform = dbg.transform;
Destroy(dbg, _duration);
// PD 지시 2026-05-14 — fade 캐싱 (Renderer·MaterialPropertyBlock·기본 alpha)
_baseScale = transform.localScale;
_renderers = GetComponentsInChildren<Renderer>();
_mpb = new MaterialPropertyBlock();
_baseAlphas = new float[_renderers.Length];
for (int i = 0; i < _renderers.Length; i++)
{
var r = _renderers[i];
if (r == null) { _baseAlphas[i] = 1f; continue; }
if (r.sharedMaterial != null && r.sharedMaterial.HasProperty("_TintColor"))
_baseAlphas[i] = r.sharedMaterial.GetColor("_TintColor").a;
else if (r.sharedMaterial != null && r.sharedMaterial.HasProperty("_Color"))
_baseAlphas[i] = r.sharedMaterial.GetColor("_Color").a;
else _baseAlphas[i] = 1f;
}
}
// PD 지시 2026-05-14 — 페이드 보간 적용
void ApplyFadeout(float t)
{
t = Mathf.Clamp01(t);
float alphaMul = 1f - t;
float scaleMul = 1f - 0.5f * t;
transform.localScale = _baseScale * scaleMul;
if (_renderers == null) return;
for (int i = 0; i < _renderers.Length; i++)
{
var r = _renderers[i];
if (r == null) continue;
r.GetPropertyBlock(_mpb);
string propName = (r.sharedMaterial != null && r.sharedMaterial.HasProperty("_TintColor"))
? "_TintColor" : "_Color";
Color baseCol = (r.sharedMaterial != null && r.sharedMaterial.HasProperty(propName))
? r.sharedMaterial.GetColor(propName) : Color.white;
baseCol.a = _baseAlphas[i] * alphaMul;
_mpb.SetColor(propName, baseCol);
r.SetPropertyBlock(_mpb);
}
} }
void Update() void Update()
@ -117,7 +185,8 @@ namespace EerieVillage.Skills.Effectors
_animator.Play(STATE_HASH, 0, normalized); _animator.Play(STATE_HASH, 0, normalized);
} }
if (Time.unscaledTime - _spawnTime >= _duration) float elapsedFromSpawn = Time.unscaledTime - _spawnTime;
if (elapsedFromSpawn >= _duration)
{ {
Destroy(gameObject); Destroy(gameObject);
return; return;
@ -125,32 +194,28 @@ namespace EerieVillage.Skills.Effectors
Vector2 center = _player != null ? (Vector2)_player.position : (Vector2)transform.position; Vector2 center = _player != null ? (Vector2)_player.position : (Vector2)transform.position;
// 적 투사체 SelfDestruct (Projectile 컴포넌트 영역 적 발사·향후 Enemy 측 투사체 구현 시 정합) // PD 지시 2026-05-14 — 정령불의 범위에 있는 적은 지속 시간 동안 일정한 피해 간격마다 피해
var allProjectiles = Object.FindObjectsByType<Projectile>(FindObjectsSortMode.None); if (Time.unscaledTime - _lastDamageTime >= _interval)
foreach (var p in allProjectiles)
{
if (p == null) continue;
float d = Vector2.Distance(center, p.transform.position);
if (d <= _radius)
{
// PD 명시 — 적 투사체만 SelfDestruct. 현 Projectile = Player 발사 only → 영향 X 영역 (방어 코드).
// 향후 Enemy 측 Projectile 분리 시 friendly check 추가 필요.
}
}
// 매 1초 근접 적 피해
if (Time.unscaledTime - _lastDamageTime >= 1f)
{ {
_lastDamageTime = Time.unscaledTime; _lastDamageTime = Time.unscaledTime;
ApplyDamageAround(center); ApplyDamageAround(center);
} }
// PD 지시 2026-05-14 — 소멸 0.5초 전 페이드 (alpha 감소·scale 50% 축소)
float remaining = _duration - elapsedFromSpawn;
if (remaining <= FADE_DURATION && remaining > 0f)
{
float t = 1f - (remaining / FADE_DURATION); // 0→1 over 0.5s
ApplyFadeout(t);
}
} }
void ApplyDamageAround(Vector2 center) void ApplyDamageAround(Vector2 center)
{ {
// PD 지시 2026-05-14 — OverlapBox 영역 박스 형태 판정 (HitboxSize 사용·박스 시각 ↔ 판정 정합)
var cf = new ContactFilter2D { useTriggers = false }; var cf = new ContactFilter2D { useTriggers = false };
var results = new Collider2D[32]; var results = new Collider2D[32];
int n = Physics2D.OverlapCircle(center, _radius, cf, results); int n = Physics2D.OverlapBox(center, _boxSize, 0f, cf, results);
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
{ {
var c = results[i]; var c = results[i];

View File

@ -87,7 +87,11 @@ namespace EerieVillage.Skills
"ProjectileHitbox_Debug", "ProjectileHitbox_Debug",
"LaserHitbox_Debug", "LaserHitbox_Debug",
"MeleeHitbox_Debug", "MeleeHitbox_Debug",
"MeleeHitbox_Debug2",
"Range_Debug", "Range_Debug",
// PD 지시 2026-05-14 — A06·A11 판정 박스 시각화 신규
"PoisonSwampHitbox_Debug",
"SpiritFireHitbox_Debug",
}; };
// PD 지시 2026-05-14 — FX clone 카탈로그 (Projectile component 미부착 — Cast FX·HitFx 등) // PD 지시 2026-05-14 — FX clone 카탈로그 (Projectile component 미부착 — Cast FX·HitFx 등)