WL #948-3 마법검 PD 재지적 3건 — 3타 = Mage@RangeAttack1(12~44f · 서서 한손 앞으로 내뻗는 시전 · StaffCast3 · 상체 마스크 · Y 베이크 끔 · 속도 2 · 발사 0.5) · 지팡이 끝 빛 2차(흰 선·위치 오류 → 축 위 끝점 + 팩 이펙트 WL_Fx_BowCharge 루프 + 약한 점광원 · 홀더 씬 루트) · 명중 직전 길쭉한 이펙트 후보 2개 축소(탄 줄기 입자 숨김 · 별 섬광 0.37→0.28)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
깃 관리자 2026-10-20 14:05:52 +09:00
parent 6278723d61
commit e9bb3ace18
6 changed files with 138 additions and 77 deletions

View File

@ -1,7 +1,14 @@
fileFormatVersion: 2
guid: d664857a783ffb645b4f451b91f99021
AssetOrigin:
serializedVersion: 1
productId: 39519
packageName: Mage Warrior Mecanim Animation Pack
packageVersion: 2.4.1
assetPath: Assets/ExplosiveLLC/Mage Warrior Mecanim Animation Pack/Animations/Mage@RangeAttack1.FBX
uploadId: 754788
ModelImporter:
serializedVersion: 25
serializedVersion: 24300
internalIDToNameTable:
- first:
1: 100000
@ -470,18 +477,18 @@ ModelImporter:
second: ImportLogs
externalObjects: {}
materials:
importMaterials: 0
materialImportMode: 0
materialName: 0
materialSearch: 1
materialLocation: 0
searchTexturesGlobally: 1
animations:
legacyGenerateAnimations: 4
bakeSimulation: 0
resampleCurves: 1
optimizeGameObjects: 0
removeConstantScaleCurves: 0
motionNodeName: Motion
rigImportErrors:
rigImportWarnings:
animationImportErrors:
animationImportWarnings: "\nClip 'Take 001' has import animation warnings that
might lower retargeting quality:\nNote: Activate translation DOF on avatar
@ -643,6 +650,49 @@ ModelImporter:
maskType: 0
maskSource: {instanceID: 0}
additiveReferencePoseFrame: 0
- serializedVersion: 16
name: StaffCast3_S_10302
takeName: Take 001
internalID: 1827226128182048838
firstFrame: 12
lastFrame: 44
wrapMode: 0
orientationOffsetY: 0
level: 0
cycleOffset: 0
loop: 0
hasAdditiveReferencePose: 0
loopTime: 0
loopBlend: 0
loopBlendOrientation: 1
loopBlendPositionY: 0
loopBlendPositionXZ: 0
keepOriginalOrientation: 1
keepOriginalPositionY: 1
keepOriginalPositionXZ: 1
heightFromFeet: 0
mirror: 0
bodyMask: 00000000010000000100000000000000000000000100000001000000010000000100000000000000000000000100000001000000
curves: []
events:
- time: 0
functionName: LookAtTarget
data:
objectReferenceParameter: {instanceID: 0}
floatParameter: 0
intParameter: 0
messageOptions: 0
- time: 0.5
functionName: Projectile
data: Projectile_10302
objectReferenceParameter: {instanceID: 0}
floatParameter: 0
intParameter: 0
messageOptions: 0
transformMask: []
maskType: 1
maskSource: {fileID: 31900000, guid: 8568a603ff2c2854190b73771710444a, type: 2}
additiveReferencePoseFrame: 0
isReadable: 1
meshes:
lODScreenPercentages: []
@ -650,26 +700,39 @@ ModelImporter:
meshCompression: 0
addColliders: 0
useSRGBMaterialColor: 1
sortHierarchyByName: 1
importPhysicalCameras: 1
importVisibility: 0
importBlendShapes: 0
importCameras: 0
importLights: 0
nodeNameCollisionStrategy: 0
fileIdsGeneration: 1
swapUVChannels: 0
generateSecondaryUV: 0
useFileUnits: 1
keepQuads: 0
weldVertices: 0
bakeAxisConversion: 0
preserveHierarchy: 0
skinWeightsMode: 0
maxBonesPerVertex: 4
minBoneWeight: 0.001
optimizeBones: 1
generateMeshLods: 0
meshLodGenerationFlags: 0
maximumMeshLod: -1
meshOptimizationFlags: 0
indexFormat: 0
secondaryUVAngleDistortion: 8
secondaryUVAreaDistortion: 15.000001
secondaryUVHardAngle: 88
secondaryUVMarginMethod: 0
secondaryUVMinLightmapResolution: 40
secondaryUVMinObjectScale: 1
secondaryUVPackMargin: 4
useFileScale: 0
strictVertexDataChecks: 0
tangentSpace:
normalSmoothAngle: 60
normalImportMode: 2
@ -680,7 +743,6 @@ ModelImporter:
normalSmoothingSource: 0
referencedClips: []
importAnimation: 1
copyAvatar: 1
humanDescription:
serializedVersion: 3
human:
@ -1389,16 +1451,14 @@ ModelImporter:
skeletonHasParents: 0
lastHumanDescriptionAvatarSource: {fileID: 9000000, guid: 7c72ed3f90575084c9f3b5b585887182,
type: 3}
autoGenerateAvatarMappingIfUnspecified: 1
animationType: 3
humanoidOversampling: 1
avatarSetup: 2
addHumanoidExtraRootOnlyWhenUsingAvatar: 0
importBlendShapeDeformPercent: 0
remapMaterialsIfMaterialImportModeIsNone: 1
additionalBone: 0
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 39519
packageName: Mage Warrior Mecanim Animation Pack
packageVersion: 2.4.1
assetPath: Assets/ExplosiveLLC/Mage Warrior Mecanim Animation Pack/Animations/Mage@RangeAttack1.FBX
uploadId: 754788

View File

@ -2316,7 +2316,7 @@ AnimatorState:
m_MirrorParameterActive: 0
m_CycleOffsetParameterActive: 0
m_TimeParameterActive: 0
m_Motion: {fileID: -4485281783395832467, guid: a9b647a73aef2fb4a83c157be5e4867c,
m_Motion: {fileID: 1827226128182048838, guid: d664857a783ffb645b4f451b91f99021,
type: 3}
m_Tag:
m_SpeedParameter:

View File

@ -984,13 +984,13 @@ MonoBehaviour:
intParameter: 0
floatParameter: 0
- stateName: attack3
sourceFbxPath: Assets/ResWork/Suriyun/Animations/AnimationsWing/Anim@CastspellC_wing.fbx
subClipName: StaffJumpCast3_S_{CID}
sourceClipName: CastspellC_wing
sourceFbxPath: Assets/ExplosiveLLC/Mage Warrior Mecanim Animation Pack/Animations/Mage@RangeAttack1.FBX
subClipName: StaffCast3_S_{CID}
sourceClipName:
incomingExitTime: 0.85
createIfMissing: 1
firstFrame: 431
lastFrame: 487
firstFrame: 12
lastFrame: 44
speed: 2
maskPath: Assets/WL/Combat/Masks/WL_UpperBody.mask
bakeRootY: 0
@ -999,13 +999,13 @@ MonoBehaviour:
time: 0
useNormalized: 1
normalizedTime: 0
stringParameter:
stringParameter:
intParameter: 0
floatParameter: 0
- functionName: Projectile
time: 0
useNormalized: 1
normalizedTime: 0.43
normalizedTime: 0.5
stringParameter: Projectile_{CID}
intParameter: 0
floatParameter: 0

View File

@ -17,13 +17,15 @@
"FX_Shoot_06_projectile/trail",
"FX_Shoot_06_projectile/back_fire",
"FX_Shoot_06_projectile/particle",
"~WL_Visual/Pos/trail"
"~WL_Visual/Pos/trail",
"~WL_Visual/Pos/bullet/SubEmitter2"
],
"_note_948b": "#948 PD 「명중 직전 길쭉한 타격 이펙트」 — 후보 2개 모두 줄임: 탄 끝에서 나오는 길쭉한 줄기 입자(bullet/SubEmitter2 · 경계 1.66×0.31 m) 숨김 + 명중 별 섬광(WL_Hit_Staff · 세로 스파이크) 0.37 → 0.28. 명중 자체는 판정 반경(공격표 aoe 1.2 m) 때문에 접촉 전에 뜬다(판정 값은 PD 결정)",
"stageScales": [
{ "stage": 3, "scale": 0.0, "hitScale": 0.0 }
],
"hitFx": "WL/Impact/WL_Hit_Staff",
"hitScale": 0.37,
"hitScale": 0.28,
"hitLife": 1.5
},
{

View File

@ -1,20 +1,19 @@
{
"enabled": 1,
"note": "#948 PD 「마법검의 지팡이 끝은 은은하게 빛나도록(마법 무기 느낌)」 — 직업 10302(마법검 1071) 무기 끝에 가산 사각형(WL_Mat_ChargeHalo 사본 · 주황빛) + 점광원 1개 · 사인 맥동. 끄기 = enabled 0 · 점광원만 끄기 = light 0",
"note": "#948 PD 「마법검의 지팡이 끝은 은은하게 빛나도록(마법 무기 느낌)」 — 직업 10302(마법검 1071). 2차: 끝 = 무기 축 위 끝점(둥근 면 중심) · 겉모습 = 팩 이펙트 WL_Fx_BowCharge(시전 모으기와 같은 것)를 루프로 작게 + 약한 점광원 · 끄기 = enabled 0 · 점광원만 끄기 = light 0",
"rules": [
{
"classIds": [ 10302 ],
"weaponNames": [],
"color": [ 1.0, 0.78, 0.42, 0.7 ],
"haloScale": 0.16,
"pulse": 0.25,
"prefab": "WL/Fx/WL_Fx_BowCharge",
"scale": 0.06,
"pulse": 0.2,
"pulseSeconds": 1.6,
"light": 1,
"lightColor": [ 1.0, 0.78, 0.42, 1.0 ],
"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"
"tipPull": 0.0
}
]
}

View File

@ -1,11 +1,12 @@
// ─────────────────────────────────────────────────────────────────────
// WLWeaponTipGlow.cs — 무기 끝 은은한 빛 (#948 · 2026-10-20)
// WLWeaponTipGlow.cs — 무기 끝 은은한 빛 (#948 · 2026-10-20 · 2차: 쿼드 → 팩 이펙트 · 끝 = 축 위 끝점)
//
// 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)
// 「지팡이 끝에 알 수 없는 흰색 선이 생겨났고 광원의 위치가 지팡이의 둥근 면이 아니라 엉뚱한 위치」
// ■ 1차 실패: 무기 자식 쿼드가 무기의 비균등 스케일에 눌려 흰 선이 됐고, 끝 = 경계 상자 모서리라 축에서 벗어났다.
// ■ 2차: 끝 = 소켓 → 렌더러 경계 중심 방향(= 무기 축) 위에서 가장 먼 모서리의 투영점(둥근 끝면 중심) · 빛 = 씬 루트의 홀더가 매 프레임 그 점을 따라감 ·
// 겉모습 = 기존 팩 이펙트 프리팹(기본 WL_Fx_BowCharge · 시전 모으기와 같은 것)을 루프로 켜 둔다 + 약한 점광원 · 사인 맥동(홀더 스케일)
// ■ 붙는 자리 = WLWeaponFitter 손 맞춤 뒤 훅 1줄(WLOrbFloat 과 같은 자리) · 값 = Resources/WL/WLWeaponTipGlow.json (C45) · enabled 0 = 아무것도 안 함
// 🔴 Debug.LogError 금지(원본 ErrorLogHook 팝업) — 예외는 LastLog 로만
// ─────────────────────────────────────────────────────────────────────
using UnityEngine;
@ -17,15 +18,15 @@ namespace WL.Combat.Weapon
{
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 string prefab = "WL/Fx/WL_Fx_BowCharge"; // Resources 경로 · 파티클을 전부 루프로
public float scale = 0.06f;
public float pulse = 0.2f; // 맥동 진폭(크기 ±비율)
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 경로
public float[] lightColor = { 1f, 0.8f, 0.45f, 1f };
public float lightRange = 0.8f;
public float lightIntensity = 0.3f;
public float tipPull = 0f; // 끝에서 손 쪽으로 당길 거리(m · 둥근 면 안쪽에 두고 싶을 때)
}
[System.Serializable]
@ -54,7 +55,7 @@ namespace WL.Combat.Weapon
[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;
Transform _weapon, _socket, _holder; Light _light; WLWeaponTipGlowRule _r; float _t0; Vector3 _localTip;
public static bool TryAttach(Transform weapon)
{
@ -76,67 +77,66 @@ namespace WL.Combat.Weapon
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");
Attached++; LastLog = "무기 끝 빛 " + weapon.name + " 직업 " + cls + " 끝(로컬) " + g._localTip.ToString("F2") + (g._holder != null ? " · " + rule.prefab : " · 프리팹 없음");
return true;
}
catch (System.Exception ex) { LastLog = "무기 끝 빛 실패 — " + ex.Message; return false; }
}
/// <summary>무기 끝 = 소켓에서 렌더러 경계 중심을 향한 축 위, 가장 멀리 투영되는 모서리의 투영점(둥근 끝면 중심 · 상자 모서리가 아님).</summary>
void Build()
{
var r = _r;
// 무기 끝 = 소켓에서 가장 먼 렌더러 경계 모서리
Vector3 tip = _weapon.position; float best = -1f;
bool any = false; Bounds b = new Bounds(_weapon.position, Vector3.zero);
foreach (var rd in _weapon.GetComponentsInChildren<Renderer>(false))
{
if (rd is ParticleSystemRenderer || !rd.enabled) continue;
var b = rd.bounds;
if (!any) { b = rd.bounds; any = true; } else b.Encapsulate(rd.bounds);
}
Vector3 tip;
if (!any) tip = _weapon.position;
else
{
Vector3 o = _socket.position; Vector3 axis = b.center - o; if (axis.sqrMagnitude < 1e-6f) axis = _weapon.up; axis.Normalize();
float far = 0f;
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; }
float d = Vector3.Dot(corner - o, axis); if (d > far) far = d;
}
tip = o + axis * Mathf.Max(0f, far - r.tipPull);
}
_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);
_localTip = _weapon.InverseTransformPoint(tip);
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;
var holder = new GameObject("~WL_TipGlow"); _holder = holder.transform; // 씬 루트(무기 스케일 영향 0)
_holder.position = tip;
var p = Resources.Load<GameObject>(r.prefab);
if (p != null)
{
var fx = Instantiate(p, _holder); fx.transform.localPosition = Vector3.zero; fx.transform.localRotation = Quaternion.identity; fx.transform.localScale = Vector3.one * r.scale;
foreach (var ps in fx.GetComponentsInChildren<ParticleSystem>(true)) { var m = ps.main; m.loop = true; m.playOnAwake = true; ps.Play(true); }
var fade = fx.GetComponent<MonoBehaviour>(); // 사본 프리팹에 수명 스크립트(TurnOff_GO · WLFxFade)가 있으면 끈다 — 계속 켜 둬야 한다
foreach (var mb in fx.GetComponents<MonoBehaviour>()) if (mb != null && !(mb is ParticleSystem)) mb.enabled = false;
}
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;
var lg = new GameObject("~WL_TipLight"); lg.transform.SetParent(_holder, false);
_light = lg.AddComponent<Light>(); _light.type = LightType.Point; _light.range = r.lightRange; _light.intensity = r.lightIntensity; _light.shadows = LightShadows.None;
_light.color = r.lightColor != null && r.lightColor.Length >= 3 ? new Color(r.lightColor[0], r.lightColor[1], r.lightColor[2]) : new Color(1f, 0.8f, 0.45f);
}
}
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;
if (_holder == 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)));
_holder.position = _weapon.TransformPoint(_localTip);
_holder.localScale = Vector3.one * k;
if (_light != null) _light.intensity = _r.lightIntensity * k;
}
void OnDestroy() { if (_mat != null) Destroy(_mat); }
void OnDisable() { if (_holder != null) _holder.gameObject.SetActive(false); }
void OnEnable() { if (_holder != null) _holder.gameObject.SetActive(true); }
void OnDestroy() { if (_holder != null) Destroy(_holder.gameObject); }
}
}