2026-10-19 13:23:08 +00:00
// ─────────────────────────────────────────────────────────────────────────────
// WLOrbFloat.cs — 오브 무기(맹약 · Res_Addr/Weapon/Orb.prefab)를 손 위에 띄운다 (#944 · 2026-10-19)
/ /
// PD 원문: 「오브(손에 빛나는 원형 이펙트)의 크기를 눈에 띄게 5개[배] 키워줘」 · 레퍼런스 = 펼친 손바닥 위에 빛나는 고리가 떠서 천천히 흔들림
// 「아트 컨셉은 무시해도 되며 오브의 위치 및 동작 형태만 참고할 것」
// ■ 원리 — 무기는 PCActor.Make_Weapon(Addressables 콜백)으로 손 소켓 자식에 붙고, WLWeaponFitter 가 LateUpdate 소켓 스캔으로 새 자식을 1회 보정한다.
// 그 자리(Fit 직후)에서 이름이 weaponNames 에 든 무기에 이 컴포넌트를 1회 붙인다(훅 1줄). 이 컴포넌트는 LateUpdate(애니메이터 뒤)마다
// 월드 위치 = 소켓 위치 + 위 hoverHeight + 흔들림(sin) · 회전 = 월드 위 축 spin · 크기 = 보정된 크기 × scaleMul 로 둔다.
// ■ 값 = Resources/WL/WLOrbFloat.json (C45) · enabled 0 = 아무것도 안 함 · 🔴 Debug.LogError 금지(원본 ErrorLogHook 팝업)
// ─────────────────────────────────────────────────────────────────────────────
using UnityEngine ;
namespace WL.Combat.Weapon
{
[System.Serializable]
public class WLOrbFloatJson
{
public int enabled = 1 ;
public string [ ] weaponNames = new [ ] { "Orb" } ; // 소켓 자식 이름 앞부분(프리팹 이름 · "(Clone)" 무시)
public float scaleMul = 5f ; // 보정된 크기 × 배수
public float hoverHeight = 0.25f ; // 소켓(손) 위 m
public float forward = 0f ; // 소켓 기준 PC 정면 쪽 m
public float bobAmp = 0.04f ; // 상하 흔들림 진폭 m
public float bobHz = 1.0f ; // 흔들림 주기(초당)
public float spinDegPerSec = 40f ; // 월드 위 축 회전
public float tiltDeg = 0f ; // 고리를 눕히는 각(0 = 프리팹 그대로)
2026-10-20 07:53:29 +00:00
public int centerChildren = 0 ; // #948-6 1 = 오브 프리팹 직접 자식(circle 고리 등)의 로컬 위치를 0 으로 — 감싸는 이펙트가 오브 중심을 돈다(원본 circle 은 +0.727 옆에 있어 어긋남)
2026-10-19 13:23:08 +00:00
public string note = "" ;
}
public static class WLOrbFloatSettings
{
public const string ResourcesPath = "WL/WLOrbFloat" ;
static WLOrbFloatJson s_cfg ; static bool s_loaded ;
public static WLOrbFloatJson Cfg { get { if ( ! s_loaded ) Load ( ) ; return s_cfg ; } }
public static void Reload ( ) { s_loaded = false ; }
static void Load ( )
{
s_loaded = true ;
try { var ta = Resources . Load < TextAsset > ( ResourcesPath ) ; s_cfg = ta ! = null ? JsonUtility . FromJson < WLOrbFloatJson > ( ta . text ) : null ; }
catch ( System . Exception ex ) { WLOrbFloat . LastLog = "설정 읽기 실패 — " + ex . Message ; s_cfg = null ; }
if ( s_cfg = = null ) s_cfg = new WLOrbFloatJson { enabled = 0 } ;
}
}
/// <summary>손 소켓 자식(오브)에 붙어 손 위에 띄운다. WLWeaponFitter 훅 1줄 → TryAttach.</summary>
public sealed class WLOrbFloat : MonoBehaviour
{
public static int Attached , Frames ; public static string LastLog = "" ;
Transform _socket , _pc ; Vector3 _baseScale ; float _t0 , _spin ;
public static bool TryAttach ( Transform weapon )
{
try
{
var c = WLOrbFloatSettings . Cfg ; if ( c = = null | | c . enabled = = 0 | | weapon = = null ) return false ;
string n = weapon . name ; bool hit = false ;
if ( c . weaponNames ! = null ) for ( int i = 0 ; i < c . weaponNames . Length ; i + + ) if ( ! string . IsNullOrEmpty ( c . weaponNames [ i ] ) & & n . StartsWith ( c . weaponNames [ i ] ) ) { hit = true ; break ; }
if ( ! hit ) return false ;
var f = weapon . GetComponent < WLOrbFloat > ( ) ; if ( f ! = null ) return true ;
f = weapon . gameObject . AddComponent < WLOrbFloat > ( ) ;
f . _socket = weapon . parent ; f . _pc = f . _socket ! = null ? f . _socket . root : null ; f . _baseScale = weapon . localScale ; f . _t0 = Time . time ;
weapon . localScale = f . _baseScale * Mathf . Max ( 0.01f , c . scaleMul ) ;
2026-10-20 07:53:29 +00:00
int centered = 0 ;
if ( c . centerChildren ! = 0 ) for ( int i = 0 ; i < weapon . childCount ; i + + ) { var ch = weapon . GetChild ( i ) ; if ( ch . localPosition . sqrMagnitude > 1e-6f ) { ch . localPosition = Vector3 . zero ; centered + + ; } } // #948-6 감싸는 고리를 오브 중심으로
Attached + + ; LastLog = "오브 부착 " + n + " 소켓=" + ( f . _socket ! = null ? f . _socket . name : "-" ) + " 크기 × " + c . scaleMul + " 높이 " + c . hoverHeight + ( centered > 0 ? " · 자식 중심 " + centered : "" ) ;
2026-10-19 13:23:08 +00:00
return true ;
}
catch ( System . Exception ex ) { LastLog = "오브 부착 실패 — " + ex . Message ; return false ; }
}
void LateUpdate ( )
{
var c = WLOrbFloatSettings . Cfg ; if ( c = = null | | c . enabled = = 0 | | _socket = = null ) return ;
float t = Time . time - _t0 ;
Vector3 fwd = _pc ! = null ? _pc . forward : Vector3 . forward ; fwd . y = 0f ; if ( fwd . sqrMagnitude < 1e-4f ) fwd = Vector3 . forward ; fwd . Normalize ( ) ;
Vector3 pos = _socket . position + Vector3 . up * ( c . hoverHeight + Mathf . Sin ( t * c . bobHz * Mathf . PI * 2f ) * c . bobAmp ) + fwd * c . forward ;
_spin + = c . spinDegPerSec * Time . deltaTime ;
var rot = Quaternion . AngleAxis ( _spin , Vector3 . up ) * Quaternion . Euler ( c . tiltDeg , 0f , 0f ) ;
transform . SetPositionAndRotation ( pos , rot ) ;
Frames + + ;
}
}
}