feat(BT12-Dev): 2차 판정 박스 옵션 + A12 정화의 빛 상·하 (PD 지시 2026-05-14)

PD 요청:
1. 정화의 빛 (A12) 판정 범위 2개 필요
2. 모든 스킬 영역 — 체크박스로 추가 판정 범위 지정 가능

ActiveSkillData 신규 필드 (Header "2차 판정 박스 (선택)"):
- bool EnableSecondHitbox  체크박스 (기본 false)
- Vector2 SecondHitboxSize (기본 (1.5, 1.0))
- Vector2 SecondOffsetDistance (기본 (0, 0))

MeleeAreaSpawner.cs 분리·일반화:
- SpawnHitboxVisual static helper 신규 (1차·2차 박스 공통)
- DoOverlapBoxAt static helper 신규 (1차·2차 판정 공통)
- Trigger 영역 EnableSecondHitbox 분기 → 2차 박스·이펙트 위치 spawn
- DoOverlapBoxFromPlayer 영역 EnableSecondHitbox 분기 → 2차 판정 위치 OverlapBox
- facing sign 1차·2차 동일 적용 (좌/우 일관 반전)

A12_jeonghwauibit.asset placeholder (PD Inspector 미세 조정 가능):
- HitboxSize: (4,4) → (1.5, 5) 1차 vertical 줄기
- OffsetDistance: (0,0) → (0, 3) Player 위쪽 3
- EnableSecondHitbox: 1
- SecondHitboxSize: (1.5, 5) 2차 vertical 줄기
- SecondOffsetDistance: (0, -3) Player 아래쪽 3

검증 (Play 모드):
- A12 발사 → MeleeHitbox_Debug + MeleeHitbox_Debug2 동시 spawn
- 1차 pos.y = Player(-2.18) + offset.y(+3) = 0.82 ✓
- 2차 pos.y = Player(-2.18) + offset.y(-3) = -5.18 ✓
- 각 박스 scale = (1.5, 5) vertical 줄기 정합

후속: Laser·Lightning·Projectile spawner 영역 SecondHitbox 적용은
PD 요청 발생 시점에 동일 패턴 (Helper extract → 2차 분기) 정합.
공통 필드는 본 commit 으로 일괄 추가.

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

View File

@ -26,8 +26,11 @@ MonoBehaviour:
Trigger: 0 Trigger: 0
BaseCooldown: 5 BaseCooldown: 5
BaseDamage: 15 BaseDamage: 15
HitboxSize: {x: 4, y: 4} HitboxSize: {x: 1.5, y: 5}
OffsetDistance: {x: 0, y: 0} OffsetDistance: {x: 0, y: 3}
EnableSecondHitbox: 1
SecondHitboxSize: {x: 1.5, y: 5}
SecondOffsetDistance: {x: 0, y: -3}
Trajectory: 0 Trajectory: 0
MinionPrefab: {fileID: 0} MinionPrefab: {fileID: 0}
ChainCount: 0 ChainCount: 0

View File

@ -27,6 +27,17 @@ namespace EerieVillage.Skills
[Tooltip("히트박스 위치 오프셋 (anchor 기준 X·Y 절대). 투사체 영역 X=facing 방향 거리·Y=직각 거리")] [Tooltip("히트박스 위치 오프셋 (anchor 기준 X·Y 절대). 투사체 영역 X=facing 방향 거리·Y=직각 거리")]
public Vector2 OffsetDistance = new Vector2(0.5f, 0f); public Vector2 OffsetDistance = new Vector2(0.5f, 0f);
// PD 지시 2026-05-14 — 2차 판정 박스 (A12 정화의 빛 상·하 관통 등 양방향 판정 필요 케이스)
[Header("2차 판정 박스 (선택)")]
[Tooltip("체크 시 SecondHitboxSize·SecondOffsetDistance 위치에 추가 판정 박스 spawn (예: A12 정화의 빛 상·하)")]
public bool EnableSecondHitbox = false;
[Tooltip("2차 판정 박스 크기")]
public Vector2 SecondHitboxSize = new Vector2(1.5f, 1.0f);
[Tooltip("2차 판정 박스 위치 오프셋 (anchor 기준 X·Y 절대·facing sign 동일 적용)")]
public Vector2 SecondOffsetDistance = new Vector2(0f, 0f);
[Tooltip("투사체 전용 (A 카테고리). 궤적 타입 — Line·Homing·Arc")] [Tooltip("투사체 전용 (A 카테고리). 궤적 타입 — Line·Homing·Arc")]
public ProjectileTrajectory Trajectory = ProjectileTrajectory.Line; public ProjectileTrajectory Trajectory = ProjectileTrajectory.Line;

View File

@ -54,25 +54,43 @@ namespace EerieVillage.Skills.Effectors
int damage = Mathf.Max(runtime.CalculateEffectiveDamage(), data.BaseDamage); int damage = Mathf.Max(runtime.CalculateEffectiveDamage(), data.BaseDamage);
float duration = Mathf.Max(data.BaseCooldown, 1f); float duration = Mathf.Max(data.BaseCooldown, 1f);
var boxGo = new GameObject("MeleeHitbox_Debug");
boxGo.hideFlags = HideFlags.DontSave;
boxGo.transform.SetParent(inventory.transform, false);
float lpx = inventory.transform.lossyScale.x != 0f ? Mathf.Abs(inventory.transform.lossyScale.x) : 1f; float lpx = inventory.transform.lossyScale.x != 0f ? Mathf.Abs(inventory.transform.lossyScale.x) : 1f;
float lpy = inventory.transform.lossyScale.y != 0f ? Mathf.Abs(inventory.transform.lossyScale.y) : 1f; float lpy = inventory.transform.lossyScale.y != 0f ? Mathf.Abs(inventory.transform.lossyScale.y) : 1f;
// PD 지시 2026-05-13 — 박스(판정) = facing 좌/우 sign 만 반영 · FxRotation 미적용 (시각 전용)
float signX = facing.x < 0f ? -1f : 1f; float signX = facing.x < 0f ? -1f : 1f;
boxGo.transform.localPosition = new Vector3(signX * data.OffsetDistance.x / lpx, data.OffsetDistance.y / lpy, 0f);
// 1차 판정 박스
SpawnHitboxVisual(inventory, "MeleeHitbox_Debug",
signX * data.OffsetDistance.x, data.OffsetDistance.y,
hitboxSize, lpx, lpy, duration);
// PD 지시 2026-05-14 — 2차 판정 박스 (A12 정화의 빛 등 양방향 판정)
if (data.EnableSecondHitbox)
{
SpawnHitboxVisual(inventory, "MeleeHitbox_Debug2",
signX * data.SecondOffsetDistance.x, data.SecondOffsetDistance.y,
data.SecondHitboxSize, lpx, lpy, duration);
}
// PD 지시 2026-05-13 — DamageFrameDelay·반복 피해 영역 정합 (Player 영역 매 hit 시 영역 영역 영역)
inventory.StartCoroutine(MeleeFixedHitDamageCoroutine(inventory, data, damage));
}
// PD 지시 2026-05-14 — 박스 시각화 공통 helper (1차 + 2차 박스 일관 정합)
static void SpawnHitboxVisual(PlayerSkillInventory inventory, string goName,
float localX, float localY, Vector2 size, float lpx, float lpy, float duration)
{
var boxGo = new GameObject(goName);
boxGo.hideFlags = HideFlags.DontSave;
boxGo.transform.SetParent(inventory.transform, false);
boxGo.transform.localPosition = new Vector3(localX / lpx, localY / lpy, 0f);
boxGo.transform.localRotation = Quaternion.identity; boxGo.transform.localRotation = Quaternion.identity;
boxGo.transform.localScale = new Vector3(hitboxSize.x / lpx, hitboxSize.y / lpy, 1f); boxGo.transform.localScale = new Vector3(size.x / lpx, size.y / lpy, 1f);
var sr = boxGo.AddComponent<SpriteRenderer>(); var sr = boxGo.AddComponent<SpriteRenderer>();
sr.sprite = HitboxDebug.GetWhiteSprite(); sr.sprite = HitboxDebug.GetWhiteSprite();
sr.color = new Color(1f, 0f, 0f, 0.35f); sr.color = new Color(1f, 0f, 0f, 0.35f);
sr.sortingOrder = 100; sr.sortingOrder = 100;
sr.enabled = HitboxDebug.ShowDebugVisuals; sr.enabled = HitboxDebug.ShowDebugVisuals;
Object.Destroy(boxGo, duration); Object.Destroy(boxGo, duration);
// PD 지시 2026-05-13 — DamageFrameDelay·반복 피해 영역 정합 (Player 영역 매 hit 시 영역 영역 영역)
inventory.StartCoroutine(MeleeFixedHitDamageCoroutine(inventory, data, damage));
} }
static System.Collections.IEnumerator MeleeFixedHitDamageCoroutine(PlayerSkillInventory inventory, ActiveSkillData data, int damage) static System.Collections.IEnumerator MeleeFixedHitDamageCoroutine(PlayerSkillInventory inventory, ActiveSkillData data, int damage)
@ -100,12 +118,26 @@ namespace EerieVillage.Skills.Effectors
var pc = inventory.GetComponent<PlayerController>(); var pc = inventory.GetComponent<PlayerController>();
if (pc != null) facing = pc.Facing; if (pc != null) facing = pc.Facing;
float signX = facing.x < 0f ? -1f : 1f; float signX = facing.x < 0f ? -1f : 1f;
Vector2 hitboxPos = (Vector2)inventory.transform.position Vector2 playerPos = inventory.transform.position;
+ new Vector2(signX * data.OffsetDistance.x, data.OffsetDistance.y);
// 1차 판정
DoOverlapBoxAt(playerPos + new Vector2(signX * data.OffsetDistance.x, data.OffsetDistance.y),
data.HitboxSize, damage);
// PD 지시 2026-05-14 — 2차 판정 박스
if (data.EnableSecondHitbox)
{
DoOverlapBoxAt(playerPos + new Vector2(signX * data.SecondOffsetDistance.x, data.SecondOffsetDistance.y),
data.SecondHitboxSize, damage);
}
}
static void DoOverlapBoxAt(Vector2 pos, Vector2 size, int damage)
{
var cf = new ContactFilter2D(); var cf = new ContactFilter2D();
cf.useTriggers = false; cf.useTriggers = false;
var results = new Collider2D[32]; var results = new Collider2D[32];
int n = Physics2D.OverlapBox(hitboxPos, data.HitboxSize, 0f, cf, results); int n = Physics2D.OverlapBox(pos, size, 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];