WL #959 PD 지시 4건(비수) — ② 주력 이펙트 폭 2배(범위 × 3.2) + 난사 끝(2.0 s)까지 0.24 s 마다 되풀이(매번 그때 PC 위치·대상 방향 = 전진 반영 · fxRepeatInterval) ③ 난사 중 0.07 s 마다 지금 자세 잔상(WLGhostStrike.LiveGhost · teal) + 잔상 풀 정리 러너 보장(각성 전엔 러너가 없어 잔상 6개가 영영 busy = 원장 47 원인) + 난사 상태 매 프레임 강제(Animator.Play · 틱 0.24 s · 배속 1.6) ④ 각성 등장 = 공중 낙하 대신 옆에서 튀어나와 정지(entrySide 1.2 m · 0.2 s 감속 · 수직 0 · 시전 잔상도 옆으로 jumpSide) ⑤ 2타 = 원본 Stander@DualBlades_Attack2 16~36 f(양날 넓게 베기 · 배속 1.1 · 3타 exit 0.5 → 0.8) · 4타 = 원본 Stander@DualBlades_Attack3(양손 낮게 가르기 · 배속 1.2) — 매핑 재굽기(백업 Combat5_20261021_1345 · 변경 3 = 2타·4타 FBX 메타 + pcanim_assassin)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
682c3f67d9
commit
a6c2bf0809
|
|
@ -1,7 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ee7d9d41f5a48514db8ac2144d80154d
|
||||
ModelImporter:
|
||||
serializedVersion: 22200
|
||||
serializedVersion: 24300
|
||||
internalIDToNameTable:
|
||||
- first:
|
||||
1: 100000
|
||||
|
|
@ -339,6 +339,7 @@ ModelImporter:
|
|||
materialName: 0
|
||||
materialSearch: 1
|
||||
materialLocation: 1
|
||||
searchTexturesGlobally: 1
|
||||
animations:
|
||||
legacyGenerateAnimations: 4
|
||||
bakeSimulation: 0
|
||||
|
|
@ -346,8 +347,6 @@ ModelImporter:
|
|||
optimizeGameObjects: 0
|
||||
removeConstantScaleCurves: 0
|
||||
motionNodeName:
|
||||
rigImportErrors:
|
||||
rigImportWarnings:
|
||||
animationImportErrors:
|
||||
animationImportWarnings:
|
||||
animationRetargetingWarnings:
|
||||
|
|
@ -419,6 +418,63 @@ ModelImporter:
|
|||
maskType: 3
|
||||
maskSource: {instanceID: 0}
|
||||
additiveReferencePoseFrame: 0
|
||||
- serializedVersion: 16
|
||||
name: Attack2_S_10202
|
||||
takeName: Take 001
|
||||
internalID: 1827226128182048838
|
||||
firstFrame: 16
|
||||
lastFrame: 36
|
||||
wrapMode: 0
|
||||
orientationOffsetY: 0
|
||||
level: 0
|
||||
cycleOffset: 0
|
||||
loop: 0
|
||||
hasAdditiveReferencePose: 0
|
||||
loopTime: 1
|
||||
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.25
|
||||
functionName: ShowEffect
|
||||
data: Effect_Slash_10202_2
|
||||
objectReferenceParameter: {instanceID: 0}
|
||||
floatParameter: 0
|
||||
intParameter: 1
|
||||
messageOptions: 0
|
||||
- time: 0.32
|
||||
functionName: Move
|
||||
data:
|
||||
objectReferenceParameter: {instanceID: 0}
|
||||
floatParameter: 0.1
|
||||
intParameter: 0
|
||||
messageOptions: 0
|
||||
- time: 0.42
|
||||
functionName: Projectile
|
||||
data: Projectile_10202
|
||||
objectReferenceParameter: {instanceID: 0}
|
||||
floatParameter: 0
|
||||
intParameter: 0
|
||||
messageOptions: 0
|
||||
transformMask: []
|
||||
maskType: 3
|
||||
maskSource: {instanceID: 0}
|
||||
additiveReferencePoseFrame: 0
|
||||
isReadable: 0
|
||||
meshes:
|
||||
lODScreenPercentages: []
|
||||
|
|
@ -445,6 +501,9 @@ ModelImporter:
|
|||
maxBonesPerVertex: 4
|
||||
minBoneWeight: 0.001
|
||||
optimizeBones: 1
|
||||
generateMeshLods: 0
|
||||
meshLodGenerationFlags: 0
|
||||
maximumMeshLod: -1
|
||||
meshOptimizationFlags: -1
|
||||
indexFormat: 0
|
||||
secondaryUVAngleDistortion: 8
|
||||
|
|
|
|||
|
|
@ -475,6 +475,63 @@ ModelImporter:
|
|||
maskType: 3
|
||||
maskSource: {instanceID: 0}
|
||||
additiveReferencePoseFrame: 0
|
||||
- serializedVersion: 16
|
||||
name: Attack4_S_10202
|
||||
takeName: Take 001
|
||||
internalID: -998496362607748373
|
||||
firstFrame: 0
|
||||
lastFrame: 35
|
||||
wrapMode: 0
|
||||
orientationOffsetY: 0
|
||||
level: 0
|
||||
cycleOffset: 0
|
||||
loop: 0
|
||||
hasAdditiveReferencePose: 0
|
||||
loopTime: 1
|
||||
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.1
|
||||
functionName: ShowEffect
|
||||
data: Effect_Slash_10202_4
|
||||
objectReferenceParameter: {instanceID: 0}
|
||||
floatParameter: 0
|
||||
intParameter: 1
|
||||
messageOptions: 0
|
||||
- time: 0.14
|
||||
functionName: Move
|
||||
data:
|
||||
objectReferenceParameter: {instanceID: 0}
|
||||
floatParameter: 0.2
|
||||
intParameter: 0
|
||||
messageOptions: 0
|
||||
- time: 0.25
|
||||
functionName: Projectile
|
||||
data: Projectile_10202
|
||||
objectReferenceParameter: {instanceID: 0}
|
||||
floatParameter: 0
|
||||
intParameter: 0
|
||||
messageOptions: 0
|
||||
transformMask: []
|
||||
maskType: 3
|
||||
maskSource: {instanceID: 0}
|
||||
additiveReferencePoseFrame: 0
|
||||
isReadable: 0
|
||||
meshes:
|
||||
lODScreenPercentages: []
|
||||
|
|
|
|||
|
|
@ -503,7 +503,7 @@ AnimatorStateTransition:
|
|||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.15
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.5
|
||||
m_ExitTime: 0.8
|
||||
m_HasExitTime: 1
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
|
|
@ -517,7 +517,7 @@ AnimatorState:
|
|||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: DualBlades_DodgeAttack
|
||||
m_Speed: 1.5
|
||||
m_Speed: 1.2
|
||||
m_CycleOffset: 0
|
||||
m_Transitions:
|
||||
- {fileID: -524724009125662056}
|
||||
|
|
@ -530,7 +530,7 @@ AnimatorState:
|
|||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 1827226128182048838, guid: 248e0d6885e2b4f44b0c03aeaa65dd8a,
|
||||
m_Motion: {fileID: -998496362607748373, guid: afbbfbe54e2f6d242983469698b3aac3,
|
||||
type: 3}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
|
|
@ -567,7 +567,7 @@ AnimatorState:
|
|||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: DualBlades_Attack2
|
||||
m_Speed: 1
|
||||
m_Speed: 1.1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions:
|
||||
- {fileID: -1743382946548511829}
|
||||
|
|
@ -580,7 +580,7 @@ AnimatorState:
|
|||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 1827226128182048838, guid: 08478a94dc135e34cb5ff79c98bb6f75,
|
||||
m_Motion: {fileID: 1827226128182048838, guid: ee7d9d41f5a48514db8ac2144d80154d,
|
||||
type: 3}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
|
|
|
|||
|
|
@ -532,48 +532,48 @@ MonoBehaviour:
|
|||
intParameter: 0
|
||||
floatParameter: 0
|
||||
- stateName: DualBlades_Attack2
|
||||
sourceFbxPath: Assets/ExplosiveLLC/Ninja Warrior Mecanim Animation Pack/Animations/Ninja@Attack2_S.FBX
|
||||
sourceFbxPath: Assets/Res_Addr/Animations/Animation/Assassin/Stander@DualBlades_Attack2.FBX
|
||||
subClipName: Attack2_S_{CID}
|
||||
sourceClipName:
|
||||
sourceClipName:
|
||||
incomingExitTime: 0.587
|
||||
createIfMissing: 0
|
||||
firstFrame: -1
|
||||
lastFrame: -1
|
||||
speed: 0
|
||||
firstFrame: 16
|
||||
lastFrame: 36
|
||||
speed: 1.1
|
||||
events:
|
||||
- functionName: LookAtTarget
|
||||
time: 0
|
||||
useNormalized: 1
|
||||
normalizedTime: 0
|
||||
stringParameter:
|
||||
stringParameter:
|
||||
intParameter: 0
|
||||
floatParameter: 0
|
||||
- functionName: ShowEffect
|
||||
time: 0
|
||||
useNormalized: 1
|
||||
normalizedTime: 0.287
|
||||
normalizedTime: 0.25
|
||||
stringParameter: Effect_Slash_{CID}_2
|
||||
intParameter: 1
|
||||
floatParameter: 0
|
||||
- functionName: Move
|
||||
time: 0
|
||||
useNormalized: 1
|
||||
normalizedTime: 0.347
|
||||
stringParameter:
|
||||
normalizedTime: 0.32
|
||||
stringParameter:
|
||||
intParameter: 0
|
||||
floatParameter: 0.1
|
||||
- functionName: Projectile
|
||||
time: 0
|
||||
useNormalized: 1
|
||||
normalizedTime: 0.407
|
||||
normalizedTime: 0.42
|
||||
stringParameter: Projectile_{CID}
|
||||
intParameter: 0
|
||||
floatParameter: 0
|
||||
- stateName: DualBlades_Attack3
|
||||
sourceFbxPath: Assets/Res_Addr/Animations/Animation/Paladin/skill_spinslash.FBX
|
||||
subClipName: Attack3_S_{CID}
|
||||
sourceClipName:
|
||||
incomingExitTime: 0.5
|
||||
sourceClipName:
|
||||
incomingExitTime: 0.8
|
||||
createIfMissing: 0
|
||||
firstFrame: -1
|
||||
lastFrame: -1
|
||||
|
|
@ -608,40 +608,40 @@ MonoBehaviour:
|
|||
intParameter: 0
|
||||
floatParameter: 0
|
||||
- stateName: DualBlades_DodgeAttack
|
||||
sourceFbxPath: Assets/Res_Addr/Animations/Animation/Assassin/Stander@DualBlades_DodgeAttack_Attack4.FBX
|
||||
sourceFbxPath: Assets/Res_Addr/Animations/Animation/Assassin/Stander@DualBlades_Attack3.FBX
|
||||
subClipName: Attack4_S_{CID}
|
||||
sourceClipName:
|
||||
sourceClipName:
|
||||
incomingExitTime: 0.55
|
||||
createIfMissing: 0
|
||||
firstFrame: -1
|
||||
lastFrame: -1
|
||||
speed: 1.5
|
||||
lastFrame: 26
|
||||
speed: 1.2
|
||||
events:
|
||||
- functionName: LookAtTarget
|
||||
time: 0
|
||||
useNormalized: 1
|
||||
normalizedTime: 0
|
||||
stringParameter:
|
||||
stringParameter:
|
||||
intParameter: 0
|
||||
floatParameter: 0
|
||||
- functionName: ShowEffect
|
||||
time: 0
|
||||
useNormalized: 1
|
||||
normalizedTime: 0.155
|
||||
normalizedTime: 0.1
|
||||
stringParameter: Effect_Slash_{CID}_4
|
||||
intParameter: 1
|
||||
floatParameter: 0
|
||||
- functionName: Move
|
||||
time: 0
|
||||
useNormalized: 1
|
||||
normalizedTime: 0.126
|
||||
stringParameter:
|
||||
normalizedTime: 0.14
|
||||
stringParameter:
|
||||
intParameter: 0
|
||||
floatParameter: 0.3
|
||||
floatParameter: 0.2
|
||||
- functionName: Projectile
|
||||
time: 0
|
||||
useNormalized: 1
|
||||
normalizedTime: 0.186
|
||||
normalizedTime: 0.25
|
||||
stringParameter: Projectile_{CID}
|
||||
intParameter: 0
|
||||
floatParameter: 0
|
||||
|
|
|
|||
|
|
@ -38,22 +38,25 @@
|
|||
"hitMinInterval": 0.08,
|
||||
"castSeconds": 2.0,
|
||||
"jumpGhosts": 4,
|
||||
"jumpHeight": 1.0,
|
||||
"jumpForward": 0.4,
|
||||
"jumpHeight": 0.15,
|
||||
"jumpForward": 0.1,
|
||||
"jumpSide": 1.0,
|
||||
"jumpSeconds": 0.25,
|
||||
"landState": "ghost_land",
|
||||
"landHeight": 0.2,
|
||||
"landFallSeconds": 0.06,
|
||||
"slideDistance": 0.45,
|
||||
"slideSeconds": 0.3,
|
||||
"landTotalSeconds": 0.45,
|
||||
"landHeight": 0.0,
|
||||
"landFallSeconds": 0.2,
|
||||
"entrySide": 1.2,
|
||||
"slideDistance": 0.1,
|
||||
"slideSeconds": 0.15,
|
||||
"landTotalSeconds": 0.5,
|
||||
"_note_959": "#959 PD 「각성 시전 후 공중에 떨어지지 말고 옆에서 튀어나와 정지하는 것처럼」 — 시전 잔상은 위(1.0) 대신 오른쪽 1.0 m 로 빠지고(jumpSide · 위 0.15 · 앞 0.1) · 다시 보이는 순간 최종 자리의 오른쪽 1.2 m(entrySide)에서 0.2 s 동안 감속하며 미끄러져 들어와(그동안 반투명 착지 자세 캐릭터 · 수직 이동 0 = landHeight 0) 정지(앞 미끄러짐 0.45 → 0.1 m) → 모델 + 잔상 3 + 먼지",
|
||||
"landAtSeconds": 3.4,
|
||||
"airClip": "Skill8_Air",
|
||||
"landGhosts": 0,
|
||||
"dustPrefab": "WL/Fx/WL_Fx_LandDust",
|
||||
"dustScale": 0.15,
|
||||
"keepCamera": 1,
|
||||
"fallForward": 0.08,
|
||||
"fallForward": 0.0,
|
||||
"landPose": "ghost_land@0.25",
|
||||
"landHideModel": 1,
|
||||
"landGhostColor": [ 0.55, 0.95, 0.85, 0.45 ],
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@
|
|||
"fxLife": 2.5,
|
||||
"fxAimTarget": 1,
|
||||
"fxRangeFrac": 0.5,
|
||||
"fxRangeWidth": 1.6,
|
||||
"fxRangeWidth": 3.2,
|
||||
"fxPrefabWidth": 5.26,
|
||||
"hideOriginalFx": 1,
|
||||
"slashStates": [ "attack", "DualBlades_Attack2" ],
|
||||
"slashInterval": 0.28,
|
||||
"slashSpeed": 1.8,
|
||||
"slashInterval": 0.24,
|
||||
"slashSpeed": 1.6,
|
||||
"slashSeconds": 2.0,
|
||||
"fxMain": 1,
|
||||
"fxPerSlash": 0,
|
||||
|
|
@ -27,6 +27,15 @@
|
|||
"fxSlashFraction": 0.6,
|
||||
"fxSlashYOffset": 0.0,
|
||||
"fxSlashFollow": 1,
|
||||
"fxRepeatInterval": 0.24,
|
||||
"fxRepeatSeconds": 0,
|
||||
"slashHardRestart": 1,
|
||||
"slashGhosts": 1,
|
||||
"slashGhostInterval": 0.07,
|
||||
"slashGhostHold": 0.03,
|
||||
"slashGhostFade": 0.14,
|
||||
"slashGhostColor": [ 0.55, 0.95, 0.85, 0.4 ],
|
||||
"_note_959": "#959 PD 「이펙트 크기 2배 더 · 플레이어 동작에 맞게 재생시간을 늘려 모션만 보이지 않게(캐릭터 전진값 고려해 위치도) · 공격 모션에 잔상(빠르게 공격하는 느낌)」 — 폭 = 공격 범위 × 1.6 → 3.2(배율 0.304 → 0.608) · 정면 사본을 0.5 s 부터 0.28 s 마다 난사가 끝나는 2.0 s 까지 되풀이(매번 그때 PC 위치·대상 방향 = 전진 반영 · 초승달 수명 0.3 s 라 1회로는 0.6 s 만 보였음) · 난사 상태는 Animator.Play 로 강제(Play 실측: 배속 1.8 에선 2타→3타 exit 전이가 틱보다 먼저 와 3타 회전·4타 돌진 0.4 m 가 섞였음 = PD 가 본 「전진」) · 난사 2.0 s 동안 0.07 s 마다 지금 자세 잔상(teal · 유지 0.03 · 소멸 0.14) · 틱 0.28 → 0.24 s · 배속 1.8 → 1.6(2타가 #959 원본 DualBlades_Attack2 16~36 f 0.61 s · exit 0.8 이 되어 1.8 배속이면 exit 0.27 s 가 틱보다 먼저 와 3타 회전이 샘 → 1.6 배속 exit 0.31 s > 0.24 s)",
|
||||
"_note_958": "#958 PD 「주력 이펙트가 양손에 작게 하나씩 → 무기 방향 전방에 다른 무기 궤적처럼 하나만 · 전방의 적이 있는 방향에 1개 · 플레이어가 타겟하려는 대상의 무기 공격 범위가 중점」 — 양손 난사 사본 끔(fxPerSlash 0) · 정면 사본 1개(fxMain 1 · 0.5 s) · 방향 = 대상 쪽(fxAimTarget 1) · 중심 = PC 몸 중심 + 공격 범위(WL_AttackRange) × 0.5(대상이 더 가까우면 대상 자리) · 폭 = 공격 범위 × 1.6(배율 = 폭 ÷ 프리팹 단위 초승달 폭 5.26 m · #956 편집 모드 실측 · fxScale 무시) · 높이 0 = 몸 중심",
|
||||
"_note_956": "#956 PD 「주력 이펙트가 너무 먼 위치(정면 1.2 m)에 재생 → 플레이어가 휘두르는 무기 위치에 맞게(휘두르는 느낌)」 — 정면 1회 사본은 끔(fxMain 0 · 켜면 fxForward 0.45 · 손 높이 0.3) · 대신 난사 루프 0.28 s 마다 같은 프리팹의 작은 사본(0.35 · 편집 모드 실측 프리팹 단위 초승달 AABB 5.26 m → 약 1.8 m · 초승달 수명 0.30 s · 2번째 초승달 시차)을 양손 소켓(w_bladedancer_l/r) 손잡이→날 끝 0.6 자리에 번갈아 띄우고 0.6 s 동안 날 방향(수평)·위치를 따라가게",
|
||||
"_note_955": "#955 PD 「주력 쓸 때 캐릭터가 공중에 뜬 채 행동 → 제자리에서 단검 난사」 — 원본 상태 사슬 DualBlades_Jump_Start → JumpAttack1·2·3(공중 자세)을 대신해 2.0 s 동안 1·2타 상태를 0.28 s 마다 번갈아(배속 1.8 · 애니메이션 이벤트 끔 = 추가 타격·궤적 없음 · 이동은 원본대로 금지) · 2.1 s 뒤 원본 순간이동·Play_Idle 그대로",
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@ namespace WL.Combat.Weapon
|
|||
public int landGhosts = 2; // 떨어지는 동안 잔상 수
|
||||
public string dustPrefab = ""; public float dustScale = 1f;
|
||||
public int keepCamera = 0; // #953 1 = 원본 Skill_DarkAmbush 의 탑뷰 전환(RealCamera.Set_TopView)을 건너뛴다 — 카메라 각도 유지
|
||||
// #959 PD 「각성 시전 후 공중에서 떨어지지 말고 옆에서 튀어나와 정지하는 것처럼」 — entrySide ≠ 0: 다시 보이는 순간 최종 자리에서 PC 오른쪽(+ · 왼쪽 −)으로 entrySide m 떨어진 곳에 워프한 뒤
|
||||
// landFallSeconds 동안 감속하며 최종 자리로 미끄러져 들어온다(그동안 landPose 반투명 캐릭터 · landHeight 0 이면 수직 이동 없음) · jumpSide: 시전 순간 잔상이 옆으로도 빠진다(m · 같은 부호 = 같은 쪽)
|
||||
public float entrySide = 0f, jumpSide = 0f;
|
||||
public float fallForward = 0f; // #953 낙하하는 동안 앞으로 가는 거리(m · 대각선으로 미끄러지며 낙하) · 착지 뒤 미끄러짐(slideDistance)은 별도
|
||||
// #954 PD 「0.5 m 정도 짧은 높이에서 반투명 상태로 갑자기 이펙트와 함께 튀어나오며 랜딩 · 랜딩 시간 짧게 · 랜딩 중 공격 불가」
|
||||
public string landPose = ""; // 착지 자세("상태@정규화 시각") — 떨어지는 동안 반투명 캐릭터(잔상 재질)로 그리고 실제 모델은 숨긴다(landHideModel)
|
||||
|
|
@ -102,7 +105,7 @@ namespace WL.Combat.Weapon
|
|||
public Vector3 lastPos; public bool hasLast, posesReady; public string sig = "";
|
||||
// #951 도약 잔상 · 착지
|
||||
public int jumpDone; public float jumpNext = -1f; public bool wasHidden, sawAir, landing, landed, dustDone, hideDuringFall; public float landT0, modelBaseY, modelY = float.NaN; public int landGhostsDone, hideUntilFrame;
|
||||
public Vector3 landPos, landFwd; public Transform model; public UnityEngine.AI.NavMeshAgent agent; public object stopWas;
|
||||
public Vector3 landPos, landFwd, landRight; public float entryMoved; public Transform model; public UnityEngine.AI.NavMeshAgent agent; public object stopWas; // #959 landRight · entryMoved = 옆에서 들어오기
|
||||
public readonly List<SkinnedMeshRenderer> smrs = new List<SkinnedMeshRenderer>();
|
||||
public readonly List<MeshFilter> weapons = new List<MeshFilter>();
|
||||
public readonly List<string> smrPaths = new List<string>();
|
||||
|
|
@ -195,15 +198,18 @@ namespace WL.Combat.Weapon
|
|||
s_cast = cast;
|
||||
s_hitQ.Clear(); s_bearing.Clear(); s_lastHitAt.Clear();
|
||||
Casts++;
|
||||
if (s_runner == null)
|
||||
{
|
||||
var go = new GameObject("[WL943] GhostStrike"); go.hideFlags = HideFlags.HideInHierarchy; Object.DontDestroyOnLoad(go);
|
||||
s_runner = go.AddComponent<WLGhostStrikeRunner>();
|
||||
}
|
||||
EnsureRunner();
|
||||
LastLog = "시전 " + r.skill + " · fx " + (cast.fx != null ? cast.fx.Count : 0) + " · 부품 " + cast.smrs.Count + "+" + cast.weapons.Count;
|
||||
}
|
||||
catch (System.Exception ex) { Errors++; LastLog = "OnSet 실패 — " + ex.Message; }
|
||||
}
|
||||
/// <summary>풀 정리(Tick · 유지·소멸 뒤 반환)를 돌리는 러너 — #959 실측: 각성(OnSet) 전에 4타 순간이동 잔상·난사 잔상만 쓰면 러너가 없어 잔상 6개가 영영 busy(원장 47 「첫 6개 뒤 0개」의 원인) → 잔상을 띄우는 모든 입구에서 보장.</summary>
|
||||
static void EnsureRunner()
|
||||
{
|
||||
if (s_runner != null) return;
|
||||
var go = new GameObject("[WL943] GhostStrike"); go.hideFlags = HideFlags.HideInHierarchy; Object.DontDestroyOnLoad(go);
|
||||
s_runner = go.AddComponent<WLGhostStrikeRunner>();
|
||||
}
|
||||
|
||||
/// <summary>타격 확정(물리 콜백 안) — 이번 시전 스킬이 시전자로 맞힌 것만 요청으로 쌓는다(생성은 러너 Update).</summary>
|
||||
static void OnHit(in HitConfirmedEvent e)
|
||||
|
|
@ -237,7 +243,8 @@ namespace WL.Combat.Weapon
|
|||
if (rl.jumpGhosts > 0 && cast.jumpDone < rl.jumpGhosts && now >= cast.jumpNext && cast.jumpNext >= 0f)
|
||||
{
|
||||
float k = (cast.jumpDone + 1f) / rl.jumpGhosts; var sp = cast.shooter.Get_position(); var fw = cast.shooter.transform.forward; fw.y = 0f; if (fw.sqrMagnitude < 1e-4f) fw = Vector3.forward; fw.Normalize();
|
||||
if (Spawn(c, cast, sp + Vector3.up * (rl.jumpHeight * k) + fw * (rl.jumpForward * k), fw)) JumpSpawned++;
|
||||
var rt = Vector3.Cross(Vector3.up, fw); // #959 PC 오른쪽
|
||||
if (Spawn(c, cast, sp + Vector3.up * (rl.jumpHeight * k) + fw * (rl.jumpForward * k) + rt * (rl.jumpSide * k), fw)) JumpSpawned++;
|
||||
cast.jumpDone++; cast.jumpNext = now + rl.jumpSeconds / rl.jumpGhosts;
|
||||
}
|
||||
// #951 ② 착지 연출 — 시작 판정은 LateTick(#952-② 다시 보이는 그 프레임에 즉시 · 서 있는 자세가 한 프레임도 안 보이게) · 진행은 여기
|
||||
|
|
@ -277,6 +284,7 @@ namespace WL.Combat.Weapon
|
|||
try
|
||||
{
|
||||
var c = Cfg; if (c.enabled == 0 || pc == null || n <= 0) return 0;
|
||||
EnsureRunner(); // #959 풀 정리 보장(원장 47)
|
||||
var cast = new Cast { shooter = pc, rule = new WLGhostStrikeRule(), t0 = Time.time, groundY = pc.Get_position().y, poseCursor = 0 };
|
||||
Capture(cast, c);
|
||||
var pose = BakePose(pc.transform, cast.smrs, cast.weapons); if (pose == null) return 0;
|
||||
|
|
@ -290,6 +298,27 @@ namespace WL.Combat.Weapon
|
|||
catch (System.Exception ex) { Errors++; LastLog = "잔상 실패 — " + ex.Message; return 0; }
|
||||
}
|
||||
public static int BlinkGhostCount;
|
||||
/// <summary>#959 지금 자세 잔상 1개(살아 있는 메시를 굽는다) — 비수 주력 난사 중 「빠르게 공격하는」 잔상 · 색(RGBA · null = 기본) · 유지 · 소멸(−1 = 기본).</summary>
|
||||
public static int LiveGhosts;
|
||||
public static bool LiveGhost(Actor pc, float[] color, float hold, float fade)
|
||||
{
|
||||
try
|
||||
{
|
||||
var c = Cfg; if (c.enabled == 0 || pc == null) return false;
|
||||
EnsureRunner(); // #959 풀 정리 보장
|
||||
var cast = new Cast { shooter = pc, rule = new WLGhostStrikeRule(), t0 = Time.time, groundY = pc.Get_position().y };
|
||||
Capture(cast, c);
|
||||
var pose = BakePose(pc.transform, cast.smrs, cast.weapons); if (pose == null) return false;
|
||||
var face = pc.transform.forward; face.y = 0f; if (face.sqrMagnitude < 1e-4f) face = Vector3.forward;
|
||||
bool ok = Spawn(c, cast, pc.Get_position(), face, pose, color, hold, fade);
|
||||
float life = (hold >= 0f ? hold : c.hold) + (fade >= 0f ? fade : c.fade) + 0.5f;
|
||||
if (pose.body != null) Object.Destroy(pose.body, life);
|
||||
foreach (var m in pose.extraMesh) if (m != null) Object.Destroy(m, life);
|
||||
if (ok) LiveGhosts++;
|
||||
return ok;
|
||||
}
|
||||
catch (System.Exception ex) { Errors++; LastLog = "잔상 실패 — " + ex.Message; return false; }
|
||||
}
|
||||
/// <summary>#954 「랜딩 중 공격할 수 없어야」 — 이 시전자가 착지 연출 중인가(자동 전투는 IsStop 으로 이미 멈춤 · 수동 공격 키는 이걸로 막는다).</summary>
|
||||
public static bool IsLanding(Actor a) { var cast = s_cast; return cast != null && cast.landing && a != null && ReferenceEquals(cast.shooter, a); }
|
||||
static System.Reflection.PropertyInfo s_isStop;
|
||||
|
|
@ -371,6 +400,12 @@ namespace WL.Combat.Weapon
|
|||
cast.model = cast.shooter.m_animation != null ? cast.shooter.m_animation.transform : null; cast.modelBaseY = cast.model != null ? cast.model.localPosition.y : 0f;
|
||||
cast.agent = cast.shooter.GetComponent<UnityEngine.AI.NavMeshAgent>();
|
||||
if (pc != null) { try { if (s_isStop == null) s_isStop = typeof(Actor).GetProperty("IsStop", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public); cast.stopWas = s_isStop != null ? s_isStop.GetValue(pc, null) : null; } catch (System.Exception) { } SetStop(pc, true); pc.WL_StopNavMove(); }
|
||||
cast.landRight = Vector3.Cross(Vector3.up, cast.landFwd).normalized; cast.entryMoved = 0f;
|
||||
if (r.entrySide != 0f) // #959 옆에서 들어오기 — 최종 자리(landPos)에서 옆으로 entrySide 만큼 떨어진 곳으로 워프 → UpdateLanding 이 감속하며 되돌아온다
|
||||
{
|
||||
var from = cast.landPos - cast.landRight * r.entrySide;
|
||||
try { if (pc != null) pc.Set_Warp(from); else cast.shooter.transform.position = from; } catch (System.Exception) { cast.shooter.transform.position = from; }
|
||||
}
|
||||
try { if (cast.shooter.m_animation != null) { cast.shooter.m_animation.speed = 1f; cast.shooter.m_animation.CrossFade(r.landState, 0f, 0, 0f); } } catch (System.Exception ex) { LandLog = "착지 클립 실패 — " + ex.Message; } // #952-② 섞임 0 = 서 있는 자세에서 번지지 않게
|
||||
if (cast.model != null) cast.model.localPosition = new Vector3(cast.model.localPosition.x, cast.modelBaseY + r.landHeight, cast.model.localPosition.z);
|
||||
Landings++; LandLog = "착지 시작 t+" + (now - cast.t0).ToString("F2") + " @" + cast.landPos.ToString("F2") + (cast.agent != null ? " agent" : " agent 없음");
|
||||
|
|
@ -391,12 +426,25 @@ namespace WL.Combat.Weapon
|
|||
float step = r.fallForward * Time.deltaTime / fall;
|
||||
if (cast.agent != null && cast.agent.enabled && cast.agent.isOnNavMesh) cast.agent.Move(cast.landFwd * step); else cast.shooter.transform.position += cast.landFwd * step;
|
||||
}
|
||||
if (r.entrySide != 0f) // #959 옆에서 튀어나오기 — 속도 2D/T → 0 감속(총 이동 = |entrySide|) · 방향 = 워프한 쪽의 반대
|
||||
{
|
||||
float total = Mathf.Abs(r.entrySide), kk = Mathf.Clamp01(t / fall);
|
||||
float step = Mathf.Min(total * (2f / fall) * (1f - kk) * Time.deltaTime, Mathf.Max(0f, total - cast.entryMoved));
|
||||
var dir = cast.landRight * Mathf.Sign(r.entrySide); cast.entryMoved += step;
|
||||
if (cast.agent != null && cast.agent.enabled && cast.agent.isOnNavMesh) cast.agent.Move(dir * step); else cast.shooter.transform.position += dir * step;
|
||||
}
|
||||
return;
|
||||
}
|
||||
cast.modelY = cast.modelBaseY;
|
||||
if (!cast.dustDone)
|
||||
{
|
||||
cast.dustDone = true;
|
||||
if (r.entrySide != 0f) // #959 들어오기 끝 = 최종 자리에 정확히
|
||||
{
|
||||
var d = cast.landPos - cast.shooter.Get_position(); d.y = 0f;
|
||||
if (cast.agent != null && cast.agent.enabled && cast.agent.isOnNavMesh) cast.agent.Move(d); else cast.shooter.transform.position += d;
|
||||
LandLog += " · 옆 진입 " + cast.entryMoved.ToString("F2") + " m";
|
||||
}
|
||||
if (cast.hideDuringFall) { cast.hideDuringFall = false; SetVisible(cast, true); } // #954 착지 순간 실제 모델이 튀어나온다
|
||||
if (r.popGhosts > 0 && s_landPose != null) for (int i = 0; i < r.popGhosts; i++) { var jitter = Quaternion.Euler(0f, (i - (r.popGhosts - 1) * 0.5f) * 18f, 0f) * cast.landFwd; if (Spawn(c, cast, cast.shooter.Get_position() + jitter * 0.12f * (i + 1), jitter, s_landPose)) JumpSpawned++; }
|
||||
if (!string.IsNullOrEmpty(r.dustPrefab))
|
||||
|
|
|
|||
|
|
@ -47,6 +47,13 @@ namespace WL.Combat.Weapon
|
|||
// 띄우고 fxSlashLife 동안 날 방향(수평 성분)·위치를 매 프레임 따라가게(fxSlashFollow 1 · 0 = 처음 자리 고정)
|
||||
public int fxMain = 1, fxPerSlash = 0, fxSlashFollow = 1;
|
||||
public float fxSlashScale = 0.35f, fxSlashLife = 0.6f, fxSlashFraction = 0.6f, fxSlashYOffset = 0f;
|
||||
// #959 PD 「이펙트 크기 2배 · 플레이어 동작에 맞게 재생시간을 늘려 모션만 보이지 않게(캐릭터 전진값 고려해 위치도) · 공격 모션에 잔상(빠르게 공격하는 느낌)」 —
|
||||
// fxRepeatInterval > 0 = 정면 사본을 fxDelay 뒤 이 간격으로 fxRepeatSeconds(0 = slashSeconds) 까지 되풀이(매번 그때의 PC 위치·대상 방향으로 다시 계산 = 전진 반영) ·
|
||||
// slashHardRestart 1 = 난사 상태를 CrossFade 대신 Animator.Play 로 강제(Play 실측: 배속 1.8 에선 컨트롤러 exit 전이(2타→3타 0.50)가 0.28 s 틱보다 먼저 와 3타 회전·4타 돌진(0.4 m 전진)이 섞였다) ·
|
||||
// slashGhosts 1 = 난사 동안 slashGhostInterval 마다 지금 자세 잔상(WLGhostStrike.LiveGhost · 색 RGBA · 유지 · 소멸 초)
|
||||
public float fxRepeatInterval = 0f, fxRepeatSeconds = 0f;
|
||||
public int slashHardRestart = 0, slashGhosts = 0;
|
||||
public float slashGhostInterval = 0.07f, slashGhostHold = 0.03f, slashGhostFade = 0.14f; public float[] slashGhostColor = new float[0];
|
||||
}
|
||||
|
||||
public static class WLShadowSlash
|
||||
|
|
@ -72,9 +79,9 @@ namespace WL.Combat.Weapon
|
|||
}
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
static void Boot() { s_cfg = null; Casts = Shown = Scaled = Errors = 0; LastLog = ""; }
|
||||
static void Boot() { s_cfg = null; Casts = Shown = Scaled = Errors = 0; SlashLoops = SlashLeaks = FxShown = FxRepeats = FxSlashes = SlashGhostCount = 0; LastLog = SlashLog = FxRepeatLog = ""; }
|
||||
|
||||
public static string Describe() { return "casts " + Casts + " · shown " + Shown + " · scaled " + Scaled + " · err " + Errors + " · " + LastLog; }
|
||||
public static string Describe() { return "casts " + Casts + " · shown " + Shown + " · scaled " + Scaled + " · err " + Errors + " · fx " + FxShown + "(반복 " + FxRepeats + ") · 잔상 " + SlashGhostCount + " · " + LastLog; }
|
||||
|
||||
/// <summary>원본 Skill_ShadowSlash.Set 훅(Set_Invisible 바로 뒤 1줄) — 캐릭터 보이기 · 베기 · 연기 크기 배율.</summary>
|
||||
public static void OnSet(Skill_ShadowSlash skill)
|
||||
|
|
@ -101,6 +108,7 @@ namespace WL.Combat.Weapon
|
|||
if (c.fxMain != 0) Runner.StartCoroutine(CoFx(shooter, c)); // #956 fxMain 0 = 정면 사본 없이 난사 사본(fxPerSlash)만
|
||||
}
|
||||
if (c.slashStates != null && c.slashStates.Length > 0 && !DSUtil.CheckNull(shooter) && shooter.m_animation != null) Runner.StartCoroutine(CoSlash(shooter, c)); // #955
|
||||
if (c.slashGhosts != 0 && !DSUtil.CheckNull(shooter)) Runner.StartCoroutine(CoGhosts(shooter, c)); // #959 난사 잔상
|
||||
LastLog = "cast " + Casts + " · shown " + Shown + " · scaled " + Scaled;
|
||||
}
|
||||
catch (System.Exception ex) { Errors++; LastLog = "실패 — " + ex.Message; }
|
||||
|
|
@ -109,12 +117,25 @@ namespace WL.Combat.Weapon
|
|||
public static int FxShown;
|
||||
static WLShadowSlashRunner s_runner;
|
||||
static WLShadowSlashRunner Runner { get { if (s_runner == null) { var go = new GameObject("[WL954] ShadowSlashFx"); go.hideFlags = HideFlags.HideAndDontSave; Object.DontDestroyOnLoad(go); s_runner = go.AddComponent<WLShadowSlashRunner>(); } return s_runner; } }
|
||||
/// <summary>#954 원본 베기가 켜지는 시각(fxDelay)에 WL 사본(녹색 난사)을 PC 정면 fxForward 에 띄운다(PC 방향 · fxLife 뒤 파괴).</summary>
|
||||
public static int FxRepeats; public static string FxRepeatLog = "";
|
||||
/// <summary>#954 원본 베기가 켜지는 시각(fxDelay)에 WL 사본(녹색 난사)을 PC 정면 fxForward 에 띄운다(PC 방향 · fxLife 뒤 파괴). #959 fxRepeatInterval > 0 이면 fxRepeatSeconds(0 = slashSeconds)까지 되풀이 — 매번 그때 PC 위치·대상 방향.</summary>
|
||||
static System.Collections.IEnumerator CoFx(Actor shooter, WLShadowSlashJson c)
|
||||
{
|
||||
if (c.fxDelay > 0f) yield return new WaitForSeconds(c.fxDelay);
|
||||
if (DSUtil.CheckNull(shooter) || shooter.IsDead()) yield break;
|
||||
var p = Resources.Load<GameObject>(c.fxPrefab); if (p == null) { LastLog = "fx 프리팹 없음 " + c.fxPrefab; yield break; }
|
||||
float t0 = Time.time - c.fxDelay, until = c.fxRepeatSeconds > 0f ? c.fxRepeatSeconds : c.slashSeconds; int n = 0;
|
||||
while (true)
|
||||
{
|
||||
if (DSUtil.CheckNull(shooter) || shooter.IsDead()) yield break;
|
||||
if (!SpawnMainFx(shooter, c)) yield break;
|
||||
n++; if (n > 1) FxRepeats++;
|
||||
if (c.fxRepeatInterval <= 0f) yield break;
|
||||
yield return new WaitForSeconds(c.fxRepeatInterval);
|
||||
if (Time.time - t0 + 0.01f >= until) { FxRepeatLog = "정면 사본 " + n + "개 · " + (Time.time - t0).ToString("F2") + " s"; yield break; }
|
||||
}
|
||||
}
|
||||
static bool SpawnMainFx(Actor shooter, WLShadowSlashJson c)
|
||||
{
|
||||
var p = Resources.Load<GameObject>(c.fxPrefab); if (p == null) { LastLog = "fx 프리팹 없음 " + c.fxPrefab; return false; }
|
||||
var fwd = shooter.transform.forward; fwd.y = 0f; if (fwd.sqrMagnitude < 1e-4f) fwd = Vector3.forward; fwd.Normalize();
|
||||
var tgt = shooter.Get_Target(); float toTarget = -1f;
|
||||
if (c.fxAimTarget != 0 && !DSUtil.CheckNull(tgt) && !tgt.IsDead()) // #958 대상 쪽
|
||||
|
|
@ -132,29 +153,52 @@ namespace WL.Combat.Weapon
|
|||
Object.Destroy(go, Mathf.Max(0.5f, c.fxLife)); FxShown++;
|
||||
LastLog = "fx " + c.fxPrefab + " @" + pos.ToString("F2") + " ×" + scale.ToString("F3") + " · 거리 " + dist.ToString("F2") + " · 공격 범위 " + range.ToString("F2") + " · 대상 " + (toTarget > 0f ? toTarget.ToString("F2") + " m" : "없음");
|
||||
FxSlashLog = LastLog;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int SlashLoops; public static string SlashLog = "";
|
||||
public static int SlashGhostCount;
|
||||
/// <summary>#959 난사 동안 지금 자세 잔상 — slashGhostInterval 마다 WLGhostStrike.LiveGhost(색 · 유지 · 소멸).</summary>
|
||||
static System.Collections.IEnumerator CoGhosts(Actor shooter, WLShadowSlashJson c)
|
||||
{
|
||||
float t0 = Time.time; var col = c.slashGhostColor != null && c.slashGhostColor.Length >= 4 ? c.slashGhostColor : null;
|
||||
while (Time.time - t0 < c.slashSeconds)
|
||||
{
|
||||
if (DSUtil.CheckNull(shooter) || shooter.IsDead()) yield break;
|
||||
if (WLGhostStrike.LiveGhost(shooter, col, c.slashGhostHold, c.slashGhostFade)) SlashGhostCount++;
|
||||
yield return new WaitForSeconds(Mathf.Max(0.02f, c.slashGhostInterval));
|
||||
}
|
||||
}
|
||||
|
||||
public static int SlashLoops, SlashLeaks; public static string SlashLog = "";
|
||||
/// <summary>#955 제자리 난사 — slashSeconds 동안 slashStates 를 번갈아 CrossFade(이벤트 끔 · 배속) · 스킬 상태를 벗어나면 중단 · 끝에 이벤트·배속 원상.</summary>
|
||||
static System.Collections.IEnumerator CoSlash(Actor shooter, WLShadowSlashJson c)
|
||||
{
|
||||
var an = shooter.m_animation; if (an == null) yield break;
|
||||
bool prevEvents = an.fireEvents; an.fireEvents = false; float t0 = Time.time; int i = 0;
|
||||
bool prevEvents = an.fireEvents; an.fireEvents = false; float t0 = Time.time; int i = -1, cur = 0; float interval = Mathf.Max(0.05f, c.slashInterval), tickAt = t0;
|
||||
try
|
||||
{
|
||||
while (Time.time - t0 < c.slashSeconds)
|
||||
{
|
||||
if (DSUtil.CheckNull(shooter) || shooter.IsDead()) break; // CurAnim 조건은 뺀다(실측: 3회 만에 끊겨 원본 콤보 전이 Attack3→4(회전)·Jump 사슬이 이어졌다) · 0.28 s 마다 다시 시작하므로 상태 exit 전이에 안 걸린다
|
||||
string st = c.slashStates[i % c.slashStates.Length]; int h = Animator.StringToHash(st);
|
||||
if (an.HasState(0, h)) { an.CrossFadeInFixedTime(h, 0.05f, 0, 0f); an.speed = Mathf.Max(0.1f, c.slashSpeed); SlashLoops++; }
|
||||
else SlashLog = "상태 없음 " + st;
|
||||
if (c.fxPerSlash != 0 && !string.IsNullOrEmpty(c.fxPrefab)) SlashFx(shooter, c, i); // #956 휘두르는 손의 날 자리에 작은 사본
|
||||
i++;
|
||||
yield return new WaitForSeconds(Mathf.Max(0.05f, c.slashInterval));
|
||||
if (DSUtil.CheckNull(shooter) || shooter.IsDead()) break; // CurAnim 조건은 뺀다(실측: 3회 만에 끊겨 원본 콤보 전이 Attack3→4(회전)·Jump 사슬이 이어졌다)
|
||||
if (i < 0 || Time.time >= tickAt)
|
||||
{
|
||||
i++; tickAt = t0 + (i + 1) * interval;
|
||||
string st = c.slashStates[i % c.slashStates.Length]; cur = Animator.StringToHash(st);
|
||||
if (an.HasState(0, cur)) { if (c.slashHardRestart != 0) an.Play(cur, 0, 0f); else an.CrossFadeInFixedTime(cur, 0.05f, 0, 0f); an.speed = Mathf.Max(0.1f, c.slashSpeed); SlashLoops++; }
|
||||
else SlashLog = "상태 없음 " + st;
|
||||
if (c.fxPerSlash != 0 && !string.IsNullOrEmpty(c.fxPrefab)) SlashFx(shooter, c, i); // #956 휘두르는 손의 날 자리에 작은 사본
|
||||
}
|
||||
else if (c.slashHardRestart != 0)
|
||||
{
|
||||
// #959 Play 실측: 틱 사이에 다른 상태(원본 콤보 AI 의 Attack3→4 · 원본 Jump 사슬 · exit 전이)가 끼어들어 3타 회전·4타 돌진(1.4 m)이 섞였다 → 매 프레임 지키고 어긋나면 그 프레임에 되돌린다
|
||||
bool foreign = an.GetCurrentAnimatorStateInfo(0).shortNameHash != cur || (an.IsInTransition(0) && an.GetNextAnimatorStateInfo(0).shortNameHash != cur);
|
||||
if (foreign) { an.Play(cur, 0, 0f); an.speed = Mathf.Max(0.1f, c.slashSpeed); SlashLeaks++; }
|
||||
}
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
finally { if (!DSUtil.CheckNull(shooter) && shooter.m_animation != null) { shooter.m_animation.fireEvents = prevEvents; shooter.m_animation.speed = 1f; } }
|
||||
SlashLog = "난사 " + i + "회 · " + (Time.time - t0).ToString("F2") + " s";
|
||||
SlashLog = "난사 " + (i + 1) + "회 · " + (Time.time - t0).ToString("F2") + " s · 되돌림 " + SlashLeaks;
|
||||
}
|
||||
|
||||
public static int FxSlashes; public static string FxSlashLog = "";
|
||||
|
|
|
|||
Loading…
Reference in New Issue