// ───────────────────────────────────────────────────────────────────────────── // WLHitboxFit.cs — 기본 공격 판정 상자를 직업 · 타수별 궤적 길이에 맞춘다 — 표 기반 (#904 · 2026-10-17) // // PD 원문: 「소드 공격은 궤적 범위에 비해 실제 판정 범위가 너무 넓은 것 같아. 공격 판정 영역(공격 가능 사정거리)을 궤적에 맞게 수정 // (검 2타 공격의 공격 범위가 모자라는 부분은 2타 공격 시점에 전진하며 맞출 수 있게 · 다른 근접 무기도 타수마다 사정거리가 다르면 // 전진하며 맞추거나 궤적을 늘리는 등으로 자연스러운 타격)」 // ■ 원리 — 원본 Actor.Shoot_Projectile 훅 1줄이 판정 상자(Projectile_101xx 등 · 원본 = PC 몸 중심 앞 0.3 m · BoxCollider 깊이 2 m · 중심 z 0.5 // = PC 정면 −0.2 ~ 1.8 m)를 만든 직후 부른다. 메인 PC 직업 · 콤보 단계가 표에 있으면 상자의 앞 끝을 frontReach(PC 몸 중심에서 정면 m)로, // 뒤 끝은 원본 그대로 두고 깊이 · 중심만 바꾼다(너비 · 높이 그대로). 표에 없으면 원본 값으로 되돌린다(풀 인스턴스를 창 등 다른 직업도 같이 쓴다). // ■ 값 = Resources/WL/WLHitboxFit.json (C45 · frontReach = Play 실측 궤적 정면 도달) · 끄기 = enabled 0 // ───────────────────────────────────────────────────────────────────────────── using UnityEngine; namespace WL.Combat.Weapon { [System.Serializable] public class WLHitboxRule { public int[] classIds = new int[0]; public int stage; public float frontReach; } [System.Serializable] public class WLHitboxJson { public int enabled; public float spawnForward = 0.3f; public WLHitboxRule[] rules = new WLHitboxRule[0]; public string note = ""; } /// 풀 인스턴스의 원본 상자 값(처음 본 값)을 기억한다. public sealed class WLHitboxBase : MonoBehaviour { public bool has; public Vector3 size, center; } public static class WLHitboxFit { public const string ResourcesPath = "WL/WLHitboxFit"; public static int Fitted, Restored; public static string LastLog = ""; static WLHitboxJson s_cfg; static WLHitboxJson Cfg { get { if (s_cfg != null) return s_cfg; s_cfg = new WLHitboxJson(); var ta = Resources.Load(ResourcesPath); if (ta == null) { LastLog = "설정 없음 " + ResourcesPath; return s_cfg; } try { JsonUtility.FromJsonOverwrite(ta.text, s_cfg); } catch (System.Exception ex) { LastLog = "설정 파싱 실패 — " + ex.Message; } return s_cfg; } } [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] static void Boot() { s_cfg = null; } /// 원본 Actor.Shoot_Projectile 훅 — 판정 상자가 만들어진 직후. public static void Apply(Actor shooter, ProjectileBase pb) { try { if (pb == null) return; var bc = pb.GetComponent(); if (bc == null) return; var b = pb.GetComponent(); if (b == null) b = pb.gameObject.AddComponent(); if (!b.has) { b.has = true; b.size = bc.size; b.center = bc.center; } WLHitboxRule hit = null; var c = Cfg; var pc = shooter as PCActor; if (c.enabled != 0 && pc != null && pc.IsMainPC() && c.rules != null) { var ct = pc.GetComponent(); int stage = ct != null ? ct.LastStage : 0, cls = pc.Get_ID(); foreach (var r in c.rules) if (r != null && r.frontReach > 0f && r.classIds != null && System.Array.IndexOf(r.classIds, cls) >= 0 && (r.stage == 0 || r.stage == stage)) { hit = r; if (r.stage == stage) break; } } // #1004 PD 「공격 판정 범위를 줄이라 (팔이 늘어나 공격하는 거리가 길기 때문에)」 — 마법책 용암 골렘(WLSummonMelee)은 앞 끝 = summonHitReach(몸 중심 기준) · 뒤 끝 원본 var sm = shooter != null ? shooter.GetComponent() : null; if (hit == null && sm != null && sm.HitReach > 0f) { float sb = b.center.z - b.size.z * 0.5f, sf = Mathf.Max(sb + 0.1f, sm.HitReach - c.spawnForward); bc.size = new Vector3(b.size.x, b.size.y, sf - sb); bc.center = new Vector3(b.center.x, b.center.y, (sf + sb) * 0.5f); Fitted++; LastLog = pb.name + " 소환 " + shooter.name + " 앞 끝 " + sm.HitReach.ToString("0.##") + " m"; return; } if (hit == null) { if (bc.size != b.size || bc.center != b.center) { bc.size = b.size; bc.center = b.center; Restored++; } return; } // 뒤 끝(원본) 고정 · 앞 끝 = frontReach(PC 몸 중심 기준) − 생성 위치 앞쪽 거리(spawnForward) float back = b.center.z - b.size.z * 0.5f; float front = Mathf.Max(back + 0.1f, hit.frontReach - c.spawnForward); bc.size = new Vector3(b.size.x, b.size.y, front - back); bc.center = new Vector3(b.center.x, b.center.y, (front + back) * 0.5f); Fitted++; LastLog = pb.name + " cls " + pc.Get_ID() + " 단계 " + hit.stage + " 앞 끝 " + hit.frontReach.ToString("0.##") + " m"; } catch (System.Exception ex) { LastLog = "실패 — " + ex.Message; } } } }