[WL] #939~#942 피격 이펙트 교체(검 SlashHitBlue · 활 PinkSlashHit · 박힌 화살 제거) · 창 2타 찌르기 6연타 + 기계총 가림(창 방향 추종) · 피격 이펙트 몸 앞(카메라 쪽) · 시전 뒤 이동해도 지팡이 주력·활 각성 유지(lockUntilFire)

- WLImpactFx: swingFx follow/forward/side/life/minInterval/alignWeapon/massLocal · WLWeaponFollowFx · TowardCamera(hitTowardCamera) · 풀 반환은 끄기 전 Update
- CombatAnimMapping 10701 Attack2~2f(속도 3 · 타격 3회) → pcanim_spear.controller · Spearman@Attack2_S 서브클립
- 사본 WL_Hit_Sword/SwordSkill(SlashHitBlue) · WL_Hit_Bow(PinkSlashHit) · WL_Impact_SpearBurst(FX_Machine Gun · loop 끔 · 줄기·섬광 평탄화)
- WLSkillCancel: lockUntilFire(Skill_Hurricane · Skill_ArrowRain) · noSkip += Skill_Hurricane
- 피격 카메라 쪽 당김: WLProjectileHitFx · WLSkillVfx hit · WLGreatWave · WLBladeStorm

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
깃 관리자 2026-10-19 02:27:34 +09:00
parent c421d272fc
commit 9202f9c8fe
19 changed files with 313776 additions and 51586 deletions

View File

@ -733,7 +733,7 @@ ModelImporter:
functionName: Move functionName: Move
data: data:
objectReferenceParameter: {instanceID: 0} objectReferenceParameter: {instanceID: 0}
floatParameter: 0.1 floatParameter: 0.05
intParameter: 0 intParameter: 0
messageOptions: 0 messageOptions: 0
- time: 0.44 - time: 0.44
@ -840,14 +840,7 @@ ModelImporter:
functionName: Move functionName: Move
data: data:
objectReferenceParameter: {instanceID: 0} objectReferenceParameter: {instanceID: 0}
floatParameter: 0.1 floatParameter: 0.05
intParameter: 0
messageOptions: 0
- time: 0.44
functionName: Projectile
data: Projectile_10101
objectReferenceParameter: {instanceID: 0}
floatParameter: 0
intParameter: 0 intParameter: 0
messageOptions: 0 messageOptions: 0
transformMask: [] transformMask: []
@ -897,7 +890,7 @@ ModelImporter:
functionName: Move functionName: Move
data: data:
objectReferenceParameter: {instanceID: 0} objectReferenceParameter: {instanceID: 0}
floatParameter: 0.1 floatParameter: 0.05
intParameter: 0 intParameter: 0
messageOptions: 0 messageOptions: 0
- time: 0.44 - time: 0.44
@ -911,6 +904,163 @@ ModelImporter:
maskType: 0 maskType: 0
maskSource: {instanceID: 0} maskSource: {instanceID: 0}
additiveReferencePoseFrame: 0 additiveReferencePoseFrame: 0
- serializedVersion: 16
name: Attack2d_S_10701
takeName: Take 001
internalID: -5059006814000262888
firstFrame: 33
lastFrame: 46
wrapMode: 0
orientationOffsetY: 0
level: 0
cycleOffset: 0
loop: 0
hasAdditiveReferencePose: 0
loopTime: 0
loopBlend: 0
loopBlendOrientation: 1
loopBlendPositionY: 1
loopBlendPositionXZ: 0
keepOriginalOrientation: 1
keepOriginalPositionY: 1
keepOriginalPositionXZ: 1
heightFromFeet: 0
mirror: 0
bodyMask: 01000000010000000100000001000000010000000100000001000000010000000100000001000000010000000100000001000000
curves: []
events:
- time: 0
functionName: LookAtTarget
data:
objectReferenceParameter: {instanceID: 0}
floatParameter: 0
intParameter: 0
messageOptions: 0
- time: 0.32
functionName: ShowEffect
data: Effect_Slash_10101_2
objectReferenceParameter: {instanceID: 0}
floatParameter: 0
intParameter: 1
messageOptions: 0
- time: 0.38
functionName: Move
data:
objectReferenceParameter: {instanceID: 0}
floatParameter: 0.05
intParameter: 0
messageOptions: 0
transformMask: []
maskType: 0
maskSource: {instanceID: 0}
additiveReferencePoseFrame: 0
- serializedVersion: 16
name: Attack2e_S_10701
takeName: Take 001
internalID: 6234510843852800090
firstFrame: 33
lastFrame: 46
wrapMode: 0
orientationOffsetY: 0
level: 0
cycleOffset: 0
loop: 0
hasAdditiveReferencePose: 0
loopTime: 0
loopBlend: 0
loopBlendOrientation: 1
loopBlendPositionY: 1
loopBlendPositionXZ: 0
keepOriginalOrientation: 1
keepOriginalPositionY: 1
keepOriginalPositionXZ: 1
heightFromFeet: 0
mirror: 0
bodyMask: 01000000010000000100000001000000010000000100000001000000010000000100000001000000010000000100000001000000
curves: []
events:
- time: 0
functionName: LookAtTarget
data:
objectReferenceParameter: {instanceID: 0}
floatParameter: 0
intParameter: 0
messageOptions: 0
- time: 0.32
functionName: ShowEffect
data: Effect_Slash_10101_2
objectReferenceParameter: {instanceID: 0}
floatParameter: 0
intParameter: 1
messageOptions: 0
- time: 0.38
functionName: Move
data:
objectReferenceParameter: {instanceID: 0}
floatParameter: 0.05
intParameter: 0
messageOptions: 0
- time: 0.44
functionName: Projectile
data: Projectile_10101
objectReferenceParameter: {instanceID: 0}
floatParameter: 0
intParameter: 0
messageOptions: 0
transformMask: []
maskType: 0
maskSource: {instanceID: 0}
additiveReferencePoseFrame: 0
- serializedVersion: 16
name: Attack2f_S_10701
takeName: Take 001
internalID: -6788666626592393668
firstFrame: 33
lastFrame: 46
wrapMode: 0
orientationOffsetY: 0
level: 0
cycleOffset: 0
loop: 0
hasAdditiveReferencePose: 0
loopTime: 0
loopBlend: 0
loopBlendOrientation: 1
loopBlendPositionY: 1
loopBlendPositionXZ: 0
keepOriginalOrientation: 1
keepOriginalPositionY: 1
keepOriginalPositionXZ: 1
heightFromFeet: 0
mirror: 0
bodyMask: 01000000010000000100000001000000010000000100000001000000010000000100000001000000010000000100000001000000
curves: []
events:
- time: 0
functionName: LookAtTarget
data:
objectReferenceParameter: {instanceID: 0}
floatParameter: 0
intParameter: 0
messageOptions: 0
- time: 0.32
functionName: ShowEffect
data: Effect_Slash_10101_2
objectReferenceParameter: {instanceID: 0}
floatParameter: 0
intParameter: 1
messageOptions: 0
- time: 0.38
functionName: Move
data:
objectReferenceParameter: {instanceID: 0}
floatParameter: 0.05
intParameter: 0
messageOptions: 0
transformMask: []
maskType: 0
maskSource: {instanceID: 0}
additiveReferencePoseFrame: 0
isReadable: 1 isReadable: 1
meshes: meshes:
lODScreenPercentages: [] lODScreenPercentages: []

View File

