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:
parent
6278723d61
commit
e9bb3ace18
|
|
@ -1,7 +1,14 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: d664857a783ffb645b4f451b91f99021
|
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:
|
ModelImporter:
|
||||||
serializedVersion: 25
|
serializedVersion: 24300
|
||||||
internalIDToNameTable:
|
internalIDToNameTable:
|
||||||
- first:
|
- first:
|
||||||
1: 100000
|
1: 100000
|
||||||
|
|
@ -470,18 +477,18 @@ ModelImporter:
|
||||||
second: ImportLogs
|
second: ImportLogs
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
materials:
|
materials:
|
||||||
importMaterials: 0
|
materialImportMode: 0
|
||||||
materialName: 0
|
materialName: 0
|
||||||
materialSearch: 1
|
materialSearch: 1
|
||||||
materialLocation: 0
|
materialLocation: 0
|
||||||
|
searchTexturesGlobally: 1
|
||||||
animations:
|
animations:
|
||||||
legacyGenerateAnimations: 4
|
legacyGenerateAnimations: 4
|
||||||
bakeSimulation: 0
|
bakeSimulation: 0
|
||||||
resampleCurves: 1
|
resampleCurves: 1
|
||||||
optimizeGameObjects: 0
|
optimizeGameObjects: 0
|
||||||
|
removeConstantScaleCurves: 0
|
||||||
motionNodeName: Motion
|
motionNodeName: Motion
|
||||||
rigImportErrors:
|
|
||||||
rigImportWarnings:
|
|
||||||
animationImportErrors:
|
animationImportErrors:
|
||||||
animationImportWarnings: "\nClip 'Take 001' has import animation warnings that
|
animationImportWarnings: "\nClip 'Take 001' has import animation warnings that
|
||||||
might lower retargeting quality:\nNote: Activate translation DOF on avatar
|
might lower retargeting quality:\nNote: Activate translation DOF on avatar
|
||||||
|
|
@ -643,6 +650,49 @@ ModelImporter:
|
||||||
maskType: 0
|
maskType: 0
|
||||||
maskSource: {instanceID: 0}
|
maskSource: {instanceID: 0}
|
||||||
additiveReferencePoseFrame: 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
|
isReadable: 1
|
||||||
meshes:
|
meshes:
|
||||||
lODScreenPercentages: []
|
lODScreenPercentages: []
|
||||||
|
|
@ -650,26 +700,39 @@ ModelImporter:
|
||||||
meshCompression: 0
|
meshCompression: 0
|
||||||
addColliders: 0
|
addColliders: 0
|
||||||
useSRGBMaterialColor: 1
|
useSRGBMaterialColor: 1
|
||||||
|
sortHierarchyByName: 1
|
||||||
|
importPhysicalCameras: 1
|
||||||
importVisibility: 0
|
importVisibility: 0
|
||||||
importBlendShapes: 0
|
importBlendShapes: 0
|
||||||
importCameras: 0
|
importCameras: 0
|
||||||
importLights: 0
|
importLights: 0
|
||||||
|
nodeNameCollisionStrategy: 0
|
||||||
|
fileIdsGeneration: 1
|
||||||
swapUVChannels: 0
|
swapUVChannels: 0
|
||||||
generateSecondaryUV: 0
|
generateSecondaryUV: 0
|
||||||
useFileUnits: 1
|
useFileUnits: 1
|
||||||
keepQuads: 0
|
keepQuads: 0
|
||||||
weldVertices: 0
|
weldVertices: 0
|
||||||
|
bakeAxisConversion: 0
|
||||||
preserveHierarchy: 0
|
preserveHierarchy: 0
|
||||||
skinWeightsMode: 0
|
skinWeightsMode: 0
|
||||||
maxBonesPerVertex: 4
|
maxBonesPerVertex: 4
|
||||||
minBoneWeight: 0.001
|
minBoneWeight: 0.001
|
||||||
|
optimizeBones: 1
|
||||||
|
generateMeshLods: 0
|
||||||
|
meshLodGenerationFlags: 0
|
||||||
|
maximumMeshLod: -1
|
||||||
meshOptimizationFlags: 0
|
meshOptimizationFlags: 0
|
||||||
indexFormat: 0
|
indexFormat: 0
|
||||||
secondaryUVAngleDistortion: 8
|
secondaryUVAngleDistortion: 8
|
||||||
secondaryUVAreaDistortion: 15.000001
|
secondaryUVAreaDistortion: 15.000001
|
||||||
secondaryUVHardAngle: 88
|
secondaryUVHardAngle: 88
|
||||||
|
secondaryUVMarginMethod: 0
|
||||||
|
secondaryUVMinLightmapResolution: 40
|
||||||
|
secondaryUVMinObjectScale: 1
|
||||||
secondaryUVPackMargin: 4
|
secondaryUVPackMargin: 4
|
||||||
useFileScale: 0
|
useFileScale: 0
|
||||||
|
strictVertexDataChecks: 0
|
||||||
tangentSpace:
|
tangentSpace:
|
||||||
normalSmoothAngle: 60
|
normalSmoothAngle: 60
|
||||||
normalImportMode: 2
|
normalImportMode: 2
|
||||||
|
|
@ -680,7 +743,6 @@ ModelImporter:
|
||||||
normalSmoothingSource: 0
|
normalSmoothingSource: 0
|
||||||
referencedClips: []
|
referencedClips: []
|
||||||
importAnimation: 1
|
importAnimation: 1
|
||||||
copyAvatar: 1
|
|
||||||
humanDescription:
|
humanDescription:
|
||||||
serializedVersion: 3
|
serializedVersion: 3
|
||||||
human:
|
human:
|
||||||
|
|
@ -1389,16 +1451,14 @@ ModelImporter:
|
||||||
skeletonHasParents: 0
|
skeletonHasParents: 0
|
||||||
lastHumanDescriptionAvatarSource: {fileID: 9000000, guid: 7c72ed3f90575084c9f3b5b585887182,
|
lastHumanDescriptionAvatarSource: {fileID: 9000000, guid: 7c72ed3f90575084c9f3b5b585887182,
|
||||||
type: 3}
|
type: 3}
|
||||||
|
autoGenerateAvatarMappingIfUnspecified: 1
|
||||||
animationType: 3
|
animationType: 3
|
||||||
humanoidOversampling: 1
|
humanoidOversampling: 1
|
||||||
|
avatarSetup: 2
|
||||||
|
addHumanoidExtraRootOnlyWhenUsingAvatar: 0
|
||||||
|
importBlendShapeDeformPercent: 0
|
||||||
|
remapMaterialsIfMaterialImportModeIsNone: 1
|
||||||
additionalBone: 0
|
additionalBone: 0
|
||||||
userData:
|
userData:
|
||||||
assetBundleName:
|
assetBundleName:
|
||||||
assetBundleVariant:
|
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
|
|
||||||
|
|
|
||||||
|
|
@ -2316,7 +2316,7 @@ AnimatorState:
|
||||||
m_MirrorParameterActive: 0
|
m_MirrorParameterActive: 0
|
||||||
m_CycleOffsetParameterActive: 0
|
m_CycleOffsetParameterActive: 0
|
||||||
m_TimeParameterActive: 0
|
m_TimeParameterActive: 0
|
||||||
m_Motion: {fileID: -4485281783395832467, guid: a9b647a73aef2fb4a83c157be5e4867c,
|
m_Motion: {fileID: 1827226128182048838, guid: d664857a783ffb645b4f451b91f99021,
|
||||||
type: 3}
|
type: 3}
|
||||||
m_Tag:
|
m_Tag:
|
||||||
m_SpeedParameter:
|
m_SpeedParameter:
|
||||||
|
|
|
||||||
|
|
@ -984,13 +984,13 @@ MonoBehaviour:
|
||||||
intParameter: 0
|
intParameter: 0
|
||||||
floatParameter: 0
|
floatParameter: 0
|
||||||
- stateName: attack3
|
- stateName: attack3
|
||||||
sourceFbxPath: Assets/ResWork/Suriyun/Animations/AnimationsWing/Anim@CastspellC_wing.fbx
|
sourceFbxPath: Assets/ExplosiveLLC/Mage Warrior Mecanim Animation Pack/Animations/Mage@RangeAttack1.FBX
|
||||||
subClipName: StaffJumpCast3_S_{CID}
|
subClipName: StaffCast3_S_{CID}
|
||||||
sourceClipName: CastspellC_wing
|
sourceClipName:
|
||||||
incomingExitTime: 0.85
|
incomingExitTime: 0.85
|
||||||
createIfMissing: 1
|
createIfMissing: 1
|
||||||
firstFrame: 431
|
firstFrame: 12
|
||||||
lastFrame: 487
|
lastFrame: 44
|
||||||
speed: 2
|
speed: 2
|
||||||
maskPath: Assets/WL/Combat/Masks/WL_UpperBody.mask
|
maskPath: Assets/WL/Combat/Masks/WL_UpperBody.mask
|
||||||
bakeRootY: 0
|
bakeRootY: 0
|
||||||
|
|
@ -999,13 +999,13 @@ MonoBehaviour:
|
||||||
time: 0
|
time: 0
|
||||||
useNormalized: 1
|
useNormalized: 1
|
||||||
normalizedTime: 0
|
normalizedTime: 0
|
||||||
stringParameter:
|
stringParameter:
|
||||||
intParameter: 0
|
intParameter: 0
|
||||||
floatParameter: 0
|
floatParameter: 0
|
||||||
- functionName: Projectile
|
- functionName: Projectile
|
||||||
time: 0
|
time: 0
|
||||||
useNormalized: 1
|
useNormalized: 1
|
||||||
normalizedTime: 0.43
|
normalizedTime: 0.5
|
||||||
stringParameter: Projectile_{CID}
|
stringParameter: Projectile_{CID}
|
||||||
intParameter: 0
|
intParameter: 0
|
||||||
floatParameter: 0
|
floatParameter: 0
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,15 @@
|
||||||
"FX_Shoot_06_projectile/trail",
|
"FX_Shoot_06_projectile/trail",
|
||||||
"FX_Shoot_06_projectile/back_fire",
|
"FX_Shoot_06_projectile/back_fire",
|
||||||
"FX_Shoot_06_projectile/particle",
|
"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": [
|
"stageScales": [
|
||||||
{ "stage": 3, "scale": 0.0, "hitScale": 0.0 }
|
{ "stage": 3, "scale": 0.0, "hitScale": 0.0 }
|
||||||
],
|
],
|
||||||
"hitFx": "WL/Impact/WL_Hit_Staff",
|
"hitFx": "WL/Impact/WL_Hit_Staff",
|
||||||
"hitScale": 0.37,
|
"hitScale": 0.28,
|
||||||
"hitLife": 1.5
|
"hitLife": 1.5
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,19 @@
|
||||||
{
|
{
|
||||||
"enabled": 1,
|
"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": [
|
"rules": [
|
||||||
{
|
{
|
||||||
"classIds": [ 10302 ],
|
"classIds": [ 10302 ],
|
||||||
"weaponNames": [],
|
"weaponNames": [],
|
||||||
"color": [ 1.0, 0.78, 0.42, 0.7 ],
|
"prefab": "WL/Fx/WL_Fx_BowCharge",
|
||||||
"haloScale": 0.16,
|
"scale": 0.06,
|
||||||
"pulse": 0.25,
|
"pulse": 0.2,
|
||||||
"pulseSeconds": 1.6,
|
"pulseSeconds": 1.6,
|
||||||
"light": 1,
|
"light": 1,
|
||||||
|
"lightColor": [ 1.0, 0.78, 0.42, 1.0 ],
|
||||||
"lightRange": 0.8,
|
"lightRange": 0.8,
|
||||||
"lightIntensity": 0.3,
|
"lightIntensity": 0.3,
|
||||||
"_note": "점광원 1.4 m/1.0 은 발밑에 큰 연두색 번짐이 생겨(캡처 948_tip) 0.8 m/0.3 으로 — 「은은하게」",
|
"tipPull": 0.0
|
||||||
"tipUp": 0.0,
|
|
||||||
"material": "WL/Fx/WL_Mat_ChargeHalo"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
// ─────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────
|
||||||
// WLWeaponTipGlow.cs — 무기 끝 은은한 빛 (#948 · 2026-10-20)
|
// WLWeaponTipGlow.cs — 무기 끝 은은한 빛 (#948 · 2026-10-20 · 2차: 쿼드 → 팩 이펙트 · 끝 = 축 위 끝점)
|
||||||
//
|
//
|
||||||
// PD 원문: 「마법검의 지팡이 끝은 은은하게 빛나도록 해줘. (마법 무기라는 느낌이 들도록)」
|
// PD 원문: 「마법검의 지팡이 끝은 은은하게 빛나도록 해줘. (마법 무기라는 느낌이 들도록)」
|
||||||
// ■ 붙는 자리 = WLWeaponFitter 손 맞춤 뒤 훅 1줄(WLOrbFloat 과 같은 자리 · 소켓 아래 무기 인스턴스마다 1회)
|
// 「지팡이 끝에 알 수 없는 흰색 선이 생겨났고 광원의 위치가 지팡이의 둥근 면이 아니라 엉뚱한 위치」
|
||||||
// ■ 무기 끝 = 소켓에서 가장 먼 렌더러 경계 모서리(WLStaffEarth.Tip 과 같은 생각 · 세로가 아닌 어떤 방향이든) → 무기 로컬 좌표로 기억해 따라간다
|
// ■ 1차 실패: 무기 자식 쿼드가 무기의 비균등 스케일에 눌려 흰 선이 됐고, 끝 = 경계 상자 모서리라 축에서 벗어났다.
|
||||||
// ■ 빛 = 카메라를 보는 가산 사각형(기존 WL_Mat_ChargeHalo 사본 · 색·크기 JSON) + 선택 점광원(range · intensity JSON · 모바일 예산 고려해 1개 · 끌 수 있음) · 사인 맥동
|
// ■ 2차: 끝 = 소켓 → 렌더러 경계 중심 방향(= 무기 축) 위에서 가장 먼 모서리의 투영점(둥근 끝면 중심) · 빛 = 씬 루트의 홀더가 매 프레임 그 점을 따라감 ·
|
||||||
// ■ 값 = Resources/WL/WLWeaponTipGlow.json (C45) · enabled 0 = 아무것도 안 함 · 원본 수정 0줄(훅은 WL 파일 WLWeaponFitter)
|
// 겉모습 = 기존 팩 이펙트 프리팹(기본 WL_Fx_BowCharge · 시전 모으기와 같은 것)을 루프로 켜 둔다 + 약한 점광원 · 사인 맥동(홀더 스케일)
|
||||||
|
// ■ 붙는 자리 = WLWeaponFitter 손 맞춤 뒤 훅 1줄(WLOrbFloat 과 같은 자리) · 값 = Resources/WL/WLWeaponTipGlow.json (C45) · enabled 0 = 아무것도 안 함
|
||||||
// 🔴 Debug.LogError 금지(원본 ErrorLogHook 팝업) — 예외는 LastLog 로만
|
// 🔴 Debug.LogError 금지(원본 ErrorLogHook 팝업) — 예외는 LastLog 로만
|
||||||
// ─────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
@ -17,15 +18,15 @@ namespace WL.Combat.Weapon
|
||||||
{
|
{
|
||||||
public int[] classIds = new int[0]; // 이 직업의 무기에 붙인다(PCActor.Get_ID)
|
public int[] classIds = new int[0]; // 이 직업의 무기에 붙인다(PCActor.Get_ID)
|
||||||
public string[] weaponNames = new string[0]; // 또는 무기 인스턴스 이름 앞부분
|
public string[] weaponNames = new string[0]; // 또는 무기 인스턴스 이름 앞부분
|
||||||
public float[] color = { 1f, 0.8f, 0.45f, 1f };
|
public string prefab = "WL/Fx/WL_Fx_BowCharge"; // Resources 경로 · 파티클을 전부 루프로
|
||||||
public float haloScale = 0.18f; // 사각형 한 변(m)
|
public float scale = 0.06f;
|
||||||
public float pulse = 0.25f; // 맥동 진폭(크기·밝기 ±비율)
|
public float pulse = 0.2f; // 맥동 진폭(크기 ±비율)
|
||||||
public float pulseSeconds = 1.6f; // 맥동 주기(초)
|
public float pulseSeconds = 1.6f;
|
||||||
public int light = 1; // 1 = 점광원도
|
public int light = 1; // 1 = 점광원도
|
||||||
public float lightRange = 1.4f;
|
public float[] lightColor = { 1f, 0.8f, 0.45f, 1f };
|
||||||
public float lightIntensity = 1.0f;
|
public float lightRange = 0.8f;
|
||||||
public float tipUp = 0f; // 끝에서 더 올릴 높이(m)
|
public float lightIntensity = 0.3f;
|
||||||
public string material = "WL/Fx/WL_Mat_ChargeHalo"; // Resources 경로
|
public float tipPull = 0f; // 끝에서 손 쪽으로 당길 거리(m · 둥근 면 안쪽에 두고 싶을 때)
|
||||||
}
|
}
|
||||||
|
|
||||||
[System.Serializable]
|
[System.Serializable]
|
||||||
|
|
@ -54,7 +55,7 @@ namespace WL.Combat.Weapon
|
||||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||||
static void ResetStatics() { s_loaded = false; s_cfg = null; } // 도메인 리로드 끔 대비(JSON 재읽기)
|
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)
|
public static bool TryAttach(Transform weapon)
|
||||||
{
|
{
|
||||||
|
|
@ -76,67 +77,66 @@ namespace WL.Combat.Weapon
|
||||||
var g = weapon.gameObject.AddComponent<WLWeaponTipGlow>();
|
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._weapon = weapon; g._socket = weapon.parent != null ? weapon.parent : weapon; g._r = rule; g._t0 = Time.time;
|
||||||
g.Build();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
catch (System.Exception ex) { LastLog = "무기 끝 빛 실패 — " + ex.Message; return false; }
|
catch (System.Exception ex) { LastLog = "무기 끝 빛 실패 — " + ex.Message; return false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>무기 끝 = 소켓에서 렌더러 경계 중심을 향한 축 위, 가장 멀리 투영되는 모서리의 투영점(둥근 끝면 중심 · 상자 모서리가 아님).</summary>
|
||||||
void Build()
|
void Build()
|
||||||
{
|
{
|
||||||
var r = _r;
|
var r = _r;
|
||||||
// 무기 끝 = 소켓에서 가장 먼 렌더러 경계 모서리
|
bool any = false; Bounds b = new Bounds(_weapon.position, Vector3.zero);
|
||||||
Vector3 tip = _weapon.position; float best = -1f;
|
|
||||||
foreach (var rd in _weapon.GetComponentsInChildren<Renderer>(false))
|
foreach (var rd in _weapon.GetComponentsInChildren<Renderer>(false))
|
||||||
{
|
{
|
||||||
if (rd is ParticleSystemRenderer || !rd.enabled) continue;
|
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++)
|
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);
|
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);
|
_localTip = _weapon.InverseTransformPoint(tip);
|
||||||
_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 holder = new GameObject("~WL_TipGlow"); _holder = holder.transform; // 씬 루트(무기 스케일 영향 0)
|
||||||
var col = q.GetComponent<Collider>(); if (col != null) Destroy(col);
|
_holder.position = tip;
|
||||||
q.transform.SetParent(_weapon, false); q.transform.localScale = Vector3.one * r.haloScale;
|
var p = Resources.Load<GameObject>(r.prefab);
|
||||||
var src = Resources.Load<Material>(r.material);
|
if (p != null)
|
||||||
var mr = q.GetComponent<MeshRenderer>();
|
{
|
||||||
if (src != null) { _mat = new Material(src); mr.sharedMaterial = _mat; SetColor(_col); }
|
var fx = Instantiate(p, _holder); fx.transform.localPosition = Vector3.zero; fx.transform.localRotation = Quaternion.identity; fx.transform.localScale = Vector3.one * r.scale;
|
||||||
mr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; mr.receiveShadows = false;
|
foreach (var ps in fx.GetComponentsInChildren<ParticleSystem>(true)) { var m = ps.main; m.loop = true; m.playOnAwake = true; ps.Play(true); }
|
||||||
_halo = q.transform;
|
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)
|
if (r.light != 0)
|
||||||
{
|
{
|
||||||
var lg = new GameObject("~WL_TipLight"); lg.transform.SetParent(_halo, false);
|
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.color = _col; _light.shadows = LightShadows.None;
|
_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()
|
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);
|
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);
|
_holder.position = _weapon.TransformPoint(_localTip);
|
||||||
var cam = Camera.main;
|
_holder.localScale = Vector3.one * k;
|
||||||
_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;
|
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); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue