Project_WL/Assets/WL/Combat/Weapon/WLHitboxFit.cs

74 lines
5.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ─────────────────────────────────────────────────────────────────────────────
// 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 = ""; }
/// <summary>풀 인스턴스의 원본 상자 값(처음 본 값)을 기억한다.</summary>
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<TextAsset>(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; }
/// <summary>원본 Actor.Shoot_Projectile 훅 — 판정 상자가 만들어진 직후.</summary>
public static void Apply(Actor shooter, ProjectileBase pb)
{
try
{
if (pb == null) return;
var bc = pb.GetComponent<BoxCollider>(); if (bc == null) return;
var b = pb.GetComponent<WLHitboxBase>(); if (b == null) b = pb.gameObject.AddComponent<WLHitboxBase>();
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<WL.Combat.Core.ComboTracker>();
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; }
}
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; }
}
}
}