@ -80,10 +80,10 @@ AnimatorState:
m_PrefabInstance: {fileID: 0} m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
m_Name: Attack2c m_Name: Attack2c
m_Speed: 1.5 m_Speed: 3
m_CycleOffset: 0 m_CycleOffset: 0
m_Transitions: m_Transitions:
- {fileID: -1702032561837743134} - {fileID: 6867887696765296130}
m_StateMachineBehaviours: [] m_StateMachineBehaviours: []
m_Position: {x: 50, y: 50, z: 0} m_Position: {x: 50, y: 50, z: 0}
m_IKOnFeet: 0 m_IKOnFeet: 0
@ -346,6 +346,28 @@ AnimatorStateTransition:
m_InterruptionSource: 0 m_InterruptionSource: 0
m_OrderedInterruption: 1 m_OrderedInterruption: 1
m_CanTransitionToSelf: 1 m_CanTransitionToSelf: 1
--- !u!1101 &-5049265843962658925
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions: []
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: -1457214211898673553}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.15
m_TransitionOffset: 0
m_ExitTime: 0.9
m_HasExitTime: 1
m_HasFixedDuration: 0
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1102 &-4643384778099596019 --- !u!1102 &-4643384778099596019
AnimatorState: AnimatorState:
serializedVersion: 6 serializedVersion: 6
@ -354,7 +376,7 @@ AnimatorState:
m_PrefabInstance: {fileID: 0} m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
m_Name: Attack2b m_Name: Attack2b
m_Speed: 1.5 m_Speed: 3
m_CycleOffset: 0 m_CycleOffset: 0
m_Transitions: m_Transitions:
- {fileID: -6303488384549399601} - {fileID: -6303488384549399601}
@ -458,6 +480,56 @@ AnimatorState:
m_MirrorParameter: m_MirrorParameter:
m_CycleOffsetParameter: m_CycleOffsetParameter:
m_TimeParameter: m_TimeParameter:
--- !u!1102 &-2630414053381351969
AnimatorState:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Attack2f
m_Speed: 3
m_CycleOffset: 0
m_Transitions:
- {fileID: 3733479137453790388}
m_StateMachineBehaviours: []
m_Position: {x: 50, y: 50, z: 0}
m_IKOnFeet: 0
m_WriteDefaultValues: 1
m_Mirror: 0
m_SpeedParameterActive: 0
m_MirrorParameterActive: 0
m_CycleOffsetParameterActive: 0
m_TimeParameterActive: 0
m_Motion: {fileID: -6788666626592393668, guid: 53058ef486fc88f49820c30b8ca9e674,
type: 3}
m_Tag:
m_SpeedParameter:
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!1101 &-2463007587306551008
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions: []
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: -2630414053381351969}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.15
m_TransitionOffset: 0
m_ExitTime: 0.9
m_HasExitTime: 1
m_HasFixedDuration: 0
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1102 &-1818568085792577103 --- !u!1102 &-1818568085792577103
AnimatorState: AnimatorState:
serializedVersion: 6 serializedVersion: 6
@ -466,7 +538,7 @@ AnimatorState:
m_PrefabInstance: {fileID: 0} m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
m_Name: Attack2 m_Name: Attack2
m_Speed: 1.5 m_Speed: 3
m_CycleOffset: 0 m_CycleOffset: 0
m_Transitions: m_Transitions:
- {fileID: 1829211669066088794} - {fileID: 1829211669066088794}
@ -486,28 +558,34 @@ AnimatorState:
m_MirrorParameter: m_MirrorParameter:
m_CycleOffsetParameter: m_CycleOffsetParameter:
m_TimeParameter: m_TimeParameter:
--- !u!1101 &-1702032561837743134 --- !u!1102 &-1457214211898673553
AnimatorStateTransition: AnimatorState:
serializedVersion: 6
m_ObjectHideFlags: 1 m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0} m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0} m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
m_Name: m_Name: Attack2e
m_Conditions: [] m_Speed: 3
m_DstStateMachine: {fileID: 0} m_CycleOffset: 0
m_DstState: {fileID: -4047895833455868784} m_Transitions:
m_Solo: 0 - {fileID: -2463007587306551008}
m_Mute: 0 m_StateMachineBehaviours: []
m_IsExit: 0 m_Position: {x: 50, y: 50, z: 0}
serializedVersion: 3 m_IKOnFeet: 0
m_TransitionDuration: 0.15 m_WriteDefaultValues: 1
m_TransitionOffset: 0 m_Mirror: 0
m_ExitTime: 0.9 m_SpeedParameterActive: 0
m_HasExitTime: 1 m_MirrorParameterActive: 0
m_HasFixedDuration: 0 m_CycleOffsetParameterActive: 0
m_InterruptionSource: 0 m_TimeParameterActive: 0
m_OrderedInterruption: 1 m_Motion: {fileID: 6234510843852800090, guid: 53058ef486fc88f49820c30b8ca9e674,
m_CanTransitionToSelf: 1 type: 3}
m_Tag:
m_SpeedParameter:
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!1102 &-1383661589576729455 --- !u!1102 &-1383661589576729455
AnimatorState: AnimatorState:
serializedVersion: 6 serializedVersion: 6
@ -2246,6 +2324,15 @@ AnimatorStateMachine:
- serializedVersion: 1 - serializedVersion: 1
m_State: {fileID: -7785429122263097812} m_State: {fileID: -7785429122263097812}
m_Position: {x: 80, y: 100, z: 0} m_Position: {x: 80, y: 100, z: 0}
- serializedVersion: 1
m_State: {fileID: 53093606317774197}
m_Position: {x: 115, y: 165, z: 0}
- serializedVersion: 1
m_State: {fileID: -1457214211898673553}
m_Position: {x: 150, y: 230, z: 0}
- serializedVersion: 1
m_State: {fileID: -2630414053381351969}
m_Position: {x: 185, y: 295, z: 0}
m_ChildStateMachines: [] m_ChildStateMachines: []
m_AnyStateTransitions: [] m_AnyStateTransitions: []
m_EntryTransitions: [] m_EntryTransitions: []
@ -2310,6 +2397,34 @@ AnimatorStateMachine:
m_ExitPosition: {x: 300, y: 60, z: 0} m_ExitPosition: {x: 300, y: 60, z: 0}
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
m_DefaultState: {fileID: 110211406} m_DefaultState: {fileID: 110211406}
--- !u!1102 &53093606317774197
AnimatorState:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Attack2d
m_Speed: 3
m_CycleOffset: 0
m_Transitions:
- {fileID: -5049265843962658925}
m_StateMachineBehaviours: []
m_Position: {x: 50, y: 50, z: 0}
m_IKOnFeet: 0
m_WriteDefaultValues: 1
m_Mirror: 0
m_SpeedParameterActive: 0
m_MirrorParameterActive: 0
m_CycleOffsetParameterActive: 0
m_TimeParameterActive: 0
m_Motion: {fileID: -5059006814000262888, guid: 53058ef486fc88f49820c30b8ca9e674,
type: 3}
m_Tag:
m_SpeedParameter:
m_MirrorParameter:
m_CycleOffsetParameter:
m_TimeParameter:
--- !u!1102 &146044103495546524 --- !u!1102 &146044103495546524
AnimatorState: AnimatorState:
serializedVersion: 6 serializedVersion: 6
@ -2696,6 +2811,28 @@ AnimatorState:
m_MirrorParameter: m_MirrorParameter:
m_CycleOffsetParameter: m_CycleOffsetParameter:
m_TimeParameter: m_TimeParameter:
--- !u!1101 &3733479137453790388
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions: []
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: -4047895833455868784}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.15
m_TransitionOffset: 0
m_ExitTime: 0.9
m_HasExitTime: 1
m_HasFixedDuration: 0
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1101 &4622039596610312686 --- !u!1101 &4622039596610312686
AnimatorStateTransition: AnimatorStateTransition:
m_ObjectHideFlags: 1 m_ObjectHideFlags: 1
@ -2878,6 +3015,28 @@ AnimatorStateTransition:
m_InterruptionSource: 0 m_InterruptionSource: 0
m_OrderedInterruption: 1 m_OrderedInterruption: 1
m_CanTransitionToSelf: 1 m_CanTransitionToSelf: 1
--- !u!1101 &6867887696765296130
AnimatorStateTransition:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_Conditions: []
m_DstStateMachine: {fileID: 0}
m_DstState: {fileID: 53093606317774197}
m_Solo: 0
m_Mute: 0
m_IsExit: 0
serializedVersion: 3
m_TransitionDuration: 0.15
m_TransitionOffset: 0
m_ExitTime: 0.9
m_HasExitTime: 1
m_HasFixedDuration: 0
m_InterruptionSource: 0
m_OrderedInterruption: 1
m_CanTransitionToSelf: 1
--- !u!1102 &8500637094174956195 --- !u!1102 &8500637094174956195
AnimatorState: AnimatorState:
serializedVersion: 6 serializedVersion: 6

