WL #948-2 마법검 PD 재지적·추가 6건 — 꼬리 = WL 덧씌움 trail(A/B 캡처 확정 · hideNames + 붙인 뒤 숨김 패스) · 3타 점프 = 매핑 bakeRootY 0(임포터 Y 베이크 끔 · 마스크만으론 불충분) + speed 2 · 3타 투사체 숨김(stageScales ×0) · 바위 popup 0.3→0.15 · 갈라짐 crack 검정→브라운 · 띄움·밀림 = StageHitReaction 3행(바위 0.2 s 뒤 · 1.0 m · 1.5 m · 기절 0.5) 일원화 · 1·2타 경직 뒤 밀림(delay 0.3) · 지팡이 끝 은은한 빛 WLWeaponTipGlow(신설 · 쿼드+점광원 · 직업 10302)
실측: 엉덩이 상승 0.01 m(점프 0) · 반응 로그 1·2·3단계 · 꼬리 renderer 제거 + 캡처 · 더미 거리 3.06→6.70→자동 R 2.49 · 콘솔 = sorceress 컨트롤러 Broken text PPtr 24(원장 7 기지) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
b19aca15bd
commit
6278723d61
|
|
@ -173,7 +173,13 @@ public static class WL_CombatAnimSetup
|
|||
// 상체 마스크(루트·다리 제외) = 지팡이 3타를 점프 없이 제자리에서 상체만(PD 「하체는 서 있는 상태에서 상체만」).
|
||||
private static void ApplyMask(UnityEditor.ModelImporterClipAnimation c, WL.Combat.StateClipSpec st, StringBuilder sb)
|
||||
{
|
||||
if (c == null || st == null || string.IsNullOrEmpty(st.maskPath)) return;
|
||||
if (c == null || st == null) return;
|
||||
if (st.bakeRootY >= 0) // #948 Root Y Bake Into Pose 덮기(0 = 점프 제거)
|
||||
{
|
||||
c.lockRootHeightY = st.bakeRootY != 0;
|
||||
sb.AppendLine(" 루트 Y Bake Into Pose = " + (st.bakeRootY != 0 ? "켬" : "끔") + " → " + c.name);
|
||||
}
|
||||
if (string.IsNullOrEmpty(st.maskPath)) return;
|
||||
var mask = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.AvatarMask>(st.maskPath);
|
||||
if (mask == null) { sb.AppendLine(" [WARN] 마스크 에셋 없음: " + st.maskPath); return; }
|
||||
c.maskType = UnityEditor.ClipAnimationMaskType.CopyFromOther;
|
||||
|
|
|
|||
|
|
@ -1815,7 +1815,7 @@ ModelImporter:
|
|||
loopTime: 0
|
||||
loopBlend: 0
|
||||
loopBlendOrientation: 1
|
||||
loopBlendPositionY: 1
|
||||
loopBlendPositionY: 0
|
||||
loopBlendPositionXZ: 1
|
||||
keepOriginalOrientation: 1
|
||||
keepOriginalPositionY: 1
|
||||
|
|
|
|||
|
|
@ -2303,7 +2303,7 @@ AnimatorState:
|
|||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: attack3
|
||||
m_Speed: 1
|
||||
m_Speed: 2
|
||||
m_CycleOffset: 0
|
||||
m_Transitions:
|
||||
- {fileID: 7396964718502629461}
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@ namespace WL.Character
|
|||
_applied[id] = k;
|
||||
if (k != 1f) LastApplied++;
|
||||
WL.Combat.Weapon.WLOrbFloat.TryAttach(w); // #944 오브(맹약) = 손 위 부유 + ×5 (Resources/WL/WLOrbFloat.json · 이름이 안 맞으면 아무것도 안 함)
|
||||
WL.Combat.Weapon.WLWeaponTipGlow.TryAttach(w); // #948 마법검(지팡이 10302) 끝 은은한 빛 (Resources/WL/WLWeaponTipGlow.json · 직업·이름이 안 맞으면 아무것도 안 함)
|
||||
}
|
||||
// ── 팔레트(WL-816b) — 메시(UV)·머티리얼만 바꾼다. 정점 좌표가 같아 위 배수 계산과 무관하다.
|
||||
if (pal && (force || !_paletted.Contains(id)))
|
||||
|
|
|
|||
|
|
@ -991,8 +991,9 @@ MonoBehaviour:
|
|||
createIfMissing: 1
|
||||
firstFrame: 431
|
||||
lastFrame: 487
|
||||
speed: 0
|
||||
speed: 2
|
||||
maskPath: Assets/WL/Combat/Masks/WL_UpperBody.mask
|
||||
bakeRootY: 0
|
||||
events:
|
||||
- functionName: LookAtTarget
|
||||
time: 0
|
||||
|
|
|
|||
|
|
@ -90,6 +90,9 @@ namespace WL.Combat
|
|||
[Tooltip("#948 서브클립에 씌울 아바타 마스크 에셋 경로(Assets/… .mask). 비우면 원본 클립 설정 그대로. 상체 마스크(루트·다리 제외) = 점프·이동 없이 제자리에서 상체만 움직인다(지팡이 3타)")]
|
||||
public string maskPath = "";
|
||||
|
||||
[Tooltip("#948 임포터 「Root Transform Position (Y) → Bake Into Pose」: -1 = 원본 클립 설정 그대로 · 0 = 끔(위아래 움직임이 포즈에서 빠져 루트모션으로 → PC 는 Y 루트모션을 버리므로 점프가 사라진다) · 1 = 켬")]
|
||||
public int bakeRootY = -1;
|
||||
|
||||
public List<AnimEventSpec> events = new List<AnimEventSpec>();
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -12,14 +12,15 @@
|
|||
"projectile": "Projectile_10302",
|
||||
"visual": "WL/WL_FireBoltVisual",
|
||||
"scale": 1.0,
|
||||
"_note_948": "#948 PD 마법검(1071): ① 「거리가 멀어질수록 꼬리처럼 길게 늘어지는 이펙트」 = 원본 FX_Shoot_06_projectile/particle(늘림 빌보드 · 비행 중 뒤로 남는 입자 · 실측 경계 2.5~3.2 m) → hideNames 추가 ② 「3타 공격의 크기를 절반」 = stageScales 3단계 겉모습·명중 이펙트 × 0.5(판정 그대로)",
|
||||
"_note_948": "#948 PD 마법검(1071): ① 「거리가 멀어질수록 꼬리처럼 길게 늘어지는 이펙트」 = A/B 캡처로 확정한 WL 덧씌움 ~WL_Visual/Pos/trail(fire_flash_trail · 비행 중 뒤로 남는 불꽃 띠) + 원본 FX_Shoot_06_projectile/particle(늘림 빌보드) → hideNames(덧씌움 자식은 붙인 뒤 한 번 더 숨김) ② 3타 = 투사체 없이 지면 바위(WLStaffEarth popup)가 공격 → stageScales 3단계 겉모습·명중 이펙트 × 0(보이지 않는 판정만 날아감)",
|
||||
"hideNames": [
|
||||
"FX_Shoot_06_projectile/trail",
|
||||
"FX_Shoot_06_projectile/back_fire",
|
||||
"FX_Shoot_06_projectile/particle"
|
||||
"FX_Shoot_06_projectile/particle",
|
||||
"~WL_Visual/Pos/trail"
|
||||
],
|
||||
"stageScales": [
|
||||
{ "stage": 3, "scale": 0.5, "hitScale": 0.5 }
|
||||
{ "stage": 3, "scale": 0.0, "hitScale": 0.0 }
|
||||
],
|
||||
"hitFx": "WL/Impact/WL_Hit_Staff",
|
||||
"hitScale": 0.37,
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@
|
|||
"stateName": "attack3",
|
||||
"fx": {
|
||||
"prefab": "WL/Fx/WL_Fx_StaffPopup",
|
||||
"scale": 0.3,
|
||||
"scale": 0.15,
|
||||
"bodyScale": 0,
|
||||
"offset": [
|
||||
0,
|
||||
|
|
@ -126,8 +126,9 @@
|
|||
"follow": 0,
|
||||
"socket": ""
|
||||
},
|
||||
"launchHeight": 1.5,
|
||||
"launchHeight": 0.0,
|
||||
"launchSeconds": 0.6,
|
||||
"stunSeconds": 0.5
|
||||
"stunSeconds": 0.0,
|
||||
"_note_948": "#948 PD 「3타 바위 크기 절반」 fx.scale 0.3 → 0.15 · 띄움·기절은 WLStageHitReaction 10302 3행(바위 등장 0.2 s 뒤 Fling)으로 옮겨 0 · 갈라짐(crack) 검정 → 브라운은 프리팹 입자 시작색"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"enabled": 1,
|
||||
"_note_948": "#948 PD 마법검(1071 · 직업 10302): 1·2타 투사체 명중 = 0.5 m 밀림 + 짧은 경직 · 3타 = 이펙트가 먼저 보이고(delaySeconds) 공중으로 튀어오르며 공격자 반대쪽으로 밀림(Fling · launchHeight)",
|
||||
"_note_948": "#948 PD 마법검(1071 · 직업 10302): 1·2타 명중 = 경직 0.3 s 동안은 움직이지 않고(PD 「경직 상태에서 풀린 후 이동」) 풀린 뒤 0.5 m 밀림(delaySeconds = staggerSeconds) · 3타 = 지면 바위(WLStaffEarth popup fx)가 먼저 보이고 0.2 s 뒤 공중으로 1.0 m 튀어오르며 공격자 반대쪽 1.5 m 밀림 + 기절 0.5 s(바위 쪽 launch 는 0 으로 — 경로 하나)",
|
||||
"rules": [
|
||||
{
|
||||
"classIds": [
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
"launchHeight": 0.0,
|
||||
"staggerSeconds": 0.3,
|
||||
"knockbackDistance": 0.5,
|
||||
"delaySeconds": 0.0
|
||||
"delaySeconds": 0.3
|
||||
},
|
||||
{
|
||||
"classIds": [
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
"launchHeight": 0.0,
|
||||
"staggerSeconds": 0.3,
|
||||
"knockbackDistance": 0.5,
|
||||
"delaySeconds": 0.0
|
||||
"delaySeconds": 0.3
|
||||
},
|
||||
{
|
||||
"classIds": [
|
||||
|
|
@ -35,8 +35,8 @@
|
|||
"stage": 3,
|
||||
"knockbackSeconds": 0.4,
|
||||
"knockbackSpeed": 0.0,
|
||||
"stunSeconds": 0.3,
|
||||
"launchHeight": 0.7,
|
||||
"stunSeconds": 0.5,
|
||||
"launchHeight": 1.0,
|
||||
"staggerSeconds": 0.0,
|
||||
"knockbackDistance": 1.5,
|
||||
"delaySeconds": 0.2
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"enabled": 1,
|
||||
"note": "#948 PD 「마법검의 지팡이 끝은 은은하게 빛나도록(마법 무기 느낌)」 — 직업 10302(마법검 1071) 무기 끝에 가산 사각형(WL_Mat_ChargeHalo 사본 · 주황빛) + 점광원 1개 · 사인 맥동. 끄기 = enabled 0 · 점광원만 끄기 = light 0",
|
||||
"rules": [
|
||||
{
|
||||
"classIds": [ 10302 ],
|
||||
"weaponNames": [],
|
||||
"color": [ 1.0, 0.78, 0.42, 0.7 ],
|
||||
"haloScale": 0.16,
|
||||
"pulse": 0.25,
|
||||
"pulseSeconds": 1.6,
|
||||
"light": 1,
|
||||
"lightRange": 0.8,
|
||||
"lightIntensity": 0.3,
|
||||
"_note": "점광원 1.4 m/1.0 은 발밑에 큰 연두색 번짐이 생겨(캡처 948_tip) 0.8 m/0.3 으로 — 「은은하게」",
|
||||
"tipUp": 0.0,
|
||||
"material": "WL/Fx/WL_Mat_ChargeHalo"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 37ce4f5a94ea7474485f4de20abfccdd
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -159,6 +159,8 @@ namespace WL.Combat.Weapon
|
|||
arr[i].Play(true);
|
||||
}
|
||||
pss = arr.Length; normal = go;
|
||||
// #948 — 덧씌움 자식(~WL_Visual/…)도 hideNames 로 끌 수 있게 붙인 뒤 한 번 더(위 첫 패스는 덧씌움이 생기기 전이라 원본 자식만 잡는다)
|
||||
if (rule.hideNames != null) foreach (var hn in rule.hideNames) { var ht = string.IsNullOrEmpty(hn) ? null : pb.transform.Find(hn); if (ht != null) foreach (var hr in ht.GetComponentsInChildren<Renderer>(true)) { if (hr.enabled) { hr.enabled = false; hid++; } } }
|
||||
}
|
||||
// #839 — 강화 화살: 글로우 자식(꺼둠) + WLChargedMark(켜질 때 창 열려 있으면 적용)
|
||||
if (!string.IsNullOrEmpty(rule.chargedVisual))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,142 @@
|
|||
// ─────────────────────────────────────────────────────────────────────
|
||||
// WLWeaponTipGlow.cs — 무기 끝 은은한 빛 (#948 · 2026-10-20)
|
||||
//
|
||||
// PD 원문: 「마법검의 지팡이 끝은 은은하게 빛나도록 해줘. (마법 무기라는 느낌이 들도록)」
|
||||
// ■ 붙는 자리 = WLWeaponFitter 손 맞춤 뒤 훅 1줄(WLOrbFloat 과 같은 자리 · 소켓 아래 무기 인스턴스마다 1회)
|
||||
// ■ 무기 끝 = 소켓에서 가장 먼 렌더러 경계 모서리(WLStaffEarth.Tip 과 같은 생각 · 세로가 아닌 어떤 방향이든) → 무기 로컬 좌표로 기억해 따라간다
|
||||
// ■ 빛 = 카메라를 보는 가산 사각형(기존 WL_Mat_ChargeHalo 사본 · 색·크기 JSON) + 선택 점광원(range · intensity JSON · 모바일 예산 고려해 1개 · 끌 수 있음) · 사인 맥동
|
||||
// ■ 값 = Resources/WL/WLWeaponTipGlow.json (C45) · enabled 0 = 아무것도 안 함 · 원본 수정 0줄(훅은 WL 파일 WLWeaponFitter)
|
||||
// 🔴 Debug.LogError 금지(원본 ErrorLogHook 팝업) — 예외는 LastLog 로만
|
||||
// ─────────────────────────────────────────────────────────────────────
|
||||
using UnityEngine;
|
||||
|
||||
namespace WL.Combat.Weapon
|
||||
{
|
||||
[System.Serializable]
|
||||
public class WLWeaponTipGlowRule
|
||||
{
|
||||
public int[] classIds = new int[0]; // 이 직업의 무기에 붙인다(PCActor.Get_ID)
|
||||
public string[] weaponNames = new string[0]; // 또는 무기 인스턴스 이름 앞부분
|
||||
public float[] color = { 1f, 0.8f, 0.45f, 1f };
|
||||
public float haloScale = 0.18f; // 사각형 한 변(m)
|
||||
public float pulse = 0.25f; // 맥동 진폭(크기·밝기 ±비율)
|
||||
public float pulseSeconds = 1.6f; // 맥동 주기(초)
|
||||
public int light = 1; // 1 = 점광원도
|
||||
public float lightRange = 1.4f;
|
||||
public float lightIntensity = 1.0f;
|
||||
public float tipUp = 0f; // 끝에서 더 올릴 높이(m)
|
||||
public string material = "WL/Fx/WL_Mat_ChargeHalo"; // Resources 경로
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class WLWeaponTipGlowJson { public int enabled = 1; public WLWeaponTipGlowRule[] rules = new WLWeaponTipGlowRule[0]; public string note = ""; }
|
||||
|
||||
public sealed class WLWeaponTipGlow : MonoBehaviour
|
||||
{
|
||||
public const string ResourcesPath = "WL/WLWeaponTipGlow";
|
||||
public static int Attached; public static string LastLog = "";
|
||||
static WLWeaponTipGlowJson s_cfg; static bool s_loaded;
|
||||
|
||||
static WLWeaponTipGlowJson Cfg
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!s_loaded)
|
||||
{
|
||||
s_loaded = true;
|
||||
var ta = Resources.Load<TextAsset>(ResourcesPath);
|
||||
try { s_cfg = ta != null ? JsonUtility.FromJson<WLWeaponTipGlowJson>(ta.text) : null; } catch (System.Exception ex) { LastLog = "설정 파싱 실패 — " + ex.Message; }
|
||||
}
|
||||
return s_cfg;
|
||||
}
|
||||
}
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
static void ResetStatics() { s_loaded = false; s_cfg = null; } // 도메인 리로드 끔 대비(JSON 재읽기)
|
||||
|
||||
Transform _weapon, _socket, _halo; Light _light; Material _mat; WLWeaponTipGlowRule _r; float _t0; Vector3 _localTip; Color _col;
|
||||
|
||||
public static bool TryAttach(Transform weapon)
|
||||
{
|
||||
try
|
||||
{
|
||||
var c = Cfg; if (c == null || c.enabled == 0 || weapon == null || c.rules == null) return false;
|
||||
if (weapon.GetComponent<WLWeaponTipGlow>() != null) return true;
|
||||
var pc = weapon.GetComponentInParent<PCActor>(); int cls = pc != null ? pc.Get_ID() : 0;
|
||||
WLWeaponTipGlowRule rule = null;
|
||||
foreach (var r in c.rules)
|
||||
{
|
||||
if (r == null) continue;
|
||||
bool byClass = r.classIds != null && System.Array.IndexOf(r.classIds, cls) >= 0;
|
||||
bool byName = false;
|
||||
if (r.weaponNames != null) foreach (var n in r.weaponNames) if (!string.IsNullOrEmpty(n) && weapon.name.StartsWith(n)) { byName = true; break; }
|
||||
if (byClass || byName) { rule = r; break; }
|
||||
}
|
||||
if (rule == null) return false;
|
||||
var g = weapon.gameObject.AddComponent<WLWeaponTipGlow>();
|
||||
g._weapon = weapon; g._socket = weapon.parent != null ? weapon.parent : weapon; g._r = rule; g._t0 = Time.time;
|
||||
g.Build();
|
||||
Attached++; LastLog = "무기 끝 빛 " + weapon.name + " 직업 " + cls + " 끝(로컬) " + g._localTip.ToString("F2");
|
||||
return true;
|
||||
}
|
||||
catch (System.Exception ex) { LastLog = "무기 끝 빛 실패 — " + ex.Message; return false; }
|
||||
}
|
||||
|
||||
void Build()
|
||||
{
|
||||
var r = _r;
|
||||
// 무기 끝 = 소켓에서 가장 먼 렌더러 경계 모서리
|
||||
Vector3 tip = _weapon.position; float best = -1f;
|
||||
foreach (var rd in _weapon.GetComponentsInChildren<Renderer>(false))
|
||||
{
|
||||
if (rd is ParticleSystemRenderer || !rd.enabled) continue;
|
||||
var b = rd.bounds;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
var corner = new Vector3((i & 1) == 0 ? b.min.x : b.max.x, (i & 2) == 0 ? b.min.y : b.max.y, (i & 4) == 0 ? b.min.z : b.max.z);
|
||||
float d = (corner - _socket.position).sqrMagnitude; if (d > best) { best = d; tip = corner; }
|
||||
}
|
||||
}
|
||||
_localTip = _weapon.InverseTransformPoint(tip + Vector3.up * r.tipUp);
|
||||
_col = r.color != null && r.color.Length >= 4 ? new Color(r.color[0], r.color[1], r.color[2], r.color[3]) : new Color(1f, 0.8f, 0.45f, 1f);
|
||||
|
||||
var q = GameObject.CreatePrimitive(PrimitiveType.Quad); q.name = "~WL_TipGlow";
|
||||
var col = q.GetComponent<Collider>(); if (col != null) Destroy(col);
|
||||
q.transform.SetParent(_weapon, false); q.transform.localScale = Vector3.one * r.haloScale;
|
||||
var src = Resources.Load<Material>(r.material);
|
||||
var mr = q.GetComponent<MeshRenderer>();
|
||||
if (src != null) { _mat = new Material(src); mr.sharedMaterial = _mat; SetColor(_col); }
|
||||
mr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; mr.receiveShadows = false;
|
||||
_halo = q.transform;
|
||||
if (r.light != 0)
|
||||
{
|
||||
var lg = new GameObject("~WL_TipLight"); lg.transform.SetParent(_halo, false);
|
||||
_light = lg.AddComponent<Light>(); _light.type = LightType.Point; _light.range = r.lightRange; _light.intensity = r.lightIntensity; _light.color = _col; _light.shadows = LightShadows.None;
|
||||
}
|
||||
}
|
||||
|
||||
void SetColor(Color c)
|
||||
{
|
||||
if (_mat == null) return;
|
||||
if (_mat.HasProperty("_TintColor")) _mat.SetColor("_TintColor", c);
|
||||
if (_mat.HasProperty("_BaseColor")) _mat.SetColor("_BaseColor", c);
|
||||
if (_mat.HasProperty("_Color")) _mat.SetColor("_Color", c);
|
||||
if (_mat.HasProperty("_EmissionColor")) _mat.SetColor("_EmissionColor", c);
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if (_halo == null || _weapon == null) return;
|
||||
float k = 1f + (_r.pulse > 0f && _r.pulseSeconds > 0f ? _r.pulse * Mathf.Sin((Time.time - _t0) / _r.pulseSeconds * Mathf.PI * 2f) : 0f);
|
||||
var pos = _weapon.TransformPoint(_localTip);
|
||||
var cam = Camera.main;
|
||||
_halo.position = pos;
|
||||
if (cam != null) _halo.rotation = Quaternion.LookRotation(pos - cam.transform.position); // 쿼드 앞면이 카메라를 보게
|
||||
_halo.localScale = Vector3.one * (_r.haloScale * k / Mathf.Max(0.0001f, _weapon.lossyScale.x)) ;
|
||||
if (_mat != null) SetColor(new Color(_col.r, _col.g, _col.b, _col.a * Mathf.Clamp01(0.6f + 0.4f * k)));
|
||||
if (_light != null) _light.intensity = _r.lightIntensity * k;
|
||||
}
|
||||
|
||||
void OnDestroy() { if (_mat != null) Destroy(_mat); }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c0252f713a410964aa6d2d6d256fbb28
|
||||
Loading…
Reference in New Issue