View File

@ -1368,7 +1368,7 @@ MonoBehaviour:
floatParameter: 0 floatParameter: 0
- classId: 10701 - classId: 10701
note: "\uCC3D \xB7 Spear(WL 10701) \u2192 Spearman Attack2_S(1\uD0C0) \xB7 \uCC0C\uB974\uAE30 note: "\uCC3D \xB7 Spear(WL 10701) \u2192 Spearman Attack2_S(1\uD0C0) \xB7 \uCC0C\uB974\uAE30
\uAD6C\uAC04 \xD73(2\uD0C0) \xB7 Attack1(3\uD0C0) (#840 \uC7AC\uBC30\uC5F4)" \uAD6C\uAC04 \xD73(2\uD0C0) \xB7 Attack1(3\uD0C0) (#840 \uC7AC\uBC30\uC5F4 \xB7 #940 2\uD0C0 = \uCC0C\uB974\uAE30 6\uC5F0\uD0C0 \uC18D\uB3C4 3 \xB7 \uD0C0\uACA9 3)"
controllerPathOverride: Assets/Res_Addr/Animations/pcanim_spear.controller controllerPathOverride: Assets/Res_Addr/Animations/pcanim_spear.controller
enabled: 1 enabled: 1
states: states:
@ -1411,7 +1411,7 @@ MonoBehaviour:
createIfMissing: 0 createIfMissing: 0
firstFrame: 33 firstFrame: 33
lastFrame: 46 lastFrame: 46
speed: 1.5 speed: 3
events: events:
- functionName: LookAtTarget - functionName: LookAtTarget
time: 0 time: 0
@ -1433,7 +1433,7 @@ MonoBehaviour:
normalizedTime: 0.38 normalizedTime: 0.38
stringParameter: stringParameter:
intParameter: 0 intParameter: 0
floatParameter: 0.1 floatParameter: 0.05
- functionName: Projectile - functionName: Projectile
time: 0 time: 0
useNormalized: 1 useNormalized: 1
@ -1449,7 +1449,7 @@ MonoBehaviour:
createIfMissing: 1 createIfMissing: 1
firstFrame: 33 firstFrame: 33
lastFrame: 46 lastFrame: 46
speed: 1.5 speed: 3
events: events:
- functionName: LookAtTarget - functionName: LookAtTarget
time: 0 time: 0
@ -1471,14 +1471,7 @@ MonoBehaviour:
normalizedTime: 0.38 normalizedTime: 0.38
stringParameter: stringParameter:
intParameter: 0 intParameter: 0
floatParameter: 0.1 floatParameter: 0.05
- functionName: Projectile
time: 0
useNormalized: 1
normalizedTime: 0.44
stringParameter: Projectile_10101
intParameter: 0
floatParameter: 0
- stateName: Attack2c - stateName: Attack2c
sourceFbxPath: Assets/ExplosiveLLC/Spearman Warrior Mecanim Animation Pack/Animations/Spearman@Attack2_S.FBX sourceFbxPath: Assets/ExplosiveLLC/Spearman Warrior Mecanim Animation Pack/Animations/Spearman@Attack2_S.FBX
subClipName: Attack2c_S_{CID} subClipName: Attack2c_S_{CID}
@ -1487,7 +1480,7 @@ MonoBehaviour:
createIfMissing: 1 createIfMissing: 1
firstFrame: 33 firstFrame: 33
lastFrame: 46 lastFrame: 46
speed: 1.5 speed: 3
events: events:
- functionName: LookAtTarget - functionName: LookAtTarget
time: 0 time: 0
@ -1509,7 +1502,7 @@ MonoBehaviour:
normalizedTime: 0.38 normalizedTime: 0.38
stringParameter: stringParameter:
intParameter: 0 intParameter: 0
floatParameter: 0.1 floatParameter: 0.05
- functionName: Projectile - functionName: Projectile
time: 0 time: 0
useNormalized: 1 useNormalized: 1
@ -1517,6 +1510,106 @@ MonoBehaviour:
stringParameter: Projectile_10101 stringParameter: Projectile_10101
intParameter: 0 intParameter: 0
floatParameter: 0 floatParameter: 0
- stateName: Attack2d
sourceFbxPath: Assets/ExplosiveLLC/Spearman Warrior Mecanim Animation Pack/Animations/Spearman@Attack2_S.FBX
subClipName: Attack2d_S_{CID}
sourceClipName:
incomingExitTime: 0.9
createIfMissing: 1
firstFrame: 33
lastFrame: 46
speed: 3
events:
- functionName: LookAtTarget
time: 0
useNormalized: 1
normalizedTime: 0
stringParameter:
intParameter: 0
floatParameter: 0
- functionName: ShowEffect
time: 0
useNormalized: 1
normalizedTime: 0.32
stringParameter: Effect_Slash_10101_2
intParameter: 1
floatParameter: 0
- functionName: Move
time: 0
useNormalized: 1
normalizedTime: 0.38
stringParameter:
intParameter: 0
floatParameter: 0.05
- stateName: Attack2e
sourceFbxPath: Assets/ExplosiveLLC/Spearman Warrior Mecanim Animation Pack/Animations/Spearman@Attack2_S.FBX
subClipName: Attack2e_S_{CID}
sourceClipName:
incomingExitTime: 0.9
createIfMissing: 1
firstFrame: 33
lastFrame: 46
speed: 3
events:
- functionName: LookAtTarget
time: 0
useNormalized: 1
normalizedTime: 0
stringParameter:
intParameter: 0
floatParameter: 0
- functionName: ShowEffect
time: 0
useNormalized: 1
normalizedTime: 0.32
stringParameter: Effect_Slash_10101_2
intParameter: 1
floatParameter: 0
- functionName: Move
time: 0
useNormalized: 1
normalizedTime: 0.38
stringParameter:
intParameter: 0
floatParameter: 0.05
- functionName: Projectile
time: 0
useNormalized: 1
normalizedTime: 0.44
stringParameter: Projectile_10101
intParameter: 0
floatParameter: 0
- stateName: Attack2f
sourceFbxPath: Assets/ExplosiveLLC/Spearman Warrior Mecanim Animation Pack/Animations/Spearman@Attack2_S.FBX
subClipName: Attack2f_S_{CID}
sourceClipName:
incomingExitTime: 0.9
createIfMissing: 1
firstFrame: 33
lastFrame: 46
speed: 3
events:
- functionName: LookAtTarget
time: 0
useNormalized: 1
normalizedTime: 0
stringParameter:
intParameter: 0
floatParameter: 0
- functionName: ShowEffect
time: 0
useNormalized: 1
normalizedTime: 0.32
stringParameter: Effect_Slash_10101_2
intParameter: 1
floatParameter: 0
- functionName: Move
time: 0
useNormalized: 1
normalizedTime: 0.38
stringParameter:
intParameter: 0
floatParameter: 0.05
- stateName: Attack3 - stateName: Attack3
sourceFbxPath: Assets/ExplosiveLLC/Spearman Warrior Mecanim Animation Pack/Animations/Spearman@Attack1.FBX sourceFbxPath: Assets/ExplosiveLLC/Spearman Warrior Mecanim Animation Pack/Animations/Spearman@Attack1.FBX
subClipName: Attack3_S_{CID} subClipName: Attack3_S_{CID}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 0eda445e0da615240b665d3520bdd2b8 guid: b045b4c9da72b1f44ac7a72a916693e5
PrefabImporter: PrefabImporter:
externalObjects: {} externalObjects: {}
userData: userData:

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 5f30f9a67194e8046aa88d000ef33761 guid: 8951081eddb29cc44bd9b735a9bbb96e
PrefabImporter: PrefabImporter:
externalObjects: {} externalObjects: {}
userData: userData:

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: a0d786a009561ff41b2158114e70f619 guid: b511e0a9c336908459c2fc3d8be6d6fc
PrefabImporter: PrefabImporter:
externalObjects: {} externalObjects: {}
userData: userData:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 978527634d822e34bbb34ddf9543ac06
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -4,6 +4,11 @@
"sizeFactorMin": 0.5, "sizeFactorMin": 0.5,
"sizeFactorMax": 3.0, "sizeFactorMax": 3.0,
"bodyCenterFraction": 0.5, "bodyCenterFraction": 0.5,
"_note_941": "#941 PD 「피격 이펙트는 몬스터보다 상단에 보여야 해」 — 피격 이펙트(hitFx · skillHitFx · 투사체별 명중 · 방패 치기 · 대검)를 몸 중심에서 카메라 쪽으로 (몸 너비 × 0.6 · 0.5~1.5 m) × hitTowardCamera 만큼 당긴다(화면 위치는 그대로 · 0 = 끔)",
"hitTowardCamera": 1.0,
"hitTowardWidthMul": 0.6,
"hitTowardMin": 0.5,
"hitTowardMax": 1.5,
"flyingGap": 0.1, "flyingGap": 0.1,
"profileWarmup": true, "profileWarmup": true,
"profileSeconds": 2.5, "profileSeconds": 2.5,
@ -15,7 +20,7 @@
], ],
"prefab": "WL_Hit_Sword", "prefab": "WL_Hit_Sword",
"scale": 1.0, "scale": 1.0,
"fit": 1.3, "fit": 1.7,
"offsetCompensate": true "offsetCompensate": true
}, },
{ {
@ -47,7 +52,7 @@
"skills": [], "skills": [],
"prefab": "WL_Hit_SwordSkill", "prefab": "WL_Hit_SwordSkill",
"scale": 1.0, "scale": 1.0,
"fit": 1.5, "fit": 1.9,
"offsetCompensate": true "offsetCompensate": true
}, },
{ {
@ -242,6 +247,37 @@
"emitter": "slash_add", "emitter": "slash_add",
"radiusFit": 1.68, "radiusFit": 1.68,
"sweepFlip": false "sweepFlip": false
},
{
"_note_940": "#940 PD 「창 2타 = 빠른 찌르기 연타 + 무기 부분을 FX_Machine_Gun 으로 가림」 — 사본 WL_Impact_SpearBurst(EricWang FX_Machine Gun · loop 끔 · 줄기 line/fire_line/trail 의 X·Y 기울기 0 = 지면 평행) · 이 이펙트는 총구 섬광이 로컬 +X 쪽, 탄 줄기는 −X 로 날아가는 2D 측면 연출(편집 모드 면 대면 렌더로 확인) → 로컬 X 를 PC 뒤쪽으로(euler -90,90,0: X→뒤 · Y→왼쪽 · Z→위 · XY 면 = 지면 평행) 두면 줄기가 적 쪽으로 난다 · 6연타 ShowEffect 중 첫 이벤트만 띄움(minInterval) · PC 를 따라감 · 1.0 초 뒤 풀 반환. 위치 = Play 실측(Sp940.Snap): 창 몸통 중심 = PC 기준 오른쪽 0.3 · 앞 0.3 · 발 위 0.37 m · 섬광 묶음 중심은 원점에서 로컬 (X 1.06 · Y 1.62 · Z -0.33) × 실제 배율만큼 벗어남(X→뒤 · Y→왼쪽이므로 세계 기준 뒤 0.385 · 왼쪽 0.59 · 아래 0.12) → forward/side/yOffset 은 「목표 + 벗어남」(실제 배율 0.363 = scale 0.6 × fallbackScale 0.55 × SizeFactor 1.1): forward 0.5 + 0.385 · side 0.3 + 0.59 · yOffset −0.15 + 0.12. 섬광 Fire_Flash 6개도 기울기 0 + Z 각 앞쪽 부채꼴(−35°~+40°) 로 고정(원본 3D 회전은 옆에서 보는 2D 연출용 · 위 카메라에선 빛줄기가 뒤를 봄)",
"classIds": [
10701
],
"stages": [
2
],
"prefab": "WL_Impact_SpearBurst",
"scale": 0.6,
"euler": [
-90,
90,
0
],
"yOffset": -0.03,
"calibrated": false,
"follow": true,
"forward": 0.885,
"side": 0.89,
"life": 1.0,
"minInterval": 0.8,
"_note_941": "#941 PD 「창 공격 시 이펙트와 캐릭터 방향이 일치하게」 — alignWeapon: 매 프레임 창 소켓(손잡이 → 창끝 · 수평) 방향으로 돌리고 손잡이에서 길이 × alignFraction 자리에 둔다(forward/side 는 이때 안 씀 · massLocal 로 섬광 묶음 중심 보정)",
"alignWeapon": true,
"alignFraction": 0.55,
"massLocal": [
1.06,
1.62,
-0.33
]
} }
], ],
"groundFx": [ "groundFx": [

View File

@ -28,7 +28,11 @@ namespace WL.Combat.Impact
public sealed class WLSwingFxRule { public int[] classIds; public int[] stages; public string prefab; public float scale = 1f; public float[] euler; public float yOffset; public bool calibrated = true; public string emitter = ""; public float radiusFit = 1f; public bool sweepFlip; public float fallbackScale = 0.55f; public sealed class WLSwingFxRule { public int[] classIds; public int[] stages; public string prefab; public float scale = 1f; public float[] euler; public float yOffset; public bool calibrated = true; public string emitter = ""; public float radiusFit = 1f; public bool sweepFlip; public float fallbackScale = 0.55f;
public float minFacing; public float minFacing;
// #932 PD 「대검 1타는 모션을 내려치기로 바꾸지 말고 대각선 베기하면서 이펙트만 좀 더 잘보이게」 — 1 = 기울임 축을 호의 현(시작 → 끝 선 · 면 법선 × 이등분) 으로: 베는 방향(대각선)은 그대로 두고 초승달 가운데만 카메라 쪽으로 펼친다 · maxTilt = 최대 각(도) // #932 PD 「대검 1타는 모션을 내려치기로 바꾸지 말고 대각선 베기하면서 이펙트만 좀 더 잘보이게」 — 1 = 기울임 축을 호의 현(시작 → 끝 선 · 면 법선 × 이등분) 으로: 베는 방향(대각선)은 그대로 두고 초승달 가운데만 카메라 쪽으로 펼친다 · maxTilt = 최대 각(도)
public int facingAxis; public float maxTilt = 40f; } // #928 0 보다 크면 초승달 면 법선과 시선의 |cos| 이 이 값보다 작을 때(카메라에서 면이 얇게 보일 때) 면을 카메라 쪽으로 그 값까지만 기울인다(피벗 고정) public int facingAxis; public float maxTilt = 40f; // #928 0 보다 크면 초승달 면 법선과 시선의 |cos| 이 이 값보다 작을 때(카메라에서 면이 얇게 보일 때) 면을 카메라 쪽으로 그 값까지만 기울인다(피벗 고정)
// #940 PD 「창 2타 연타 때 무기 부분을 FX_Machine_Gun 으로 가려」 — calibrated 0 전용: follow = PC 에 붙여 따라감 · forward = PC 정면 거리(m) · life = 켜 둘 초(0 = 프리팹 길이) · minInterval = 같은 PC·단계의 ShowEffect 가 이 초 안에 또 오면 새로 띄우지 않고 궤적만 막는다(6연타 이벤트 → 이펙트 1개)
public bool follow; public float forward; public float side; public float life; public float minInterval; // side = PC 오른쪽 거리(m)
// #941 PD 「창 공격 시 이펙트와 캐릭터 방향이 일치하게」 — alignWeapon = 매 프레임 무기 소켓(손잡이 → 무기 끝) 방향에 맞춰 돌리고 손잡이에서 alignFraction 만큼 간 자리에 둔다(forward/side 는 안 씀) · massLocal = 프리팹 로컬 단위의 보이는 묶음 중심(원점 보정 · Play 실측)
public bool alignWeapon; public float alignFraction = 0.5f; public float[] massLocal; }
/// <summary>지면 충격파. tipContact = 칼끝이 지면(contactHeight 이내)에 닿는 프레임·자리에서 재생(단계 안에서 minDrop 이상 올라갔다 내려올 때) · 못 잡으면 timeout 뒤 최저점(fallbackHeight 이내)에.</summary> /// <summary>지면 충격파. tipContact = 칼끝이 지면(contactHeight 이내)에 닿는 프레임·자리에서 재생(단계 안에서 minDrop 이상 올라갔다 내려올 때) · 못 잡으면 timeout 뒤 최저점(fallbackHeight 이내)에.</summary>
/// <summary>#900 frontOnly = PC 정면 쪽 접점만 인정(해머 1타 내려치기 — 뒤로 휘두를 때 등 뒤 지면 접점을 건너뛴다 · 2026-10-17 실측 −0.8 m).</summary> /// <summary>#900 frontOnly = PC 정면 쪽 접점만 인정(해머 1타 내려치기 — 뒤로 휘두를 때 등 뒤 지면 접점을 건너뛴다 · 2026-10-17 실측 −0.8 m).</summary>
public sealed class WLGroundFxRule { public int[] classIds; public int stage; public string prefab; public float scale = 1f; public float forward; public bool tipContact = true; public float contactHeight = 0.15f; public float minDrop = 0.35f; public float timeout = 1.5f; public float fallbackHeight = 0.5f; public bool frontOnly; } public sealed class WLGroundFxRule { public int[] classIds; public int stage; public string prefab; public float scale = 1f; public float forward; public bool tipContact = true; public float contactHeight = 0.15f; public float minDrop = 0.35f; public float timeout = 1.5f; public float fallbackHeight = 0.5f; public bool frontOnly; }
@ -45,6 +49,8 @@ namespace WL.Combat.Impact
public bool enabled = true; public bool enabled = true;
public float referenceHeight = 1f, sizeFactorMin = 0.5f, sizeFactorMax = 3f; public float referenceHeight = 1f, sizeFactorMin = 0.5f, sizeFactorMax = 3f;
public float bodyCenterFraction = 0.5f; // 몸 중심 높이 = 발 + 키 × 이 값 public float bodyCenterFraction = 0.5f; // 몸 중심 높이 = 발 + 키 × 이 값
// #941 PD 「피격 이펙트는 몬스터보다 상단에 보여야 해(몬스터에게 가려지기 때문)」 — 피격 이펙트를 몸 중심에서 카메라 쪽으로 (몸 너비 × hitTowardWidthMul · hitTowardMin~Max m) × hitTowardCamera 만큼 당겨 몸 앞에 그린다(화면 위치는 그대로) · 0 = 끔
public float hitTowardCamera = 1f, hitTowardWidthMul = 0.6f, hitTowardMin = 0.5f, hitTowardMax = 1.5f;
public float flyingGap = 0.1f; // #921 몸 그림 바닥이 발보다 이만큼(m) 넘게 높으면 떠 있는 몹 — 몸 중심 = 지금 그림(bounds) 가운데(오르내려도 정중앙) · 0 = 끔 public float flyingGap = 0.1f; // #921 몸 그림 바닥이 발보다 이만큼(m) 넘게 높으면 떠 있는 몹 — 몸 중심 = 지금 그림(bounds) 가운데(오르내려도 정중앙) · 0 = 끔
public bool profileWarmup = true; // 프리팹 프로필 실측(끄면 구 방식 배치) public bool profileWarmup = true; // 프리팹 프로필 실측(끄면 구 방식 배치)
public float profileSeconds = 2.5f; // 프로필 실측 길이 public float profileSeconds = 2.5f; // 프로필 실측 길이
@ -138,6 +144,17 @@ namespace WL.Combat.Impact
} }
public static Vector3 BodyCenter(Actor a) { return a == null ? Vector3.zero : GetBodyBox(a).center; } public static Vector3 BodyCenter(Actor a) { return a == null ? Vector3.zero : GetBodyBox(a).center; }
/// <summary>#941 피격 이펙트를 몸 앞(카메라 쪽)에 두는 이동량 — 카메라가 없으면 0. 다른 피격 경로(방패 치기 · 대검 · 투사체별 명중)도 같은 식을 쓴다.</summary>
public static Vector3 TowardCamera(Vector3 at, float bodyWidth)
{
var s = S; if (s == null || s.hitTowardCamera <= 0f) return Vector3.zero;
var cam = Camera.main; if (cam == null) return Vector3.zero;
var d = cam.transform.position - at; if (d.sqrMagnitude < 1e-4f) return Vector3.zero;
float m = Mathf.Clamp(bodyWidth * s.hitTowardWidthMul, s.hitTowardMin, s.hitTowardMax) * s.hitTowardCamera;
return d.normalized * m;
}
public static Vector3 TowardCamera(Actor a) { if (a == null) return Vector3.zero; var bb = GetBodyBox(a); return TowardCamera(bb.center, bb.width); }
public static float SizeFactor(Actor actor) public static float SizeFactor(Actor actor)
{ {
var s = S; if (actor == null || s == null || s.referenceHeight <= 0f) return 1f; var s = S; if (actor == null || s == null || s.referenceHeight <= 0f) return 1f;
@ -177,9 +194,10 @@ namespace WL.Combat.Impact
float scale; string how; float scale; string how;
if (fit > 0f && prof != null && prof.extent > 1e-3f) { scale = scale0 * fit * bb.height / prof.extent; how = "fit"; } if (fit > 0f && prof != null && prof.extent > 1e-3f) { scale = scale0 * fit * bb.height / prof.extent; how = "fit"; }
else { scale = scale0 * SizeFactor(target); how = "legacy"; } else { scale = scale0 * SizeFactor(target); how = "legacy"; }
Vector3 pos = bb.center - (prof != null && offsetCompensate ? prof.centerOffset * scale : Vector3.zero); var toCam = TowardCamera(bb.center, bb.width); // #941 몸 앞(카메라 쪽)
Vector3 pos = bb.center - (prof != null && offsetCompensate ? prof.centerOffset * scale : Vector3.zero) + toCam;
if (Play(prefab, pos, Quaternion.identity, scale) == null) return false; if (Play(prefab, pos, Quaternion.identity, scale) == null) return false;
LastHit = kind + " " + prefab + " → " + target.name + " (" + why + ") " + how + " scale=" + scale.ToString("F3") + " body h=" + bb.height.ToString("F2") + " w=" + bb.width.ToString("F2") + " center=" + bb.center.ToString("F2") + " feet=" + target.Get_position().ToString("F2"); LastHit = kind + " " + prefab + " → " + target.name + " (" + why + ") " + how + " scale=" + scale.ToString("F3") + " body h=" + bb.height.ToString("F2") + " w=" + bb.width.ToString("F2") + " center=" + bb.center.ToString("F2") + " toCam=" + toCam.magnitude.ToString("F2") + " feet=" + target.Get_position().ToString("F2");
LastLog = LastHit; LastLog = LastHit;
return true; return true;
} }
@ -265,7 +283,7 @@ namespace WL.Combat.Impact
var r = s.swingFx[i]; var r = s.swingFx[i];
if (r == null || !Contains(r.classIds, cls) || (r.stages != null && r.stages.Length > 0 && !Contains(r.stages, stage))) continue; if (r == null || !Contains(r.classIds, cls) || (r.stages != null && r.stages.Length > 0 && !Contains(r.stages, stage))) continue;
var tf = pc.transform; var tf = pc.transform;
Quaternion rot; Vector3 pos; float scale; string how; Quaternion rot; Vector3 pos; float scale; string how; bool cal_used = false;
SwingCalibration cal = null; SwingCalibration cal = null;
var prof = r.calibrated ? Profile(r.prefab, r.emitter) : null; var prof = r.calibrated ? Profile(r.prefab, r.emitter) : null;
if (r.calibrated && SlashTrailSettings.Instance != null) cal = SlashTrailSettings.Instance.FindCalibration(cls, ev.stringParameter); if (r.calibrated && SlashTrailSettings.Instance != null) cal = SlashTrailSettings.Instance.FindCalibration(cls, ev.stringParameter);
@ -290,23 +308,35 @@ namespace WL.Combat.Impact
} }
Vector3 pivotW = tf.position + tf.rotation * cal.pivot; Vector3 pivotW = tf.position + tf.rotation * cal.pivot;
pos = pivotW - rot * (prof.sweepCenter * scale) + Vector3.up * r.yOffset; pos = pivotW - rot * (prof.sweepCenter * scale) + Vector3.up * r.yOffset;
SwingCalibrated++; how = "cal tilt=" + tilt.ToString("F0") + " axis=" + axis.ToString("F2") + " bis=" + bW.ToString("F2") + " sweep=" + cal.sweepDeg.ToString("F0") + (positive ? "+" : "-") + " tipR=" + cal.tipRadius.ToString("F2") + " pivot=" + pivotW.ToString("F2") + " effR=" + prof.sweepRadius.ToString("F2"); SwingCalibrated++; cal_used = true; how = "cal tilt=" + tilt.ToString("F0") + " axis=" + axis.ToString("F2") + " bis=" + bW.ToString("F2") + " sweep=" + cal.sweepDeg.ToString("F0") + (positive ? "+" : "-") + " tipR=" + cal.tipRadius.ToString("F2") + " pivot=" + pivotW.ToString("F2") + " effR=" + prof.sweepRadius.ToString("F2");
} }
else else
{ {
if (r.minInterval > 0f) // #940 연타 이벤트 묶음 — 첫 이벤트만 띄우고 나머지는 궤적만 막는다
{
long key = ((long)pc.GetInstanceID() << 4) | (long)(stage & 15); float last;
if (s_swingAt.TryGetValue(key, out last) && Time.time - last < r.minInterval) { SwingThrottled++; LastSwing = "swing throttled stage " + stage; return true; }
s_swingAt[key] = Time.time;
}
rot = tf.rotation * (r.euler != null && r.euler.Length >= 3 ? Quaternion.Euler(r.euler[0], r.euler[1], r.euler[2]) : Quaternion.identity); rot = tf.rotation * (r.euler != null && r.euler.Length >= 3 ? Quaternion.Euler(r.euler[0], r.euler[1], r.euler[2]) : Quaternion.identity);
scale = r.scale * r.fallbackScale * SizeFactor(pc); scale = r.scale * r.fallbackScale * SizeFactor(pc);
pos = pc.Get_Center_position() + Vector3.up * r.yOffset; pos = pc.Get_Center_position() + tf.forward * r.forward + tf.right * r.side + Vector3.up * r.yOffset;
how = "legacy(" + (cal == null ? "no-cal" : prof == null ? "no-profile" : !prof.sweepValid ? "no-sweep" : "cal-kind") + ")"; how = "legacy(" + (cal == null ? "no-cal" : prof == null ? "no-profile" : !prof.sweepValid ? "no-sweep" : "cal-kind") + ")" + (r.follow ? " follow" : "");
} }
if (Play(r.prefab, pos, rot, scale) == null) return false; var inst = Play(r.prefab, pos, rot, scale); if (inst == null) return false;
if (r.follow) inst.transform.SetParent(tf, true); // #940 PC 를 따라간다(풀 반환 때 WLImpactOneShot 이 원래 부모로 되돌린다)
if (r.life > 0f) { var os = inst.GetComponent<WLImpactOneShot>(); if (os != null) os.OverrideLife(r.life); }
var fw = inst.GetComponent<WLWeaponFollowFx>(); // #941 무기 방향 추종
if (r.alignWeapon && !cal_used) { if (fw == null) fw = inst.AddComponent<WLWeaponFollowFx>(); if (fw.Arm(pc, r, scale)) how += " alignWeapon"; else how += " alignWeapon(no-socket)"; }
else if (fw != null) fw.Disarm();
SwingShown++; LastSwing = "swing " + r.prefab + " stage " + stage + " " + how + " scale=" + scale.ToString("F3"); LastLog = LastSwing; SwingShown++; LastSwing = "swing " + r.prefab + " stage " + stage + " " + how + " scale=" + scale.ToString("F3"); LastLog = LastSwing;
return true; return true;
} }
return false; return false;
} }
public static int SwingTilted; public static int SwingTilted, SwingThrottled;
static readonly Dictionary<long, float> s_swingAt = new Dictionary<long, float>(); // #940 (PC, 단계) → 마지막으로 띄운 시각
/// <summary>#928 PD 「대검 1타 공격이 보는 방향에 따라서 너무 얇게 보여서 … 어느 방향에서 보더라도 검기처럼 보이게」 — /// <summary>#928 PD 「대검 1타 공격이 보는 방향에 따라서 너무 얇게 보여서 … 어느 방향에서 보더라도 검기처럼 보이게」 —
/// 초승달 면 법선 n 과 시선(Camera.main)의 |cos| 이 minFacing 보다 작으면 n 을 시선 쪽(가까운 쪽 부호)으로 그 값이 될 만큼만 돌리는 회전(축 = n × 시선 · 가장 작은 회전) · deg = 돌린 각. /// 초승달 면 법선 n 과 시선(Camera.main)의 |cos| 이 minFacing 보다 작으면 n 을 시선 쪽(가까운 쪽 부호)으로 그 값이 될 만큼만 돌리는 회전(축 = n × 시선 · 가장 작은 회전) · deg = 돌린 각.
@ -597,7 +627,7 @@ namespace WL.Combat.Impact
{ {
if (s_root == null) { var r = new GameObject("WL_ImpactFx"); Object.DontDestroyOnLoad(r); s_root = r.transform; } if (s_root == null) { var r = new GameObject("WL_ImpactFx"); Object.DontDestroyOnLoad(r); s_root = r.transform; }
go = Object.Instantiate(p, s_root); go.name = prefab; go.SetActive(false); go = Object.Instantiate(p, s_root); go.name = prefab; go.SetActive(false);
if (go.GetComponent<WLImpactOneShot>() == null) go.AddComponent<WLImpactOneShot>(); var one = go.GetComponent<WLImpactOneShot>(); if (one == null) one = go.AddComponent<WLImpactOneShot>(); one.home = s_root;
if (list == null) { list = new List<GameObject>(4); s_pool[prefab] = list; } if (list == null) { list = new List<GameObject>(4); s_pool[prefab] = list; }
list.Add(go); list.Add(go);
} }
@ -614,6 +644,36 @@ namespace WL.Combat.Impact
} }
} }
/// <summary>#941 궤적 교체 이펙트를 매 프레임 무기 소켓 방향(손잡이 → 무기 끝 · 수평)에 맞춘다 — 창 2타 기계총 가림처럼 PC 정면이 아니라 무기가 향한 쪽으로 쏘게. 자리 = 손잡이 + 방향 × 길이 × alignFraction + 위 yOffset − R × massLocal × 배율.</summary>
public sealed class WLWeaponFollowFx : MonoBehaviour
{
Transform _socket; Vector3 _tipLocal, _mass; Quaternion _base; float _scale, _frac, _yOff; PCActor _pc; bool _armed;
public static int Frames; public static string LastLog = "";
public bool Arm(PCActor pc, WLSwingFxRule r, float scale)
{
_pc = pc; _scale = scale; _frac = r.alignFraction; _yOff = r.yOffset;
_base = r.euler != null && r.euler.Length >= 3 ? Quaternion.Euler(r.euler[0], r.euler[1], r.euler[2]) : Quaternion.identity;
_mass = r.massLocal != null && r.massLocal.Length >= 3 ? new Vector3(r.massLocal[0], r.massLocal[1], r.massLocal[2]) : Vector3.zero;
_armed = WLImpactRunner.SocketTip(pc, out _socket, out _tipLocal);
if (_armed) Apply();
return _armed;
}
public void Disarm() { _armed = false; }
void OnDisable() { _armed = false; }
void LateUpdate() { if (_armed) Apply(); }
void Apply()
{
if (_socket == null || _pc == null) { _armed = false; return; }
Vector3 hilt = _socket.position, tip = _socket.TransformPoint(_tipLocal);
Vector3 d = tip - hilt; float len = d.magnitude; d.y = 0f;
if (d.sqrMagnitude < 1e-4f) d = _pc.transform.forward; d.Normalize();
var rot = Quaternion.LookRotation(d, Vector3.up) * _base;
Vector3 pos = hilt + d * (len * _frac) + Vector3.up * _yOff - rot * (_mass * _scale);
transform.SetPositionAndRotation(pos, rot); Frames++;
LastLog = "align hilt=" + hilt.ToString("F2") + " tip=" + tip.ToString("F2") + " dir=" + d.ToString("F2") + " pos=" + pos.ToString("F2");
}
}
/// <summary>PC 에 붙는 러너 — 콤보 단계 진입 뒤 칼끝(WeaponTrailDriver) 높이를 추적해 지면 접점에서 충격파를 띄운다.</summary> /// <summary>PC 에 붙는 러너 — 콤보 단계 진입 뒤 칼끝(WeaponTrailDriver) 높이를 추적해 지면 접점에서 충격파를 띄운다.</summary>
public sealed class WLImpactRunner : MonoBehaviour public sealed class WLImpactRunner : MonoBehaviour
{ {
@ -725,6 +785,8 @@ namespace WL.Combat.Impact
public sealed class WLImpactOneShot : MonoBehaviour public sealed class WLImpactOneShot : MonoBehaviour
{ {
ParticleSystem[] _ps; float _life = -1f, _offAt; ParticleSystem[] _ps; float _life = -1f, _offAt;
public Transform home; // #940 풀 루트 — follow 로 PC 에 붙었던 것을 꺼질 때 되돌린다
public void OverrideLife(float sec) { if (sec > 0f) _offAt = Time.time + sec; } // #940 규칙의 life(초)
void OnEnable() void OnEnable()
{ {
if (_ps == null) if (_ps == null)
@ -742,7 +804,8 @@ namespace WL.Combat.Impact
for (int i = 0; i < _ps.Length; i++) { _ps[i].Clear(true); _ps[i].Play(true); } for (int i = 0; i < _ps.Length; i++) { _ps[i].Clear(true); _ps[i].Play(true); }
_offAt = Time.time + _life; _offAt = Time.time + _life;
} }
void Update() { if (Time.time >= _offAt) gameObject.SetActive(false); } // #940 follow 로 PC 에 붙었던 것은 끄기 전에 풀 루트로 되돌린다(OnDisable 안 SetParent 는 「already being activated or deactivated」 오류 — Play 실측 7건)
void Update() { if (Time.time >= _offAt) { if (home != null && transform.parent != home) transform.SetParent(home, false); gameObject.SetActive(false); } }
} }
/// <summary>맞은 투사체를 몸 중심까지 조준 비행시킨 뒤 피격 이펙트와 함께 끈다(지팡이 마법 투사체).</summary> /// <summary>맞은 투사체를 몸 중심까지 조준 비행시킨 뒤 피격 이펙트와 함께 끈다(지팡이 마법 투사체).</summary>

View File

@ -6,7 +6,13 @@
], ],
"noSkip": [ "noSkip": [
"Skill_ArrowRain", "Skill_ArrowRain",
"Skill_FatalBlow" "Skill_FatalBlow",
"Skill_Hurricane"
],
"_note_942": "#942 PD 「마법사 주력 스킬 … 플레이어가 이동해도 발동 된 스킬이 끊겨선 안돼 … 활 각성 스킬도 동일」 — Play 실측(CastMove): 시전 0.4~0.6 초 뒤 이동하면 발동 이벤트(SkillFired) 전에 시전 동작이 Run 으로 끊겨 투사체가 아예 안 나왔다(스킬발사 +0) → lockUntilFire = 발동까지만 이동·구르기 입력 무시(상한 holdMaxSeconds) · 발동 뒤 이동은 자유 · 투사체는 noSkip(Skill_Hurricane 추가 · Skill_ArrowRain 기존)으로 끝까지 유지",
"lockUntilFire": [
"Skill_Hurricane",
"Skill_ArrowRain"
], ],
"holdMove": [ "holdMove": [
"Skill_DashSlash" "Skill_DashSlash"

View File

@ -366,6 +366,7 @@ namespace WL.Combat.Weapon
if (!s_hitPrefabs.TryGetValue(_c.hitFx, out prefab) || prefab == null) { prefab = Resources.Load<GameObject>(_c.hitFx); s_hitPrefabs[_c.hitFx] = prefab; } if (!s_hitPrefabs.TryGetValue(_c.hitFx, out prefab) || prefab == null) { prefab = Resources.Load<GameObject>(_c.hitFx); s_hitPrefabs[_c.hitFx] = prefab; }
if (prefab == null) { WLBladeStorm.LastLog = "히트 이펙트 없음 " + _c.hitFx; return; } if (prefab == null) { WLBladeStorm.LastLog = "히트 이펙트 없음 " + _c.hitFx; return; }
var at = a.Get_Center_position(); var d = at - from; d.y = 0f; if (d.sqrMagnitude < 1e-4f) d = _pc.transform.forward; var at = a.Get_Center_position(); var d = at - from; d.y = 0f; if (d.sqrMagnitude < 1e-4f) d = _pc.transform.forward;
at += WL.Combat.Impact.WLImpactFx.TowardCamera(a); // #941 몸 앞(카메라 쪽)
var go = Instantiate(prefab, at, Quaternion.LookRotation(d) * prefab.transform.rotation); var go = Instantiate(prefab, at, Quaternion.LookRotation(d) * prefab.transform.rotation);
foreach (var ps in go.GetComponentsInChildren<ParticleSystem>(true)) { var m = ps.main; m.scalingMode = ParticleSystemScalingMode.Hierarchy; } foreach (var ps in go.GetComponentsInChildren<ParticleSystem>(true)) { var m = ps.main; m.scalingMode = ParticleSystemScalingMode.Hierarchy; }
go.transform.localScale = prefab.transform.localScale * Mathf.Max(0.01f, _c.hitFxScale); go.transform.localScale = prefab.transform.localScale * Mathf.Max(0.01f, _c.hitFxScale);

View File

@ -522,7 +522,7 @@ namespace WL.Combat.Weapon
{ {
f.hits++; f.last = now; WLGreatWave.Hits++; f.hits++; f.last = now; WLGreatWave.Hits++;
if (f.hits > WLGreatWave.MaxHitsOnOne) WLGreatWave.MaxHitsOnOne = f.hits; if (f.hits > WLGreatWave.MaxHitsOnOne) WLGreatWave.MaxHitsOnOne = f.hits;
WLGreatWave.Spawn(c.hitFx, a.Get_Center_position(), Quaternion.LookRotation(_dir), c.hitFxScale, c.hitFxLife); WLGreatWave.Spawn(c.hitFx, a.Get_Center_position() + WL.Combat.Impact.WLImpactFx.TowardCamera(a), Quaternion.LookRotation(_dir), c.hitFxScale, c.hitFxLife); // #941 몸 앞(카메라 쪽)
} }
} }
// 밀기 — 최대 횟수 전인 동안 앞머리와 함께(보스 제외 · 내비 위) // 밀기 — 최대 횟수 전인 동안 앞머리와 함께(보스 제외 · 내비 위)

View File

@ -177,7 +177,7 @@ namespace WL.Combat.Weapon
GameObject p; GameObject p;
if (!s_prefabs.TryGetValue(prefab, out p) || p == null) { p = Resources.Load<GameObject>(prefab); s_prefabs[prefab] = p; } if (!s_prefabs.TryGetValue(prefab, out p) || p == null) { p = Resources.Load<GameObject>(prefab); s_prefabs[prefab] = p; }
if (p == null) return false; if (p == null) return false;
var go = Instantiate(p, WL.Combat.Impact.WLImpactFx.BodyCenter(target), p.transform.rotation); var go = Instantiate(p, WL.Combat.Impact.WLImpactFx.BodyCenter(target) + WL.Combat.Impact.WLImpactFx.TowardCamera(target), p.transform.rotation); // #941 몸 앞(카메라 쪽)
go.transform.localScale = p.transform.localScale * (scale * WL.Combat.Impact.WLImpactFx.SizeFactor(target)); go.transform.localScale = p.transform.localScale * (scale * WL.Combat.Impact.WLImpactFx.SizeFactor(target));
Destroy(go, Mathf.Max(0.2f, life)); Destroy(go, Mathf.Max(0.2f, life));
Shown++; Shown++;

View File

@ -33,6 +33,7 @@ namespace WL.Combat.Weapon
public string[] noSkip = new string[0]; // 이동 · 구르기로 끊겨도 이펙트를 남기는 스킬 public string[] noSkip = new string[0]; // 이동 · 구르기로 끊겨도 이펙트를 남기는 스킬
public string[] holdMove = new string[0]; // #911 발동 뒤 공격 이펙트(이 PC 의 원본 스킬 투사체)가 끝날 때까지 이동 입력 무시 · 구르기는 허용(= 스킵) public string[] holdMove = new string[0]; // #911 발동 뒤 공격 이펙트(이 PC 의 원본 스킬 투사체)가 끝날 때까지 이동 입력 무시 · 구르기는 허용(= 스킵)
public string[] endWithMotion = new string[0]; // #914 스킬 동작이 입력 없이 끝나면(대기로) 그 스킬 이펙트도 바로 끈다 — 남은 이펙트 때문에 서 있지 않게 public string[] endWithMotion = new string[0]; // #914 스킬 동작이 입력 없이 끝나면(대기로) 그 스킬 이펙트도 바로 끈다 — 남은 이펙트 때문에 서 있지 않게
public string[] lockUntilFire = new string[0]; // #942 시전 뒤 첫 발동(SkillFired)까지 이동 · 구르기 입력 무시(holdMaxSeconds 상한) — 발동 뒤엔 자유(투사체는 noSkip 으로 유지) · PD 「플레이어가 이동해도 발동 된 스킬이 끊겨선 안돼」(실측: 이동이 발동 이벤트 전에 시전 동작을 끊어 투사체가 아예 안 나왔다)
public int holdUntilFire = 1; // 1 = 이동 키를 누른 채 쓴 스킬은 첫 발동까지 그 입력을 무시 public int holdUntilFire = 1; // 1 = 이동 키를 누른 채 쓴 스킬은 첫 발동까지 그 입력을 무시
public float holdMaxSeconds = 1.5f; // ① 의 상한(발동 이벤트가 없는 스킬 대비) public float holdMaxSeconds = 1.5f; // ① 의 상한(발동 이벤트가 없는 스킬 대비)
public string note = ""; public string note = "";
@ -48,7 +49,7 @@ namespace WL.Combat.Weapon
static PCActor s_pc; static PCActor s_pc;
static string s_effect = ""; static string s_effect = "";
static float s_castAt; static float s_castAt;
static bool s_active, s_fired, s_heldAtCast, s_lock, s_hold; static bool s_active, s_fired, s_heldAtCast, s_lock, s_hold, s_lockFire; // s_lockFire = #942 발동까지 잠금
static int s_pendingBrake = -1; // -1 없음 · 0 = Set_AutoBraking(false) · 1 = Set_AutoBraking(true) static int s_pendingBrake = -1; // -1 없음 · 0 = Set_AutoBraking(false) · 1 = Set_AutoBraking(true)
static GameObject s_runner; static GameObject s_runner;
static FieldInfo s_fDicSkill; static FieldInfo s_fDicSkill;
@ -102,8 +103,9 @@ namespace WL.Combat.Weapon
s_heldAtCast = pc.JoystickStatus == 1; s_heldAtCast = pc.JoystickStatus == 1;
s_lock = Has(c.lockMove, s_effect); s_lock = Has(c.lockMove, s_effect);
s_hold = Has(c.holdMove, s_effect); s_hold = Has(c.holdMove, s_effect);
s_lockFire = Has(c.lockUntilFire, s_effect);
Casts++; Casts++;
LastLog = "시작 " + s_effect + (s_lock ? " · 이동 잠금" : "") + (s_heldAtCast ? " · 이동 키 누른 채" : ""); LastLog = "시작 " + s_effect + (s_lock ? " · 이동 잠금" : "") + (s_lockFire ? " · 발동까지 잠금" : "") + (s_heldAtCast ? " · 이동 키 누른 채" : "");
} }
static void OnFired(in SkillFiredEvent e) { if (s_active && ReferenceEquals(e.actor, s_pc)) s_fired = true; } static void OnFired(in SkillFiredEvent e) { if (s_active && ReferenceEquals(e.actor, s_pc)) s_fired = true; }
@ -141,6 +143,7 @@ namespace WL.Combat.Weapon
if (!s_active || !ReferenceEquals(pc, s_pc) || pc.Get_CurAnim() != eAnim.Skill) return false; if (!s_active || !ReferenceEquals(pc, s_pc) || pc.Get_CurAnim() != eAnim.Skill) return false;
if (s_lock) return true; if (s_lock) return true;
var c = Cfg; var c = Cfg;
if (s_lockFire && !s_fired && Time.time - s_castAt < c.holdMaxSeconds) return true; // #942 발동 전엔 새 이동 입력도 무시
return c.holdUntilFire != 0 && s_heldAtCast && !s_fired && Time.time - s_castAt < c.holdMaxSeconds; return c.holdUntilFire != 0 && s_heldAtCast && !s_fired && Time.time - s_castAt < c.holdMaxSeconds;
} }
@ -155,7 +158,8 @@ namespace WL.Combat.Weapon
/// <summary>WLDoubleTapDodge 훅 — 이동 잠금 스킬(점프) 중엔 구르기도 무시.</summary> /// <summary>WLDoubleTapDodge 훅 — 이동 잠금 스킬(점프) 중엔 구르기도 무시.</summary>
public static bool BlockDodge(PCActor pc) public static bool BlockDodge(PCActor pc)
{ {
if (pc == null || !s_active || !s_lock || !ReferenceEquals(pc, s_pc) || pc.Get_CurAnim() != eAnim.Skill) return false; if (pc == null || !s_active || !ReferenceEquals(pc, s_pc) || pc.Get_CurAnim() != eAnim.Skill) return false;
if (!s_lock && !(s_lockFire && !s_fired && Time.time - s_castAt < Cfg.holdMaxSeconds)) return false; // #942 발동 전 구르기도 무시
DodgeBlocked++; return true; DodgeBlocked++; return true;
} }

View File

@ -303,7 +303,7 @@ namespace WL.Combat.Weapon
var dir = !DSUtil.CheckNull(e.attacker) ? at - e.attacker.Get_Center_position() : e.victim.transform.forward; var dir = !DSUtil.CheckNull(e.attacker) ? at - e.attacker.Get_Center_position() : e.victim.transform.forward;
dir.y = 0f; dir.y = 0f;
var rot = dir.sqrMagnitude > 1e-4f ? Quaternion.LookRotation(dir) : Quaternion.identity; var rot = dir.sqrMagnitude > 1e-4f ? Quaternion.LookRotation(dir) : Quaternion.identity;
if (Spawn(r.hit, at, rot)) Hits++; if (Spawn(r.hit, at + WL.Combat.Impact.WLImpactFx.TowardCamera(e.victim), rot)) Hits++; // #941 몸 앞(카메라 쪽)
if ((r.launchHeight > 0f || r.stunSeconds > 0f) && WLLaunch.Apply(e.victim, r.launchHeight, r.launchSeconds, r.stunSeconds)) Launched++; // #902 if ((r.launchHeight > 0f || r.stunSeconds > 0f) && WLLaunch.Apply(e.victim, r.launchHeight, r.launchSeconds, r.stunSeconds)) Launched++; // #902
} }
catch (System.Exception ex) { Errors++; LastLog = "hit 실패 — " + ex.Message; } catch (System.Exception ex) { Errors++; LastLog = "hit 실패 — " + ex.Message; }