Merge branch 'wl/gameplay/WL-811s-effect-scale-pass'
This commit is contained in:
commit
a3a5aedb55
|
|
@ -0,0 +1,201 @@
|
|||
=== WL-811s MEASURE · 2026-09-10 01:01:55 (에디트 모드 · Play 0) ===
|
||||
|
||||
[A] 액터 높이 (프리팹 렌더러 바운드 · 원 크기 → × 테이블 배율)
|
||||
kind name rawH(m) rawD(m) tableScale 실높이(m) 실지름(m)
|
||||
PC Ai01 (10101/10102/10501/10502) 1.702 1.305 0.70 1.191 0.913
|
||||
PC Ai02 1.611 1.305 0.70 1.127 0.913
|
||||
PC Eri01 1.689 1.305 0.70 1.182 0.913
|
||||
존1 Mob/Batty_A 0.358 0.624 1.50 0.537 0.936
|
||||
존1 Mob/Batty_D 0.358 0.624 1.50 0.537 0.936
|
||||
존2 Mob/Porin_A 0.491 0.526 1.30 0.639 0.684
|
||||
존2 Mob/Porin_E 0.539 0.526 1.30 0.701 0.684
|
||||
존3 Mob/Racco_A 0.582 0.762 1.10 0.640 0.838
|
||||
존3 Mob/Rabby_Brown 0.385 0.545 1.50 0.577 0.817
|
||||
존4 Mob/Devilu_A 0.556 0.704 1.20 0.668 0.845
|
||||
존4 Mob/Devilu_C 0.612 0.704 1.20 0.734 0.845
|
||||
엘리트1 Elite/Aspethis_D 0.691 0.559 2.30 1.590 1.286
|
||||
엘리트2 Elite/Cute_Crab_C_Blue 0.376 0.617 2.50 0.941 1.542
|
||||
엘리트3 Elite/Beeto_A 0.526 0.596 3.00 1.579 1.787
|
||||
엘리트4 Elite/Mummy_King 0.591 0.401 2.30 1.360 0.921
|
||||
보스 FieldBoss/Anubis 2.085 1.771 1.50 3.128 2.656
|
||||
엘리트 배수(WLEliteSettings.scaleMultiplier) = 1.30 → 위 「엘리트*」 실높이 × 1.30
|
||||
PC 기준 높이 = 1.191 m
|
||||
|
||||
[B] 이펙트 원 크기 (프리팹 localScale 포함 · Simulate t 스윕 · 파티클 실좌표 GetParticles)
|
||||
prefab baseScale ps pMax diaP90(m) diaMax(m) hgt(m) note
|
||||
Effect_Berserk 1.000 4 19 1.430 3.084 3.340
|
||||
Effect_PCDash 1.000 4 10 2.036 3.076 2.726
|
||||
Effect_Howling 1.000 11 20 5.702 10.632 6.765
|
||||
Effect_Casting_Energyball 1.000 6 15 2.143 2.541 3.824
|
||||
Effect_Casting_ManaShield 1.000 12 38 2.650 5.000 6.683
|
||||
Effect_Casting_Meteor 1.000 7 93 1.802 2.747 2.607
|
||||
Effect_Slash_10101_1 1.000 8 20 3.890 7.790 7.200
|
||||
Effect_Slash_10101_2 1.000 8 20 5.332 8.108 7.200
|
||||
Effect_Slash_10101_3 1.000 5 20 14.741 20.928 9.628
|
||||
Effect_WLSwingArc 1.000 5 15 4.801 5.103 2.808
|
||||
Effect_Slash_10102_1 1.000 6 20 3.874 6.582 3.704
|
||||
Effect_Slash_10102_2 1.000 6 20 4.431 4.533 5.912
|
||||
Effect_SwordShield 1.000 1 1 10.200 15.095 10.200
|
||||
Effect_STR_EXTRA_SPLASH 1.000 7 20 10.680 20.865 10.221
|
||||
Effect_Slash_10501_1 1.000 6 12 5.333 6.062 4.520
|
||||
Effect_Slash_10501_2 1.000 6 12 5.653 6.461 4.522
|
||||
Effect_Slash_10501_3 1.000 5 20 14.741 20.928 9.628
|
||||
Effect_Heal 1.000 8 18 2.171 3.512 2.802
|
||||
Effect_Slash_10502_1 1.000 5 10 4.828 5.472 4.364
|
||||
Effect_Slash_10502_2 1.000 5 10 5.195 5.641 4.364
|
||||
Effect_Aggro 1.000 1 3 0.454 0.454 0.454
|
||||
ShineBulletFlash 1.000 4 11 2.157 5.000 5.000
|
||||
Effect_Hit_10101 1.000 5 20 3.530 5.216 2.277
|
||||
Effect_Hit_10102 1.000 6 5 1.150 1.492 1.492
|
||||
Effect_Hit_10501 1.000 6 20 8.826 20.865 7.670
|
||||
Effect_Hit_SweepAttack 1.000 6 10 1.961 2.847 2.847
|
||||
Effect_Hit_Skill_EnergySlashFlash 1.000 8 37 4.925 16.424 16.424
|
||||
Effect_Hit_Energyball 1.000 4 6 2.705 6.517 6.517
|
||||
WindBullet_Hit 1.000 5 30 2.840 5.084 5.638
|
||||
WaveShotHit 1.000 5 100 1.134 3.013 3.000
|
||||
ShineBulletHit 1.000 5 11 2.700 7.000 7.000
|
||||
Effect_Hit_Skill_CrossSlash 1.000 5 17 0.638 1.199 1.147
|
||||
Effect_Hit_Skill_SpinSlash 1.000 3 6 0.463 0.648 0.647
|
||||
Effect_Stun 1.000 5 10 5.776 6.840 6.802
|
||||
Effect_Poison 1.000 5 41 1.866 3.130 2.095
|
||||
Effect_Burn 1.000 10 20 3.151 5.000 5.000
|
||||
Effect_Blind 1.000 0 0 0.000 0.000 0.000 nops,noparticle
|
||||
Effect_Curse 1.000 7 30 4.000 5.363 4.989
|
||||
Effect_Slow 1.000 9 7 3.000 5.011 5.884
|
||||
Effect_Freezing 1.000 7 24 4.549 4.950 5.515
|
||||
Effect_Frost 1.000 9 20 7.595 20.865 7.670
|
||||
Effect_HPBurn 1.000 3 12 3.436 4.998 4.998
|
||||
Effect_ManaBurn 1.000 4 50 2.355 3.915 3.666
|
||||
Effect_SilenceCurse 1.000 9 13 3.351 4.936 4.777
|
||||
Effect_Blood 1.000 5 59 7.199 20.299 7.487
|
||||
Effect_DefenceDecrease 1.000 4 4 4.226 4.893 8.215
|
||||
Effect_Barrier 1.000 4 2 22.000 22.000 22.000
|
||||
Effect_ManaShield 1.000 9 14 1.395 20.000 20.000
|
||||
Effect_Cure 1.000 4 20 5.249 10.433 4.532
|
||||
Effect_SpeedUp 1.000 10 7 3.000 5.011 6.857
|
||||
Effect_StatUp 1.000 4 20 2.231 2.248 8.763
|
||||
Effect_Reincarnation 1.000 28 40 3.372 4.909 5.948
|
||||
Effect_VampirHeal 1.000 4 25 8.000 20.299 3.180
|
||||
Effect_Hit_10201 1.000 8 10 4.421 5.486 5.486
|
||||
Effect_Hit_10202 1.000 3 10 1.327 1.329 1.329
|
||||
Effect_Hit_10301 1.000 3 3 1.345 1.505 2.591
|
||||
Effect_Hit_10302 1.500 5 16 2.167 2.970 2.580
|
||||
Effect_Hit_10303 1.000 8 22 1.519 2.431 2.552
|
||||
Effect_Hit_10401 1.000 3 16 1.136 1.726 0.830
|
||||
Effect_Hit_10402 1.000 4 16 1.818 2.766 1.374
|
||||
Effect_Hit_10502 1.000 6 10 2.847 3.254 3.087
|
||||
Effect_Hit_10601 1.000 7 15 2.488 3.425 3.355
|
||||
Effect_Hit_IceBolt 1.000 7 21 2.961 7.000 7.000
|
||||
Effect_Hit_Fireball 1.000 12 35 3.503 5.395 7.106
|
||||
Effect_Hit_Firebolt 1.000 8 15 1.933 2.500 2.500
|
||||
Effect_Hit_Electronic 1.000 7 15 1.919 2.500 2.504
|
||||
Effect_Hit_Bead 1.000 7 30 1.205 2.400 1.594
|
||||
Effect_Hit_BlackHole 1.000 9 20 10.757 22.952 8.449
|
||||
Effect_Hit_AnubisSkill1 1.000 3 15 1.484 2.047 2.207
|
||||
Effect_Hit_AnubisSkill2 1.000 9 10 1.587 2.774 2.774
|
||||
Effect_Hit_BrotherPenguin_Attack 1.000 6 9 2.112 3.421 4.070
|
||||
Effect_Hit_FlowerDryad_Attack 1.000 4 37 0.933 1.253 1.206
|
||||
Effect_Hit_IceGolem_Attack 1.000 4 8 1.013 1.497 3.694
|
||||
Effect_Hit_Shaga_Attack 1.000 5 20 1.385 1.887 2.531
|
||||
Effect_Hit_Mob_HellfireFlames 1.000 8 20 1.991 2.500 2.500
|
||||
Effect_Hit_RookieArcherSkill1 1.000 9 11 1.800 3.821 3.185
|
||||
Effect_Hit_SummonGolem 1.000 6 12 1.163 2.100 2.100
|
||||
Effect_Hit_SummonWorm 1.000 6 10 1.164 1.750 2.531
|
||||
Effect_Hit_Skill_DaggerThrow 1.000 5 37 2.590 5.761 8.645
|
||||
Effect_Hit_Skill_Frozen 1.000 7 20 6.000 20.299 3.229
|
||||
|
||||
[C] 카메라 (Main Camera.prefab fov 60.0 · RealCamera yaw 347.21 pan 0.00 · 811o 오버라이드 h 76.0 d 3.00 up 2.40)
|
||||
camPos(타겟 원점 기준) = (-2.120, 4.585, -4.211) · 거리 6.576 m · 내림각 44.2°
|
||||
세로 1080×1920 (aspect 0.5625) · 1 m 구가 PC 몸 중심(y=0.60)에 있을 때 세로 점유 = 19.4 %
|
||||
|
||||
[C2] 현재 16행 슬롯 4 본체의 화면 세로 점유 (현재 scale · 앵커 Mid = PC 몸 중심)
|
||||
skill prefab scale dia0(m) dia*(m) 세로점유%
|
||||
702006 Effect_WLSwingArc 0.58 4.801 2.770 53.7
|
||||
702009 Effect_STR_EXTRA_SPLASH 0.23 10.680 2.435 47.2
|
||||
702005 Effect_Slash_10501_3 0.16 14.741 2.344 45.5
|
||||
702012 ShineBulletFlash 1.00 2.157 2.150 41.7
|
||||
|
||||
콘솔 error/exception = 0
|
||||
=== WL-811s VERIFY · 2026-09-10 01:03:02 (에디트 모드 · Play 0) ===
|
||||
PASS SO 로드 · spectacle=True effectScale=True rules=57
|
||||
|
||||
[G] 새 에셋 guid → 경로(충돌 0 확인)
|
||||
PASS guid 8115a0c3 · Assets/WL/Combat/Reaction/WLEffectScaleSettings.cs
|
||||
PASS guid 8115b1d4 · Assets/WL/Combat/Reaction/EffectScaleGuard.cs
|
||||
PASS guid 8115c2e5 · Assets/WL/Combat/Reaction/EffectScaleGuardRunner.cs
|
||||
PASS guid 8115d3f6 · Assets/WL/Combat/Settings/Resources/WL/WLEffectScaleSettings.asset
|
||||
|
||||
[D] 화면 점유 (fov 60 · 카메라 거리 6.58 m · 1080×1920)
|
||||
세로 50 % 가 되는 지름 = 2.577 m (= 슬롯 4 본체 캡)
|
||||
|
||||
[D2] 16행 전/후 (scale · dia0 = 프리팹 원 지름 실측 · after = dia0 × new)
|
||||
skill slot label phase prefab dia0 before after 지름before 지름after 세로%before 세로%after
|
||||
703101 1 연속 베기 body Effect_Slash_10101_1 3.890 1.200 0.459 4.668 1.786 90.6 34.6
|
||||
703101 1 연속 베기 impact Effect_Hit_10101 3.530 1.000 0.270 3.530 0.953 68.5 18.5
|
||||
703102 2 회심의 일격 charge Effect_Berserk 1.430 1.000 0.999 1.430 1.429 26.0 25.9
|
||||
703102 2 회심의 일격 body Effect_Slash_10101_2 5.332 1.300 0.335 6.931 1.786 134.5 34.6
|
||||
703102 2 회심의 일격 impact Effect_Hit_10101 3.530 1.200 0.270 4.236 0.953 82.2 18.5
|
||||
703103 3 돌진 베기 charge Effect_PCDash 2.036 1.000 0.702 2.036 1.429 37.0 26.0
|
||||
703103 3 돌진 베기 body Effect_Slash_10101_3 14.741 1.400 0.121 20.638 1.784 867.8 34.6
|
||||
703103 3 돌진 베기 impact Effect_Hit_SweepAttack 1.961 1.000 0.486 1.961 0.953 38.0 18.5
|
||||
702006 4 돌개 바람 charge Effect_Howling 5.702 1.200 0.251 6.842 1.431 124.2 26.0
|
||||
702006 4 돌개 바람 body Effect_WLSwingArc 4.801 1.500 0.483 7.202 2.319 139.7 45.0
|
||||
702006 4 돌개 바람 impact WindBullet_Hit 2.840 1.200 0.335 3.408 0.951 66.1 18.5
|
||||
703106 1 무자비한 일격 body Effect_Slash_10102_1 3.874 1.200 0.461 4.649 1.786 90.2 34.6
|
||||
703106 1 무자비한 일격 impact Effect_Hit_10102 1.150 1.000 0.829 1.150 0.953 22.3 18.5
|
||||
703105 2 검기 분출 charge Effect_Casting_Energyball 2.143 1.000 0.667 2.143 1.430 38.9 26.0
|
||||
703105 2 검기 분출 body Effect_Slash_10102_2 4.431 1.300 0.403 5.761 1.786 111.8 34.6
|
||||
703105 2 검기 분출 impact Effect_Hit_Skill_EnergySlashFlash 4.925 1.000 0.193 4.925 0.950 95.5 18.4
|
||||
703104 3 칼날 방패 charge Effect_Casting_ManaShield 2.650 1.000 0.539 2.650 1.428 48.1 25.9
|
||||
703104 3 칼날 방패 body Effect_SwordShield 10.200 1.350 0.175 13.770 1.785 329.6 34.6
|
||||
702009 4 마나 폭발 charge Effect_Casting_Meteor 1.802 1.200 0.793 2.163 1.429 39.3 26.0
|
||||
702009 4 마나 폭발 body Effect_STR_EXTRA_SPLASH 10.680 1.600 0.217 17.088 2.318 499.3 45.0
|
||||
702009 4 마나 폭발 impact Effect_Hit_Energyball 2.705 1.200 0.352 3.247 0.952 63.0 18.5
|
||||
703502 1 방패 치기 body Effect_Slash_10501_1 5.333 1.200 0.335 6.400 1.787 124.2 34.7
|
||||
703502 1 방패 치기 impact Effect_Hit_10501 8.826 1.000 0.108 8.826 0.953 801.3 18.5
|
||||
703503 2 강력한 일격 charge Effect_PCDash 2.036 1.000 0.702 2.036 1.429 37.0 26.0
|
||||
703503 2 강력한 일격 body Effect_Slash_10501_2 5.653 1.400 0.316 7.915 1.786 143.7 32.4
|
||||
703503 2 강력한 일격 impact Effect_Hit_SweepAttack 1.961 1.000 0.486 1.961 0.953 38.0 18.5
|
||||
703501 3 간식 먹기 body Effect_Heal 2.171 1.300 0.823 2.823 1.787 54.8 34.7
|
||||
702005 4 휘몰아치는 파도 charge Effect_Howling 5.702 1.200 0.251 6.842 1.431 124.2 26.0
|
||||
702005 4 휘몰아치는 파도 body Effect_Slash_10501_3 14.741 1.800 0.157 26.535 2.314 1269.4 44.9
|
||||
702005 4 휘몰아치는 파도 impact WaveShotHit 1.134 1.200 0.840 1.361 0.953 26.4 18.5
|
||||
703505 1 십자 가르기 body Effect_Slash_10502_1 4.828 1.200 0.370 5.793 1.786 112.4 34.7
|
||||
703505 1 십자 가르기 impact Effect_Hit_Skill_CrossSlash 0.638 1.000 1.000 0.638 0.638 12.4 12.4
|
||||
703506 2 회전 베기 body Effect_Slash_10502_2 5.195 1.500 0.344 7.793 1.787 151.2 34.7
|
||||
703506 2 회전 베기 impact Effect_Hit_Skill_SpinSlash 0.463 1.000 1.000 0.463 0.463 9.0 9.0
|
||||
703504 3 도발 charge Effect_Howling 5.702 1.000 0.251 5.702 1.431 103.5 26.0
|
||||
703504 3 도발 body Effect_Aggro 0.454 1.300 1.300 0.590 0.590 11.4 11.4
|
||||
702012 4 심판의 빛 charge Effect_Casting_Meteor 1.802 1.200 0.793 2.163 1.429 39.3 26.0
|
||||
702012 4 심판의 빛 body ShineBulletFlash 2.157 1.500 1.075 3.235 2.319 62.8 45.0
|
||||
702012 4 심판의 빛 impact ShineBulletHit 2.700 1.200 0.353 3.240 0.953 62.9 18.5
|
||||
|
||||
[D3] 슬롯 4 본체 캡 판정 (≤ 50 %)
|
||||
PASS 슬롯4 702006 Effect_WLSwingArc · 지름 2.319 m · 세로 45.0 %
|
||||
PASS 슬롯4 702009 Effect_STR_EXTRA_SPLASH · 지름 2.318 m · 세로 45.0 %
|
||||
PASS 슬롯4 702005 Effect_Slash_10501_3 · 지름 2.314 m · 세로 44.9 %
|
||||
PASS 슬롯4 702012 ShineBulletFlash · 지름 2.319 m · 세로 45.0 %
|
||||
|
||||
[E] EffectScaleGuard (가짜 풀 루트 = InGameInfo.tf_Effects · 진짜 이펙트 프리팹 인스턴스)
|
||||
PASS 규칙 매칭 · Effect_Stun → rule 0 (scale 0.124) · Effect_AnubisCharging → -1(없음 = 원본)
|
||||
PASS ① 활성 인스턴스 배율 적용 · Effect_Stun 1.000 → 0.1240 (기대 0.1240)
|
||||
PASS ② 규칙 없는 것은 무변경 · Effect_AnubisCharging 1.0000 (원본 1.0000)
|
||||
PASS ③ 비활성은 아직 미적용 · Effect_Poison 1.0000
|
||||
PASS ④ 멱등(3회 더 틱) · scale 0.124000 · Applied 2 → 2
|
||||
PASS ⑤ 반납(SetActive false) → 원 스케일 복귀 · 1.0000 (원본 1.0000)
|
||||
PASS ⑥ 재활성 → 다시 적용 · 0.1240
|
||||
PASS ⑦ 나중에 활성된 인스턴스도 적용 · Effect_Poison → 0.3830
|
||||
|
||||
[E2] scaleByTargetHeight (대상 액터 실높이로 재계산)
|
||||
PASS 리플렉션 필드 존재 · Actor=True Actor.tf_Top=True TurnOff_GO.m_target2=True
|
||||
PASS Effect_Stun 에 TurnOff_GO 존재 · ok
|
||||
PASS ⑧ 대상 높이 기반 재계산 · 몹 높이 0.537 m × 0.6 ÷ 5.776 = 0.0558 → scale 0.0558 · 지름 0.322 m · HeightResolved 1
|
||||
PASS ⑨ 대상 없으면 고정 배율로 폴백 · scale 0.1240 · HeightMissed 0 → 1
|
||||
|
||||
[F] GC · C8
|
||||
PASS ⑩ GC 0 (Tick ×200,000 · 워밍 후) · GetAllocatedBytesForCurrentThread Δ 0 B · MonoUsed Δ 0 B
|
||||
PASS ⑪ C8 (enabled = 0) → 전량 원복 · stun 1.0000 · hit 1.0000 · ActiveScaled 0
|
||||
PASS ⑫ C8 상태에서 무동작 · Applied 6 → 6
|
||||
카운터: Sweep 200012 · Tracked 4 · Ruled 3 · Applied 6 · Restored 6 · Skipped 0
|
||||
|
||||
RESULT PASS 24/24 · 콘솔 error/exception = 0
|
||||
|
|
@ -0,0 +1,667 @@
|
|||
// PD 지시 #813 · 발주서 WL-811s — 이펙트 크기 1차 조정 검증 프로브 (에디트 모드 전용 · Play 0 · 로그인 0 · 에셋 무수정)
|
||||
//
|
||||
// 실행 (CLAUDE.md §1: using 금지 · 네임스페이스 풀어 쓰기)
|
||||
// unity command run_script --file AgentScripts/WL811s_Probe.cs --entry WL811s_Probe.Measure --timeout 1800
|
||||
// unity command run_script --file AgentScripts/WL811s_Probe.cs --entry WL811s_Probe.Verify --timeout 1800
|
||||
//
|
||||
// Measure — 값을 정하기 위한 실측(코드 수정 전)
|
||||
// A. 액터 높이 : PC 프리팹 렌더러 바운드 × ClassConfig.f_Scale · 존 몹 × MonsterList.f_DefaultScale ·
|
||||
// 엘리트 × WLEliteSettings.scaleMultiplier · 보스 Anubis
|
||||
// B. 이펙트 원 크기 : 프리팹 인스턴스화 → ParticleSystem.Simulate(t, withChildren, restart) 를 t 스윕하며
|
||||
// 모든 렌더러 월드 바운드 합집합의 최대 수평 지름/높이 (= 프리팹 자체 localScale 포함한 「원 크기」)
|
||||
// C. 화면 점유 : Main Camera 프리팹 fov + RealCamera 궤도식(Quaternion.Euler(h,yaw,pan) * Vector3.one)에
|
||||
// 811o 오버라이드(h 76 · d 3 · up 2.4)를 넣어 카메라를 세우고 1080×1920 뷰포트에서 세로 점유율
|
||||
//
|
||||
// Verify — 코드/값을 넣은 뒤의 검증
|
||||
// D. 16행 전/후 표 · 슬롯 4 화면 점유 ≤ 50 %
|
||||
// E. EffectScaleGuard : 가짜 풀 루트에 진짜 이펙트 인스턴스를 넣고 활성/비활성 → 배율 적용·원복·멱등 · 규칙 매칭
|
||||
// F. GC 0 · C8(enabled=0 → 무동작 · 스케일 원본)
|
||||
//
|
||||
// 에디트 모드 주의: 프로브가 만드는 오브젝트는 전부 HideAndDontSave + DestroyImmediate · 어떤 에셋도 쓰지 않는다.
|
||||
public static class WL811s_Probe
|
||||
{
|
||||
const string kOutDir = "AgentScripts/staging/WL811s";
|
||||
const string kMeasureOut = "AgentScripts/staging/WL811s/MEASURE.txt";
|
||||
const string kVerifyOut = "AgentScripts/staging/WL811s/VERIFY.txt";
|
||||
const string kPcPrefab = "Assets/Res_Addr/PC/Ai01.prefab";
|
||||
const string kCamPrefab = "Assets/ResWork/Prefabs/Ingame/Main Camera.prefab";
|
||||
const string kEffectDir = "Assets/Res_Addr/Effect/";
|
||||
|
||||
static System.Text.StringBuilder s_sb = new System.Text.StringBuilder(1 << 16);
|
||||
static void L(string s) { s_sb.Append(s).Append('\n'); }
|
||||
|
||||
static System.Collections.Generic.List<string> s_err = new System.Collections.Generic.List<string>();
|
||||
static bool s_hooked;
|
||||
static void OnLog(string cond, string stack, UnityEngine.LogType t)
|
||||
{
|
||||
if (t == UnityEngine.LogType.Error || t == UnityEngine.LogType.Exception || t == UnityEngine.LogType.Assert)
|
||||
s_err.Add(t + " | " + cond);
|
||||
}
|
||||
static void Hook() { if (!s_hooked) { UnityEngine.Application.logMessageReceived += OnLog; s_hooked = true; } }
|
||||
static void Unhook() { if (s_hooked) { UnityEngine.Application.logMessageReceived -= OnLog; s_hooked = false; } }
|
||||
|
||||
static void Save(string path)
|
||||
{
|
||||
System.IO.Directory.CreateDirectory(kOutDir);
|
||||
System.IO.File.WriteAllText(path, s_sb.ToString(), new System.Text.UTF8Encoding(false));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────── 공용 측정
|
||||
|
||||
/// <summary>프리팹을 숨은 인스턴스로 띄운다(원 localScale 유지 · 원점 · 무회전).</summary>
|
||||
static UnityEngine.GameObject Spawn(string path)
|
||||
{
|
||||
var pf = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.GameObject>(path);
|
||||
if (pf == null) return null;
|
||||
var go = UnityEngine.Object.Instantiate(pf);
|
||||
go.hideFlags = UnityEngine.HideFlags.HideAndDontSave;
|
||||
go.transform.position = UnityEngine.Vector3.zero;
|
||||
go.transform.rotation = UnityEngine.Quaternion.identity;
|
||||
go.transform.localScale = pf.transform.localScale;
|
||||
return go;
|
||||
}
|
||||
|
||||
/// <summary>활성 여부와 무관하게 모든 렌더러의 월드 바운드 합집합. 없으면 false.</summary>
|
||||
static bool RendererBounds(UnityEngine.GameObject go, out UnityEngine.Bounds b, out int count)
|
||||
{
|
||||
b = new UnityEngine.Bounds();
|
||||
count = 0;
|
||||
var rs = go.GetComponentsInChildren<UnityEngine.Renderer>(true);
|
||||
for (int i = 0; i < rs.Length; i++)
|
||||
{
|
||||
var r = rs[i];
|
||||
if (r == null) continue;
|
||||
var rb = r.bounds;
|
||||
if (rb.size.sqrMagnitude <= 0.0000001f) continue;
|
||||
if (count == 0) b = rb; else b.Encapsulate(rb);
|
||||
count++;
|
||||
}
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
static UnityEngine.ParticleSystem.Particle[] s_buf = new UnityEngine.ParticleSystem.Particle[8192];
|
||||
static System.Collections.Generic.List<float> s_radii = new System.Collections.Generic.List<float>(1 << 14);
|
||||
|
||||
/// <summary>파티클 실좌표(GetParticles)로 원점 기준 반경 분포를 만든다. 렌더러 바운드(=Unity 보수 컬링 바운드)는 쓰지 않는다.
|
||||
/// diaMax = 전 파티클 최대 지름 · diaP90 = 90 백분위 지름(스트레이 1개에 흔들리지 않는 「덩치」) · hgt = y 폭.</summary>
|
||||
static void MeasureEffect(string prefabName, out float diaMax, out float diaP90, out float hgt, out float baseScale, out int psCount, out int pCount, out string note)
|
||||
{
|
||||
diaMax = 0f; diaP90 = 0f; hgt = 0f; baseScale = 1f; psCount = 0; pCount = 0; note = "";
|
||||
var go = Spawn(kEffectDir + prefabName + ".prefab");
|
||||
if (go == null) { note = "noprefab"; return; }
|
||||
try
|
||||
{
|
||||
baseScale = go.transform.localScale.x;
|
||||
var pss = go.GetComponentsInChildren<UnityEngine.ParticleSystem>(true);
|
||||
psCount = pss.Length;
|
||||
// 🔴 재현성: 자동 랜덤 시드를 끄고 고정 시드를 준다(안 하면 같은 프리팹이 실행마다 ±16 % 다르게 나온다).
|
||||
for (int i = 0; i < pss.Length; i++)
|
||||
{
|
||||
var ps = pss[i]; if (ps == null) continue;
|
||||
try { ps.useAutoRandomSeed = false; ps.randomSeed = 8115u; } catch (System.Exception) { }
|
||||
}
|
||||
go.SetActive(true);
|
||||
s_radii.Clear();
|
||||
float yMin = 1e9f, yMax = -1e9f;
|
||||
|
||||
// 파티클이 아닌 메시(예: Effect_WLSwingArc 의 슬래시 메시)는 실 바운드가 정확하다
|
||||
var mrs = go.GetComponentsInChildren<UnityEngine.MeshRenderer>(true);
|
||||
for (int i = 0; i < mrs.Length; i++)
|
||||
{
|
||||
var mr = mrs[i];
|
||||
if (mr == null || mr.GetComponent<UnityEngine.ParticleSystem>() != null) continue;
|
||||
var mb = mr.bounds;
|
||||
if (mb.size.sqrMagnitude <= 0.0000001f) continue;
|
||||
float rr = new UnityEngine.Vector2(UnityEngine.Mathf.Abs(mb.center.x) + mb.extents.x, UnityEngine.Mathf.Abs(mb.center.z) + mb.extents.z).magnitude;
|
||||
s_radii.Add(rr);
|
||||
if (mb.min.y < yMin) yMin = mb.min.y;
|
||||
if (mb.max.y > yMax) yMax = mb.max.y;
|
||||
}
|
||||
|
||||
if (pss.Length > 0)
|
||||
{
|
||||
float[] ts = new float[] { 0.05f, 0.1f, 0.15f, 0.2f, 0.3f, 0.4f, 0.5f, 0.7f, 1.0f, 1.3f, 1.6f, 2.0f };
|
||||
for (int k = 0; k < ts.Length; k++)
|
||||
{
|
||||
for (int i = 0; i < pss.Length; i++)
|
||||
{
|
||||
var ps = pss[i];
|
||||
if (ps == null) continue;
|
||||
if (ps.transform.parent != null && ps.transform.parent.GetComponentInParent<UnityEngine.ParticleSystem>() != null) continue;
|
||||
try { ps.Simulate(ts[k], true, true, false); } catch (System.Exception) { }
|
||||
}
|
||||
for (int i = 0; i < pss.Length; i++)
|
||||
{
|
||||
var ps = pss[i];
|
||||
if (ps == null) continue;
|
||||
int n = 0;
|
||||
try { n = ps.GetParticles(s_buf); } catch (System.Exception) { continue; }
|
||||
if (n <= 0) continue;
|
||||
if (n > pCount) pCount = n;
|
||||
bool local = ps.main.simulationSpace == UnityEngine.ParticleSystemSimulationSpace.Local;
|
||||
float ls = UnityEngine.Mathf.Abs(ps.transform.lossyScale.x); if (ls <= 0f) ls = 1f;
|
||||
for (int q = 0; q < n; q++)
|
||||
{
|
||||
var p = s_buf[q];
|
||||
var pos = local ? ps.transform.TransformPoint(p.position) : p.position;
|
||||
float sz = p.GetCurrentSize(ps) * (local ? ls : 1f);
|
||||
float rr = new UnityEngine.Vector2(pos.x, pos.z).magnitude + sz * 0.5f;
|
||||
s_radii.Add(rr);
|
||||
if (pos.y - sz * 0.5f < yMin) yMin = pos.y - sz * 0.5f;
|
||||
if (pos.y + sz * 0.5f > yMax) yMax = pos.y + sz * 0.5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < pss.Length; i++) { var ps = pss[i]; if (ps != null) { try { ps.Stop(true, UnityEngine.ParticleSystemStopBehavior.StopEmittingAndClear); } catch (System.Exception) { } } }
|
||||
}
|
||||
else note = "nops";
|
||||
|
||||
if (s_radii.Count == 0) { note = (note.Length > 0 ? note + "," : "") + "noparticle"; return; }
|
||||
s_radii.Sort();
|
||||
diaMax = s_radii[s_radii.Count - 1] * 2f;
|
||||
int idx = UnityEngine.Mathf.Clamp(UnityEngine.Mathf.RoundToInt((s_radii.Count - 1) * 0.90f), 0, s_radii.Count - 1);
|
||||
diaP90 = s_radii[idx] * 2f;
|
||||
hgt = (yMax > yMin) ? (yMax - yMin) : 0f;
|
||||
}
|
||||
finally { UnityEngine.Object.DestroyImmediate(go); }
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────── A. 액터 높이
|
||||
|
||||
class ActorRow { public string kind, name, path; public float tableScale; public float rawH, rawD; public bool ok; }
|
||||
|
||||
static System.Collections.Generic.List<ActorRow> MeasureActors()
|
||||
{
|
||||
var list = new System.Collections.Generic.List<ActorRow>();
|
||||
// PC (ClassConfig.f_Scale = 0.7 · 4 근접 클래스 공통 · 프리팹은 코스튬 테이블이 고른다 → 기본 Ai01)
|
||||
list.Add(new ActorRow { kind = "PC", name = "Ai01 (10101/10102/10501/10502)", path = kPcPrefab, tableScale = 0.7f });
|
||||
list.Add(new ActorRow { kind = "PC", name = "Ai02", path = "Assets/Res_Addr/PC/Ai02.prefab", tableScale = 0.7f });
|
||||
list.Add(new ActorRow { kind = "PC", name = "Eri01", path = "Assets/Res_Addr/PC/Eri01.prefab", tableScale = 0.7f });
|
||||
// 존 몹 (813e 813001~813004)
|
||||
AddMob(list, "존1", "Mob/Batty_A", 1.5f); AddMob(list, "존1", "Mob/Batty_D", 1.5f);
|
||||
AddMob(list, "존2", "Mob/Porin_A", 1.3f); AddMob(list, "존2", "Mob/Porin_E", 1.3f);
|
||||
AddMob(list, "존3", "Mob/Racco_A", 1.1f); AddMob(list, "존3", "Mob/Rabby_Brown", 1.5f);
|
||||
AddMob(list, "존4", "Mob/Devilu_A", 1.2f); AddMob(list, "존4", "Mob/Devilu_C", 1.2f);
|
||||
// 엘리트 (813s · × scaleMultiplier)
|
||||
AddMob(list, "엘리트1", "Elite/Aspethis_D", 2.3f); AddMob(list, "엘리트2", "Elite/Cute_Crab_C_Blue", 2.5f);
|
||||
AddMob(list, "엘리트3", "Elite/Beeto_A", 3.0f); AddMob(list, "엘리트4", "Elite/Mummy_King", 2.3f);
|
||||
// 보스
|
||||
AddMob(list, "보스", "FieldBoss/Anubis", 1.5f);
|
||||
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
var r = list[i];
|
||||
var go = Spawn(r.path);
|
||||
if (go == null) { r.ok = false; continue; }
|
||||
try
|
||||
{
|
||||
go.transform.localScale = UnityEngine.Vector3.one; // 원 크기(테이블 배율 전) 를 잰다
|
||||
UnityEngine.Bounds b; int c;
|
||||
if (RendererBounds(go, out b, out c)) { r.rawH = b.size.y; r.rawD = UnityEngine.Mathf.Max(b.size.x, b.size.z); r.ok = true; }
|
||||
}
|
||||
finally { UnityEngine.Object.DestroyImmediate(go); }
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
static void AddMob(System.Collections.Generic.List<ActorRow> l, string kind, string rel, float sc)
|
||||
{
|
||||
l.Add(new ActorRow { kind = kind, name = rel, path = "Assets/Res_Addr/Mobs/" + rel + ".prefab", tableScale = sc });
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────── C. 카메라 · 화면 점유
|
||||
|
||||
class CamInfo { public float fov, h, dist, up, yaw, pan; public UnityEngine.Vector3 camPos; public UnityEngine.Vector3 lookAt; }
|
||||
|
||||
static CamInfo BuildCam()
|
||||
{
|
||||
var ci = new CamInfo { fov = 60f, h = 76f, dist = 3f, up = 2.4f, yaw = 347.21f, pan = 0f };
|
||||
var cam = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.GameObject>(kCamPrefab);
|
||||
if (cam != null)
|
||||
{
|
||||
var c = cam.GetComponent<UnityEngine.Camera>();
|
||||
if (c != null) ci.fov = c.fieldOfView;
|
||||
var rc = cam.GetComponent<RealCamera>();
|
||||
if (rc != null)
|
||||
{
|
||||
var t = typeof(RealCamera);
|
||||
var fy = t.GetField("rotateAround", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
if (fy != null) ci.yaw = (float)fy.GetValue(rc);
|
||||
var fp = t.GetField("cameraPan", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
if (fp != null) ci.pan = (float)fp.GetValue(rc);
|
||||
}
|
||||
}
|
||||
var st = WL.CameraFraming.WLCameraFramingSettings.Instance;
|
||||
if (st != null && st.framingEnabled) { ci.h = st.cameraHeight; ci.dist = st.distance; ci.up = st.distanceUp; }
|
||||
|
||||
// RealCamera 궤도식 그대로: rotateVector = Quaternion.Euler(h, yaw, pan) * Vector3.one
|
||||
var rot = UnityEngine.Quaternion.Euler(ci.h, ci.yaw, ci.pan);
|
||||
var rv = rot * UnityEngine.Vector3.one;
|
||||
ci.camPos = UnityEngine.Vector3.up * ci.up - rv * ci.dist; // 타겟 = 원점
|
||||
ci.lookAt = UnityEngine.Vector3.zero;
|
||||
return ci;
|
||||
}
|
||||
|
||||
/// <summary>원점(PC 발밑)에 지름 dia · 높이 hgt 의 구/원기둥이 있을 때 세로 화면 점유율(0~1).</summary>
|
||||
static float ScreenFraction(CamInfo ci, float dia, float hgt, float anchorY)
|
||||
{
|
||||
var go = new UnityEngine.GameObject("__wl811s_cam");
|
||||
go.hideFlags = UnityEngine.HideFlags.HideAndDontSave;
|
||||
try
|
||||
{
|
||||
var cam = go.AddComponent<UnityEngine.Camera>();
|
||||
cam.enabled = false;
|
||||
cam.fieldOfView = ci.fov;
|
||||
cam.aspect = 1080f / 1920f;
|
||||
cam.nearClipPlane = 0.1f; cam.farClipPlane = 1000f;
|
||||
go.transform.position = ci.camPos;
|
||||
go.transform.LookAt(ci.lookAt);
|
||||
|
||||
float r = dia * 0.5f;
|
||||
float yc = anchorY;
|
||||
float minY = 1e9f, maxY = -1e9f;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
float a = i * (UnityEngine.Mathf.PI * 2f / 8f);
|
||||
float x = UnityEngine.Mathf.Cos(a) * r, z = UnityEngine.Mathf.Sin(a) * r;
|
||||
for (int k = 0; k < 2; k++)
|
||||
{
|
||||
float y = yc + (k == 0 ? -hgt * 0.5f : hgt * 0.5f);
|
||||
var vp = cam.WorldToViewportPoint(new UnityEngine.Vector3(x, y, z));
|
||||
if (vp.z <= 0f) continue;
|
||||
if (vp.y < minY) minY = vp.y;
|
||||
if (vp.y > maxY) maxY = vp.y;
|
||||
}
|
||||
}
|
||||
if (maxY < minY) return 0f;
|
||||
return maxY - minY;
|
||||
}
|
||||
finally { UnityEngine.Object.DestroyImmediate(go); }
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────── Measure 엔트리
|
||||
|
||||
public static string Measure()
|
||||
{
|
||||
s_sb.Length = 0; s_err.Clear(); Hook();
|
||||
L("=== WL-811s MEASURE · " + System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " (에디트 모드 · Play 0) ===");
|
||||
|
||||
// A
|
||||
L("");
|
||||
L("[A] 액터 높이 (프리팹 렌더러 바운드 · 원 크기 → × 테이블 배율)");
|
||||
L("kind\tname\trawH(m)\trawD(m)\ttableScale\t실높이(m)\t실지름(m)");
|
||||
var actors = MeasureActors();
|
||||
float pcH = 0f;
|
||||
for (int i = 0; i < actors.Count; i++)
|
||||
{
|
||||
var r = actors[i];
|
||||
if (!r.ok) { L(r.kind + "\t" + r.name + "\tMISSING"); continue; }
|
||||
float h = r.rawH * r.tableScale, d = r.rawD * r.tableScale;
|
||||
L(r.kind + "\t" + r.name + "\t" + r.rawH.ToString("F3") + "\t" + r.rawD.ToString("F3") + "\t" + r.tableScale.ToString("F2")
|
||||
+ "\t" + h.ToString("F3") + "\t" + d.ToString("F3"));
|
||||
if (r.kind == "PC" && pcH <= 0f) pcH = h;
|
||||
}
|
||||
float eliteMul = 1.3f;
|
||||
var est = WL.Combat.Reaction.WLEliteSettings.Instance;
|
||||
if (est != null) eliteMul = est.scaleMultiplier;
|
||||
L("엘리트 배수(WLEliteSettings.scaleMultiplier) = " + eliteMul.ToString("F2") + " → 위 「엘리트*」 실높이 × " + eliteMul.ToString("F2"));
|
||||
L("PC 기준 높이 = " + pcH.ToString("F3") + " m");
|
||||
|
||||
// B
|
||||
L("");
|
||||
L("[B] 이펙트 원 크기 (프리팹 localScale 포함 · Simulate t 스윕 · 파티클 실좌표 GetParticles)");
|
||||
L("prefab\tbaseScale\tps\tpMax\tdiaP90(m)\tdiaMax(m)\thgt(m)\tnote");
|
||||
string[] names = EffectNames();
|
||||
var dict = new System.Collections.Generic.Dictionary<string, float>();
|
||||
for (int i = 0; i < names.Length; i++)
|
||||
{
|
||||
float diaMax, diaP90, hgt, bs; int ps, pc; string note;
|
||||
MeasureEffect(names[i], out diaMax, out diaP90, out hgt, out bs, out ps, out pc, out note);
|
||||
dict[names[i]] = diaP90;
|
||||
L(names[i] + "\t" + bs.ToString("F3") + "\t" + ps + "\t" + pc + "\t" + diaP90.ToString("F3") + "\t" + diaMax.ToString("F3") + "\t" + hgt.ToString("F3") + "\t" + note);
|
||||
}
|
||||
|
||||
// C
|
||||
L("");
|
||||
var ci = BuildCam();
|
||||
L("[C] 카메라 (Main Camera.prefab fov " + ci.fov.ToString("F1") + " · RealCamera yaw " + ci.yaw.ToString("F2") + " pan " + ci.pan.ToString("F2")
|
||||
+ " · 811o 오버라이드 h " + ci.h.ToString("F1") + " d " + ci.dist.ToString("F2") + " up " + ci.up.ToString("F2") + ")");
|
||||
L("camPos(타겟 원점 기준) = " + ci.camPos.ToString("F3") + " · 거리 " + ci.camPos.magnitude.ToString("F3")
|
||||
+ " m · 내림각 " + (UnityEngine.Mathf.Asin(UnityEngine.Mathf.Clamp(ci.camPos.y / UnityEngine.Mathf.Max(0.0001f, ci.camPos.magnitude), -1f, 1f)) * UnityEngine.Mathf.Rad2Deg).ToString("F1") + "°");
|
||||
L("세로 1080×1920 (aspect " + (1080f / 1920f).ToString("F4") + ") · 1 m 구가 PC 몸 중심(y=" + (pcH * 0.5f).ToString("F2") + ")에 있을 때 세로 점유 = "
|
||||
+ (ScreenFraction(ci, 1f, 1f, pcH * 0.5f) * 100f).ToString("F1") + " %");
|
||||
|
||||
L("");
|
||||
L("[C2] 현재 16행 슬롯 4 본체의 화면 세로 점유 (현재 scale · 앵커 Mid = PC 몸 중심)");
|
||||
L("skill\tprefab\tscale\tdia0(m)\tdia*(m)\t세로점유%");
|
||||
var so = WL.Combat.Reaction.WLSkillSpectacleSettings.Instance;
|
||||
if (so != null && so.rows != null)
|
||||
{
|
||||
for (int i = 0; i < so.rows.Length; i++)
|
||||
{
|
||||
var row = so.rows[i];
|
||||
if (row.slot != 4) continue;
|
||||
string p = row.body.prefab;
|
||||
float d0 = 0f; dict.TryGetValue(p, out d0);
|
||||
float sc = row.body.scale > 0f ? row.body.scale : 1f;
|
||||
float d = d0 * sc;
|
||||
L(row.skillId + "\t" + p + "\t" + sc.ToString("F2") + "\t" + d0.ToString("F3") + "\t" + d.ToString("F3") + "\t"
|
||||
+ (ScreenFraction(ci, d, d, pcH * 0.5f) * 100f).ToString("F1"));
|
||||
}
|
||||
}
|
||||
else L("WLSkillSpectacleSettings 로드 실패");
|
||||
|
||||
L("");
|
||||
L("콘솔 error/exception = " + s_err.Count);
|
||||
for (int i = 0; i < s_err.Count && i < 10; i++) L(" " + s_err[i]);
|
||||
Unhook();
|
||||
Save(kMeasureOut);
|
||||
return "MEASURE done · rows=" + actors.Count + " effects=" + names.Length + " err=" + s_err.Count + " → " + kMeasureOut;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────── Verify 엔트리
|
||||
|
||||
// 발주 전 16행 값(main @ 22832ff5d 실측) — (skillId, charge, body, impact) · −1 = 그 단 없음
|
||||
static float[][] Before()
|
||||
{
|
||||
return new float[][] {
|
||||
new float[]{703101,-1f,1.2f,1.0f}, new float[]{703102,1.0f,1.3f,1.2f},
|
||||
new float[]{703103,1.0f,1.4f,1.0f}, new float[]{702006,1.2f,1.5f,1.2f},
|
||||
new float[]{703106,-1f,1.2f,1.0f}, new float[]{703105,1.0f,1.3f,1.0f},
|
||||
new float[]{703104,1.0f,1.35f,-1f}, new float[]{702009,1.2f,1.6f,1.2f},
|
||||
new float[]{703502,-1f,1.2f,1.0f}, new float[]{703503,1.0f,1.4f,1.0f},
|
||||
new float[]{703501,-1f,1.3f,-1f}, new float[]{702005,1.2f,1.8f,1.2f},
|
||||
new float[]{703505,-1f,1.2f,1.0f}, new float[]{703506,-1f,1.5f,1.0f},
|
||||
new float[]{703504,1.0f,1.3f,-1f}, new float[]{702012,1.2f,1.5f,1.2f},
|
||||
};
|
||||
}
|
||||
|
||||
static UnityEngine.GameObject s_fakeInfoGo, s_poolRoot;
|
||||
static InGameInfo s_prevInfo;
|
||||
|
||||
static void PoolBegin()
|
||||
{
|
||||
s_prevInfo = InGameInfo.Ins;
|
||||
s_fakeInfoGo = new UnityEngine.GameObject("__wl811s_info");
|
||||
s_fakeInfoGo.hideFlags = UnityEngine.HideFlags.HideAndDontSave;
|
||||
s_poolRoot = new UnityEngine.GameObject("__wl811s_pool");
|
||||
s_poolRoot.hideFlags = UnityEngine.HideFlags.HideAndDontSave;
|
||||
var info = s_fakeInfoGo.AddComponent<InGameInfo>(); // 에디트 모드라 Awake 미실행 = 부작용 0
|
||||
info.tf_Effects = s_poolRoot.transform;
|
||||
InGameInfo.Ins = info;
|
||||
}
|
||||
static void PoolEnd()
|
||||
{
|
||||
InGameInfo.Ins = s_prevInfo;
|
||||
if (s_poolRoot != null) UnityEngine.Object.DestroyImmediate(s_poolRoot);
|
||||
if (s_fakeInfoGo != null) UnityEngine.Object.DestroyImmediate(s_fakeInfoGo);
|
||||
s_poolRoot = null; s_fakeInfoGo = null;
|
||||
}
|
||||
|
||||
static UnityEngine.GameObject PoolChild(string prefabName, bool active)
|
||||
{
|
||||
var pf = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.GameObject>(kEffectDir + prefabName + ".prefab");
|
||||
if (pf == null) return null;
|
||||
var go = UnityEngine.Object.Instantiate(pf, s_poolRoot.transform);
|
||||
go.name = prefabName + "(Clone)"; // 원본 풀과 같은 이름 규약
|
||||
go.hideFlags = UnityEngine.HideFlags.HideAndDontSave;
|
||||
go.transform.localScale = pf.transform.localScale;
|
||||
go.SetActive(active);
|
||||
return go;
|
||||
}
|
||||
|
||||
static float s_tick;
|
||||
static void Tick() { s_tick += 1f; WL.Combat.Reaction.EffectScaleGuard.Tick(s_tick); }
|
||||
|
||||
static int s_pass, s_fail;
|
||||
static void Chk(string name, bool ok, string detail)
|
||||
{
|
||||
if (ok) s_pass++; else s_fail++;
|
||||
L((ok ? "PASS " : "🔴FAIL ") + name + " · " + detail);
|
||||
}
|
||||
static bool Near(float a, float b, float eps) { return UnityEngine.Mathf.Abs(a - b) <= eps; }
|
||||
|
||||
public static string Verify()
|
||||
{
|
||||
s_sb.Length = 0; s_err.Clear(); s_pass = 0; s_fail = 0; Hook();
|
||||
L("=== WL-811s VERIFY · " + System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " (에디트 모드 · Play 0) ===");
|
||||
|
||||
UnityEditor.AssetDatabase.Refresh(); // 디스크에서 손으로 고친 .asset 을 다시 읽는다
|
||||
UnityEngine.Resources.UnloadUnusedAssets();
|
||||
WL.Combat.Reaction.WLSkillSpectacleSettings.ClearCache();
|
||||
WL.Combat.Reaction.WLEffectScaleSettings.ClearCache();
|
||||
var sk = WL.Combat.Reaction.WLSkillSpectacleSettings.Instance;
|
||||
var es = WL.Combat.Reaction.WLEffectScaleSettings.Instance;
|
||||
Chk("SO 로드", sk != null && es != null, "spectacle=" + (sk != null) + " effectScale=" + (es != null) + " rules=" + (es != null && es.rules != null ? es.rules.Length : -1));
|
||||
|
||||
// G. guid
|
||||
L("");
|
||||
L("[G] 새 에셋 guid → 경로(충돌 0 확인)");
|
||||
string[][] gp = new string[][] {
|
||||
new string[]{"8115a0c3e7d24b6f9a1c8e5d3b7f2049","Assets/WL/Combat/Reaction/WLEffectScaleSettings.cs"},
|
||||
new string[]{"8115b1d4f8e35c709b2d9f6e4c803150","Assets/WL/Combat/Reaction/EffectScaleGuard.cs"},
|
||||
new string[]{"8115c2e509f46d81ac3ea07f5d914261","Assets/WL/Combat/Reaction/EffectScaleGuardRunner.cs"},
|
||||
new string[]{"8115d3f61a057e92bd4fb1806ea25372","Assets/WL/Combat/Settings/Resources/WL/WLEffectScaleSettings.asset"},
|
||||
};
|
||||
for (int i = 0; i < gp.Length; i++)
|
||||
{
|
||||
string got = UnityEditor.AssetDatabase.GUIDToAssetPath(gp[i][0]);
|
||||
Chk("guid " + gp[i][0].Substring(0, 8), got == gp[i][1], got);
|
||||
}
|
||||
|
||||
// D. 16행 전/후 · 화면 점유
|
||||
var ci = BuildCam();
|
||||
float pcH = 1.191f;
|
||||
// 세로 50 % 가 되는 지름(이분 탐색 · 원 산식 그대로)
|
||||
float lo = 0.1f, hi = 20f;
|
||||
for (int i = 0; i < 40; i++) { float mid = (lo + hi) * 0.5f; if (ScreenFraction(ci, mid, mid, pcH * 0.5f) < 0.5f) lo = mid; else hi = mid; }
|
||||
float capDia = (lo + hi) * 0.5f;
|
||||
L("");
|
||||
L("[D] 화면 점유 (fov " + ci.fov.ToString("F0") + " · 카메라 거리 " + ci.camPos.magnitude.ToString("F2") + " m · 1080×1920)");
|
||||
L("세로 50 % 가 되는 지름 = " + capDia.ToString("F3") + " m (= 슬롯 4 본체 캡)");
|
||||
|
||||
var before = Before();
|
||||
L("");
|
||||
L("[D2] 16행 전/후 (scale · dia0 = 프리팹 원 지름 실측 · after = dia0 × new)");
|
||||
L("skill\tslot\tlabel\tphase\tprefab\tdia0\tbefore\tafter\t지름before\t지름after\t세로%before\t세로%after");
|
||||
var dia = new System.Collections.Generic.Dictionary<string, float>();
|
||||
for (int i = 0; sk != null && sk.rows != null && i < sk.rows.Length; i++)
|
||||
{
|
||||
var row = sk.rows[i];
|
||||
float[] b = null;
|
||||
for (int k = 0; k < before.Length; k++) if ((int)before[k][0] == row.skillId) { b = before[k]; break; }
|
||||
for (int ph = 0; ph < 3; ph++)
|
||||
{
|
||||
var v = ph == 0 ? row.charge : (ph == 1 ? row.body : row.impact);
|
||||
if (string.IsNullOrEmpty(v.prefab)) continue;
|
||||
float d0;
|
||||
if (!dia.TryGetValue(v.prefab, out d0))
|
||||
{
|
||||
float dm, dp, hg, bs; int ps, pc; string nt;
|
||||
MeasureEffect(v.prefab, out dm, out dp, out hg, out bs, out ps, out pc, out nt);
|
||||
d0 = dp; dia[v.prefab] = d0;
|
||||
}
|
||||
float ob = b != null ? b[ph + 1] : -1f;
|
||||
float ns = v.scale > 0f ? v.scale : 1f;
|
||||
float anchor = v.anchor == eEffectLocation.Bottom ? 0f : (v.anchor == eEffectLocation.Top ? pcH : pcH * 0.5f);
|
||||
L(row.skillId + "\t" + row.slot + "\t" + row.label + "\t" + (ph == 0 ? "charge" : (ph == 1 ? "body" : "impact")) + "\t" + v.prefab
|
||||
+ "\t" + d0.ToString("F3") + "\t" + (ob < 0f ? "?" : ob.ToString("F3")) + "\t" + ns.ToString("F3")
|
||||
+ "\t" + (ob < 0f ? "?" : (d0 * ob).ToString("F3")) + "\t" + (d0 * ns).ToString("F3")
|
||||
+ "\t" + (ob < 0f ? "?" : (ScreenFraction(ci, d0 * ob, d0 * ob, anchor) * 100f).ToString("F1"))
|
||||
+ "\t" + (ScreenFraction(ci, d0 * ns, d0 * ns, anchor) * 100f).ToString("F1"));
|
||||
}
|
||||
}
|
||||
|
||||
L("");
|
||||
L("[D3] 슬롯 4 본체 캡 판정 (≤ 50 %)");
|
||||
for (int i = 0; sk != null && sk.rows != null && i < sk.rows.Length; i++)
|
||||
{
|
||||
var row = sk.rows[i];
|
||||
if (row.slot != 4 || string.IsNullOrEmpty(row.body.prefab)) continue;
|
||||
float d0 = dia.ContainsKey(row.body.prefab) ? dia[row.body.prefab] : 0f;
|
||||
float d = d0 * (row.body.scale > 0f ? row.body.scale : 1f);
|
||||
float f = ScreenFraction(ci, d, d, pcH * 0.5f);
|
||||
Chk("슬롯4 " + row.skillId + " " + row.body.prefab, f <= 0.5f + 0.005f,
|
||||
"지름 " + d.ToString("F3") + " m · 세로 " + (f * 100f).ToString("F1") + " %");
|
||||
}
|
||||
|
||||
// E. 가드
|
||||
L("");
|
||||
L("[E] EffectScaleGuard (가짜 풀 루트 = InGameInfo.tf_Effects · 진짜 이펙트 프리팹 인스턴스)");
|
||||
WL.Combat.Reaction.EffectScaleGuard.ResetDiagnostics();
|
||||
WL.Combat.Reaction.WLEffectScaleSettings.RuntimeOverride = 1;
|
||||
bool prevByHeight = es != null && es.scaleByTargetHeight;
|
||||
if (es != null) es.scaleByTargetHeight = false;
|
||||
PoolBegin();
|
||||
try
|
||||
{
|
||||
var stun = PoolChild("Effect_Stun", true);
|
||||
var hit = PoolChild("Effect_Hit_10101", true);
|
||||
var none = PoolChild("Effect_AnubisCharging", true); // 보스 패턴 = 규칙 없음 = 원본 유지
|
||||
var poison = PoolChild("Effect_Poison", false); // 비활성으로 시작
|
||||
var origStun = stun.transform.localScale;
|
||||
var origHit = hit.transform.localScale;
|
||||
var origNone = none.transform.localScale;
|
||||
var origPoison = poison.transform.localScale;
|
||||
|
||||
int rStun = es.FindRule("Effect_Stun(Clone)", WL.Combat.Reaction.WLEffectScaleSettings.CoreLength("Effect_Stun(Clone)"));
|
||||
int rNone = es.FindRule("Effect_AnubisCharging(Clone)", WL.Combat.Reaction.WLEffectScaleSettings.CoreLength("Effect_AnubisCharging(Clone)"));
|
||||
Chk("규칙 매칭", rStun >= 0 && rNone < 0, "Effect_Stun → rule " + rStun + " (scale " + (rStun >= 0 ? es.rules[rStun].scale.ToString("F3") : "-") + ") · Effect_AnubisCharging → " + rNone + "(없음 = 원본)");
|
||||
|
||||
Tick();
|
||||
float fStun = es.rules[rStun].scale;
|
||||
Chk("① 활성 인스턴스 배율 적용", Near(stun.transform.localScale.x, origStun.x * fStun, 0.0005f),
|
||||
"Effect_Stun " + origStun.x.ToString("F3") + " → " + stun.transform.localScale.x.ToString("F4") + " (기대 " + (origStun.x * fStun).ToString("F4") + ")");
|
||||
Chk("② 규칙 없는 것은 무변경", Near(none.transform.localScale.x, origNone.x, 0.0001f),
|
||||
"Effect_AnubisCharging " + none.transform.localScale.x.ToString("F4") + " (원본 " + origNone.x.ToString("F4") + ")");
|
||||
Chk("③ 비활성은 아직 미적용", Near(poison.transform.localScale.x, origPoison.x, 0.0001f), "Effect_Poison " + poison.transform.localScale.x.ToString("F4"));
|
||||
|
||||
int ap1 = WL.Combat.Reaction.EffectScaleGuard.AppliedCount;
|
||||
var afterFirst = stun.transform.localScale;
|
||||
Tick(); Tick(); Tick();
|
||||
Chk("④ 멱등(3회 더 틱)", Near(stun.transform.localScale.x, afterFirst.x, 0.000001f) && WL.Combat.Reaction.EffectScaleGuard.AppliedCount == ap1,
|
||||
"scale " + stun.transform.localScale.x.ToString("F6") + " · Applied " + ap1 + " → " + WL.Combat.Reaction.EffectScaleGuard.AppliedCount);
|
||||
|
||||
stun.SetActive(false); Tick();
|
||||
Chk("⑤ 반납(SetActive false) → 원 스케일 복귀", Near(stun.transform.localScale.x, origStun.x, 0.0001f),
|
||||
stun.transform.localScale.x.ToString("F4") + " (원본 " + origStun.x.ToString("F4") + ")");
|
||||
|
||||
stun.SetActive(true); Tick();
|
||||
Chk("⑥ 재활성 → 다시 적용", Near(stun.transform.localScale.x, origStun.x * fStun, 0.0005f), stun.transform.localScale.x.ToString("F4"));
|
||||
|
||||
poison.SetActive(true); Tick();
|
||||
int rP = es.FindRule("Effect_Poison(Clone)", WL.Combat.Reaction.WLEffectScaleSettings.CoreLength("Effect_Poison(Clone)"));
|
||||
Chk("⑦ 나중에 활성된 인스턴스도 적용", rP >= 0 && Near(poison.transform.localScale.x, origPoison.x * es.rules[rP].scale, 0.0005f),
|
||||
"Effect_Poison → " + poison.transform.localScale.x.ToString("F4"));
|
||||
|
||||
// 대상 높이 옵션
|
||||
L("");
|
||||
L("[E2] scaleByTargetHeight (대상 액터 실높이로 재계산)");
|
||||
var mobPf = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.GameObject>("Assets/Res_Addr/Mobs/Mob/Batty_A.prefab");
|
||||
var mobGo = mobPf != null ? UnityEngine.Object.Instantiate(mobPf) : null;
|
||||
if (mobGo != null)
|
||||
{
|
||||
mobGo.hideFlags = UnityEngine.HideFlags.HideAndDontSave;
|
||||
mobGo.transform.position = UnityEngine.Vector3.zero;
|
||||
var actor = mobGo.GetComponent<Actor>();
|
||||
var fiTop = typeof(Actor).GetField("tf_Top", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
|
||||
var fiT2 = typeof(TurnOff_GO).GetField("m_target2", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
|
||||
Chk("리플렉션 필드 존재", actor != null && fiTop != null && fiT2 != null,
|
||||
"Actor=" + (actor != null) + " Actor.tf_Top=" + (fiTop != null) + " TurnOff_GO.m_target2=" + (fiT2 != null));
|
||||
if (actor != null && fiTop != null && fiT2 != null)
|
||||
{
|
||||
var topGo = new UnityEngine.GameObject("HUD_Top"); topGo.hideFlags = UnityEngine.HideFlags.HideAndDontSave;
|
||||
topGo.transform.SetParent(mobGo.transform, false);
|
||||
topGo.transform.position = new UnityEngine.Vector3(0f, 0.537f, 0f); // 존1 Batty 실높이(MEASURE [A])
|
||||
fiTop.SetValue(actor, topGo.transform);
|
||||
var off = stun.GetComponent<TurnOff_GO>();
|
||||
Chk("Effect_Stun 에 TurnOff_GO 존재", off != null, off != null ? "ok" : "없음");
|
||||
if (off != null)
|
||||
{
|
||||
fiT2.SetValue(off, actor);
|
||||
es.scaleByTargetHeight = true;
|
||||
stun.SetActive(false); Tick(); stun.SetActive(true); Tick();
|
||||
float expect = UnityEngine.Mathf.Clamp((0.537f * es.rules[rStun].targetRatio) / es.rules[rStun].baseDiameter, es.minScale, es.maxScale);
|
||||
Chk("⑧ 대상 높이 기반 재계산", Near(stun.transform.localScale.x, origStun.x * expect, 0.0005f),
|
||||
"몹 높이 0.537 m × " + es.rules[rStun].targetRatio + " ÷ " + es.rules[rStun].baseDiameter + " = " + expect.ToString("F4")
|
||||
+ " → scale " + stun.transform.localScale.x.ToString("F4") + " · 지름 " + (es.rules[rStun].baseDiameter * expect).ToString("F3") + " m"
|
||||
+ " · HeightResolved " + WL.Combat.Reaction.EffectScaleGuard.HeightResolved);
|
||||
fiT2.SetValue(off, null);
|
||||
int miss0 = WL.Combat.Reaction.EffectScaleGuard.HeightMissed;
|
||||
stun.SetActive(false); Tick(); stun.SetActive(true); Tick();
|
||||
Chk("⑨ 대상 없으면 고정 배율로 폴백", Near(stun.transform.localScale.x, origStun.x * fStun, 0.0005f) && WL.Combat.Reaction.EffectScaleGuard.HeightMissed > miss0,
|
||||
"scale " + stun.transform.localScale.x.ToString("F4") + " · HeightMissed " + miss0 + " → " + WL.Combat.Reaction.EffectScaleGuard.HeightMissed);
|
||||
es.scaleByTargetHeight = false;
|
||||
}
|
||||
}
|
||||
UnityEngine.Object.DestroyImmediate(mobGo);
|
||||
}
|
||||
else Chk("몹 프리팹 로드", false, "Batty_A 없음");
|
||||
|
||||
// F. GC
|
||||
L("");
|
||||
L("[F] GC · C8");
|
||||
stun.SetActive(true); hit.SetActive(true); Tick();
|
||||
long m0 = System.GC.GetAllocatedBytesForCurrentThread();
|
||||
long mono0 = UnityEngine.Profiling.Profiler.GetMonoUsedSizeLong();
|
||||
for (int i = 0; i < 200000; i++) Tick();
|
||||
long dAlloc = System.GC.GetAllocatedBytesForCurrentThread() - m0;
|
||||
long dMono = UnityEngine.Profiling.Profiler.GetMonoUsedSizeLong() - mono0;
|
||||
Chk("⑩ GC 0 (Tick ×200,000 · 워밍 후)", dAlloc == 0, "GetAllocatedBytesForCurrentThread Δ " + dAlloc + " B · MonoUsed Δ " + dMono + " B");
|
||||
|
||||
// C8
|
||||
WL.Combat.Reaction.WLEffectScaleSettings.RuntimeOverride = -1;
|
||||
Tick();
|
||||
Chk("⑪ C8 (enabled = 0) → 전량 원복", Near(stun.transform.localScale.x, origStun.x, 0.0001f) && Near(hit.transform.localScale.x, origHit.x, 0.0001f)
|
||||
&& WL.Combat.Reaction.EffectScaleGuard.ActiveScaled == 0,
|
||||
"stun " + stun.transform.localScale.x.ToString("F4") + " · hit " + hit.transform.localScale.x.ToString("F4") + " · ActiveScaled " + WL.Combat.Reaction.EffectScaleGuard.ActiveScaled);
|
||||
int ap2 = WL.Combat.Reaction.EffectScaleGuard.AppliedCount;
|
||||
Tick(); Tick();
|
||||
Chk("⑫ C8 상태에서 무동작", WL.Combat.Reaction.EffectScaleGuard.AppliedCount == ap2, "Applied " + ap2 + " → " + WL.Combat.Reaction.EffectScaleGuard.AppliedCount);
|
||||
|
||||
L("카운터: Sweep " + WL.Combat.Reaction.EffectScaleGuard.SweepCount + " · Tracked " + WL.Combat.Reaction.EffectScaleGuard.TrackedCount
|
||||
+ " · Ruled " + WL.Combat.Reaction.EffectScaleGuard.RuledCount + " · Applied " + WL.Combat.Reaction.EffectScaleGuard.AppliedCount
|
||||
+ " · Restored " + WL.Combat.Reaction.EffectScaleGuard.RestoredCount + " · Skipped " + WL.Combat.Reaction.EffectScaleGuard.SkippedCount);
|
||||
}
|
||||
finally
|
||||
{
|
||||
WL.Combat.Reaction.WLEffectScaleSettings.RuntimeOverride = 0;
|
||||
if (es != null) es.scaleByTargetHeight = prevByHeight;
|
||||
WL.Combat.Reaction.EffectScaleGuard.ResetDiagnostics();
|
||||
PoolEnd();
|
||||
}
|
||||
|
||||
L("");
|
||||
L("RESULT " + (s_fail == 0 ? "PASS" : "FAIL") + " " + s_pass + "/" + (s_pass + s_fail) + " · 콘솔 error/exception = " + s_err.Count);
|
||||
for (int i = 0; i < s_err.Count && i < 10; i++) L(" " + s_err[i]);
|
||||
Unhook();
|
||||
Save(kVerifyOut);
|
||||
return "VERIFY " + (s_fail == 0 ? "PASS" : "FAIL") + " " + s_pass + "/" + (s_pass + s_fail) + " err=" + s_err.Count + " → " + kVerifyOut;
|
||||
}
|
||||
|
||||
static string[] EffectNames()
|
||||
{
|
||||
return new string[]
|
||||
{
|
||||
// 16행(811i 매핑) — 차지/본체/착탄
|
||||
"Effect_Berserk","Effect_PCDash","Effect_Howling","Effect_Casting_Energyball","Effect_Casting_ManaShield","Effect_Casting_Meteor",
|
||||
"Effect_Slash_10101_1","Effect_Slash_10101_2","Effect_Slash_10101_3","Effect_WLSwingArc",
|
||||
"Effect_Slash_10102_1","Effect_Slash_10102_2","Effect_SwordShield","Effect_STR_EXTRA_SPLASH",
|
||||
"Effect_Slash_10501_1","Effect_Slash_10501_2","Effect_Slash_10501_3","Effect_Heal",
|
||||
"Effect_Slash_10502_1","Effect_Slash_10502_2","Effect_Aggro","ShineBulletFlash",
|
||||
"Effect_Hit_10101","Effect_Hit_10102","Effect_Hit_10501","Effect_Hit_SweepAttack","Effect_Hit_Skill_EnergySlashFlash",
|
||||
"Effect_Hit_Energyball","WindBullet_Hit","WaveShotHit","ShineBulletHit","Effect_Hit_Skill_CrossSlash","Effect_Hit_Skill_SpinSlash",
|
||||
// 원본 풀 상태이상
|
||||
"Effect_Stun","Effect_Poison","Effect_Burn","Effect_Blind","Effect_Curse","Effect_Slow","Effect_Freezing","Effect_Frost",
|
||||
"Effect_HPBurn","Effect_ManaBurn","Effect_SilenceCurse","Effect_Blood","Effect_DefenceDecrease","Effect_Barrier","Effect_ManaShield",
|
||||
"Effect_Cure","Effect_SpeedUp","Effect_StatUp","Effect_Reincarnation","Effect_VampirHeal",
|
||||
// 원본 풀 히트(클래스 · 몹 · 보스)
|
||||
"Effect_Hit_10201","Effect_Hit_10202","Effect_Hit_10301","Effect_Hit_10302","Effect_Hit_10303","Effect_Hit_10401","Effect_Hit_10402",
|
||||
"Effect_Hit_10502","Effect_Hit_10601","Effect_Hit_IceBolt","Effect_Hit_Fireball","Effect_Hit_Firebolt","Effect_Hit_Electronic",
|
||||
"Effect_Hit_Bead","Effect_Hit_BlackHole","Effect_Hit_AnubisSkill1","Effect_Hit_AnubisSkill2",
|
||||
"Effect_Hit_BrotherPenguin_Attack","Effect_Hit_FlowerDryad_Attack","Effect_Hit_IceGolem_Attack","Effect_Hit_Shaga_Attack",
|
||||
"Effect_Hit_Mob_HellfireFlames","Effect_Hit_RookieArcherSkill1","Effect_Hit_SummonGolem","Effect_Hit_SummonWorm",
|
||||
"Effect_Hit_Skill_DaggerThrow","Effect_Hit_Skill_Frozen"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,336 @@
|
|||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// EffectScaleGuard.cs — 원본 풀이 스폰하는 상태이상·히트 이펙트의 크기 가드(PD 지시 #813 · 발주서 WL-811s §1-3)
|
||||
//
|
||||
// ■ 무엇을 하나
|
||||
// 원본 `InGameInfo.Show_Effect` 가 쓰는 이펙트 풀(`dic_str_Effect` · 전부 `tf_Effects` 의 직속 자식)을
|
||||
// LateUpdate 에서 훑어, **활성화된 인스턴스**에 규칙 배율을 적용한다.
|
||||
// 적용 = `localScale = 원 스케일 × 배율` **대입**(누적 곱이 아니다 → 몇 번 불려도 같은 값 = 멱등)
|
||||
// 원복 = 원본 `TurnOff_GO.Off_Imm` 이 `SetActive(false)` 로 반납하면 원 스케일 대입
|
||||
//
|
||||
// ■ 왜 스폰 훅이 아니라 풀 스윕인가 (813m3 가 같은 이유로 같은 방식을 썼다)
|
||||
// `Effect_Stun`(Actor.cs:1793) · `Effect_Burn`(Skill_Golem.cs:54) · SkillTypeConfig 의 상태이상 ·
|
||||
// `ProjectileBase`(:365) 의 히트 이펙트는 **전부 원본 코드가 스폰**한다. 원본은 1줄도 고칠 수 없으므로
|
||||
// (발주서 §2) 스폰 지점이 아니라 **풀 루트**를 본다. WL 스폰 지점용 공개 API `Apply(GameObject)` 도 함께 둔다.
|
||||
//
|
||||
// ■ 왜 LateUpdate 인가
|
||||
// 원본 스폰은 Update·코루틴에서 일어난다. LateUpdate 는 그 뒤 · 렌더 앞이라 **활성화된 그 프레임에** 적용된다
|
||||
// (Update 에서 돌면 스폰 순서에 따라 1프레임 원본 크기로 그려질 수 있다 = 과대 이펙트가 한 번 번쩍인다).
|
||||
//
|
||||
// ■ GC 0
|
||||
// `Transform.GetChild` · `GetInstanceID` · `activeSelf` · `Dictionary<int,int>` 조회는 전부 비할당.
|
||||
// 유일한 할당 = **처음 보는 인스턴스 1개당 `GameObject.name` 1회**(풀은 인스턴스를 재사용하므로 워밍 후 0 B).
|
||||
//
|
||||
// ■ C8 롤백: `WLEffectScaleSettings.enabled = 0`(또는 RuntimeOverride < 0) → 다음 틱에 전량 원복 후 무동작.
|
||||
//
|
||||
// 🔴 원본 프리팹/머티리얼/셰이더 무수정 — 런타임 인스턴스의 localScale 만 만진다.
|
||||
// 🔴 Debug.LogError/LogException 금지(813z).
|
||||
// 🔴 어셈블리 주의: Assets/WL/Combat/ 에 .asmdef 를 만들지 말 것.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace WL.Combat.Reaction
|
||||
{
|
||||
/// <summary>원본 이펙트 풀 인스턴스에 크기 배율을 걸고 반납 시 되돌린다. 정적 · 멱등 · GC 0.</summary>
|
||||
public static class EffectScaleGuard
|
||||
{
|
||||
// ── 진단(프로브·QA 가 읽는다)
|
||||
/// <summary>스윕 횟수 · 추적 중인 인스턴스 수 · 규칙이 붙은 인스턴스 수.</summary>
|
||||
public static int SweepCount, TrackedCount, RuledCount;
|
||||
/// <summary>배율을 건 횟수 · 되돌린 횟수 · 슬롯 부족으로 건너뛴 수.</summary>
|
||||
public static int AppliedCount, RestoredCount, SkippedCount;
|
||||
/// <summary>현재 배율이 걸려 있는 인스턴스 수.</summary>
|
||||
public static int ActiveScaled;
|
||||
/// <summary>대상 액터 높이로 배율을 다시 계산한 횟수 · 대상을 못 찾아 고정 배율로 떨어진 횟수.</summary>
|
||||
public static int HeightResolved, HeightMissed;
|
||||
/// <summary>마지막으로 배율을 건 이름·배율(표본).</summary>
|
||||
public static string LastName = "";
|
||||
public static float LastFactor;
|
||||
|
||||
// ── 추적 슬롯(사전 할당 · 병렬 배열)
|
||||
static int[] s_id;
|
||||
static Transform[] s_tf;
|
||||
static Vector3[] s_orig;
|
||||
static float[] s_factor; // 0 = 규칙 없음(손대지 않는다)
|
||||
static int[] s_rule; // −1 = 규칙 없음
|
||||
static bool[] s_applied;
|
||||
static TurnOff_GO[] s_off;
|
||||
static bool[] s_offResolved;
|
||||
static int s_count;
|
||||
static int s_capacity;
|
||||
static readonly Dictionary<int, int> s_map = new Dictionary<int, int>(512);
|
||||
|
||||
static float s_nextSweep;
|
||||
static bool s_sceneHooked;
|
||||
|
||||
// 원본 private 필드 리플렉션(대상 높이 옵션 전용 · 캐시 1회 · 참조형이라 박싱 없음)
|
||||
static FieldInfo s_fiTarget2, s_fiTfTop;
|
||||
static bool s_reflectDone;
|
||||
|
||||
static WLEffectScaleSettings St { get { return WLEffectScaleSettings.Instance; } }
|
||||
|
||||
// ───────────────────────────────────────── 틱
|
||||
|
||||
/// <summary>러너가 LateUpdate 마다 부른다. C8·주기 판정은 여기서 한다.</summary>
|
||||
public static void Tick(float unscaledNow)
|
||||
{
|
||||
var st = St;
|
||||
if (st == null || !WLEffectScaleSettings.Enabled)
|
||||
{
|
||||
if (ActiveScaled > 0) RestoreAll(); // C8: 끄면 다음 틱에 전량 원복
|
||||
return;
|
||||
}
|
||||
if (unscaledNow < s_nextSweep) return;
|
||||
s_nextSweep = unscaledNow + Mathf.Max(0f, st.sweepIntervalSeconds);
|
||||
|
||||
EnsureCapacity(st);
|
||||
HookScene();
|
||||
SweepCount++;
|
||||
|
||||
int budget = Mathf.Max(1, st.maxNewPerSweep);
|
||||
|
||||
var info = InGameInfo.Ins;
|
||||
if (info != null)
|
||||
{
|
||||
if (st.sweepIngameEffects) budget = SweepRoot(info.tf_Effects, st, budget);
|
||||
if (st.sweepIngameObjs) budget = SweepRoot(info.tf_Objs, st, budget);
|
||||
}
|
||||
|
||||
if (st.sweepSkillSpectacle && SkillSpectacleRunner.Exists)
|
||||
{
|
||||
var runner = SkillSpectacleRunner.Ensure(); // Exists 가 true 라 새로 만들지 않는다
|
||||
if (runner != null)
|
||||
{
|
||||
budget = SweepRoot(runner.LiveRoot, st, budget);
|
||||
budget = SweepRoot(runner.PoolRoot, st, budget);
|
||||
}
|
||||
}
|
||||
|
||||
if (st.sweepMainPc)
|
||||
{
|
||||
var pc = MyValue.MyPC;
|
||||
if (pc != null) budget = SweepRoot(pc.transform, st, budget);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>루트의 직속 자식을 훑는다. 처음 보는 것은 등록(예산 소모)하고, 아는 것은 활성/비활성만 반영한다.</summary>
|
||||
static int SweepRoot(Transform root, WLEffectScaleSettings st, int budget)
|
||||
{
|
||||
if (root == null) return budget;
|
||||
int n = root.childCount;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
var child = root.GetChild(i);
|
||||
if (child == null) continue;
|
||||
int id = child.GetInstanceID();
|
||||
int slot;
|
||||
if (!s_map.TryGetValue(id, out slot))
|
||||
{
|
||||
if (budget <= 0) continue;
|
||||
budget--;
|
||||
slot = Register(child, id, st);
|
||||
if (slot < 0) continue;
|
||||
}
|
||||
UpdateSlot(slot, st);
|
||||
}
|
||||
return budget;
|
||||
}
|
||||
|
||||
/// <summary>처음 보는 인스턴스를 등록한다(이름 1회 읽기 = 유일한 할당). 슬롯이 없으면 −1.</summary>
|
||||
static int Register(Transform t, int id, WLEffectScaleSettings st)
|
||||
{
|
||||
if (s_count >= s_capacity)
|
||||
{
|
||||
Compact();
|
||||
if (s_count >= s_capacity) { SkippedCount++; return -1; }
|
||||
}
|
||||
int slot = s_count++;
|
||||
s_id[slot] = id;
|
||||
s_tf[slot] = t;
|
||||
s_orig[slot] = t.localScale;
|
||||
s_applied[slot] = false;
|
||||
s_off[slot] = null;
|
||||
s_offResolved[slot] = false;
|
||||
|
||||
string nm = t.name; // ← 인스턴스당 1회
|
||||
int core = WLEffectScaleSettings.CoreLength(nm);
|
||||
int r = st.FindRule(nm, core);
|
||||
s_rule[slot] = r;
|
||||
s_factor[slot] = r >= 0 ? Mathf.Max(0f, st.rules[r].scale) : 0f;
|
||||
if (r >= 0) RuledCount++;
|
||||
|
||||
s_map[id] = slot;
|
||||
TrackedCount = s_count;
|
||||
return slot;
|
||||
}
|
||||
|
||||
/// <summary>활성이면 배율을 대입하고 비활성이면 되돌린다(둘 다 멱등).</summary>
|
||||
static void UpdateSlot(int slot, WLEffectScaleSettings st)
|
||||
{
|
||||
var t = s_tf[slot];
|
||||
if (t == null) return;
|
||||
if (s_rule[slot] < 0) return; // 규칙 없음 = 원본 100 %
|
||||
|
||||
bool active = t.gameObject.activeSelf;
|
||||
if (active)
|
||||
{
|
||||
if (s_applied[slot]) return;
|
||||
float f = ResolveFactor(slot, st);
|
||||
if (f <= 0f) return;
|
||||
t.localScale = s_orig[slot] * f; // 대입(멱등) — 누적 곱이 아니다
|
||||
s_applied[slot] = true;
|
||||
AppliedCount++; ActiveScaled++;
|
||||
LastName = t.name; LastFactor = f;
|
||||
if (st.verboseLog) Debug.Log("[WL811s] effectScale " + LastName + " ×" + f.ToString("F3")); // 🔴 Log 만(813z)
|
||||
}
|
||||
else if (s_applied[slot])
|
||||
{
|
||||
if (st.restoreOnReturn) t.localScale = s_orig[slot];
|
||||
s_applied[slot] = false;
|
||||
RestoredCount++; if (ActiveScaled > 0) ActiveScaled--;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>고정 배율 · (옵션) 대상 액터 높이 기반 재계산.</summary>
|
||||
static float ResolveFactor(int slot, WLEffectScaleSettings st)
|
||||
{
|
||||
int r = s_rule[slot];
|
||||
float f = s_factor[slot];
|
||||
if (!st.scaleByTargetHeight || r < 0) return f;
|
||||
|
||||
var rule = st.rules[r];
|
||||
if (!rule.useTargetHeight || rule.baseDiameter <= 0f || rule.targetRatio <= 0f) return f;
|
||||
|
||||
float h;
|
||||
if (!TryTargetHeight(slot, out h) || h <= 0f) { HeightMissed++; return f; }
|
||||
HeightResolved++;
|
||||
float byHeight = (h * rule.targetRatio) / rule.baseDiameter;
|
||||
return Mathf.Clamp(byHeight, Mathf.Max(0.0001f, st.minScale), Mathf.Max(st.minScale, st.maxScale));
|
||||
}
|
||||
|
||||
/// <summary>이 이펙트가 따라다니는 대상 액터의 실높이(발밑→머리). 원본 private 필드라 리플렉션(캐시 1회).</summary>
|
||||
static bool TryTargetHeight(int slot, out float height)
|
||||
{
|
||||
height = 0f;
|
||||
if (!s_offResolved[slot])
|
||||
{
|
||||
var tf = s_tf[slot];
|
||||
s_off[slot] = tf != null ? tf.GetComponent<TurnOff_GO>() : null;
|
||||
s_offResolved[slot] = true;
|
||||
}
|
||||
var off = s_off[slot];
|
||||
if (off == null) return false;
|
||||
|
||||
if (!s_reflectDone)
|
||||
{
|
||||
s_reflectDone = true;
|
||||
s_fiTarget2 = typeof(TurnOff_GO).GetField("m_target2", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
s_fiTfTop = typeof(Actor).GetField("tf_Top", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
}
|
||||
if (s_fiTarget2 == null || s_fiTfTop == null) return false;
|
||||
|
||||
var actor = s_fiTarget2.GetValue(off) as Actor; // 참조형 필드 → 박싱 없음
|
||||
if (actor == null) return false;
|
||||
var top = s_fiTfTop.GetValue(actor) as Transform;
|
||||
if (top == null) return false;
|
||||
|
||||
height = top.position.y - actor.transform.position.y;
|
||||
return height > 0.01f;
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────── 공개 API · 정리
|
||||
|
||||
/// <summary>WL 스폰 지점용(후속 1줄 연결). 스폰 직후 부르면 그 인스턴스를 즉시 등록·적용한다.</summary>
|
||||
public static void Apply(GameObject go)
|
||||
{
|
||||
var st = St;
|
||||
if (st == null || !WLEffectScaleSettings.Enabled || go == null) return;
|
||||
EnsureCapacity(st);
|
||||
var t = go.transform;
|
||||
int id = t.GetInstanceID();
|
||||
int slot;
|
||||
if (!s_map.TryGetValue(id, out slot))
|
||||
{
|
||||
slot = Register(t, id, st);
|
||||
if (slot < 0) return;
|
||||
}
|
||||
UpdateSlot(slot, st);
|
||||
}
|
||||
|
||||
/// <summary>적용된 배율을 전부 되돌린다(C8 · 씬 전환 · 프로브).</summary>
|
||||
public static void RestoreAll()
|
||||
{
|
||||
for (int i = 0; i < s_count; i++)
|
||||
{
|
||||
if (!s_applied[i]) continue;
|
||||
var t = s_tf[i];
|
||||
if (t != null) t.localScale = s_orig[i];
|
||||
s_applied[i] = false;
|
||||
RestoredCount++;
|
||||
}
|
||||
ActiveScaled = 0;
|
||||
}
|
||||
|
||||
static void EnsureCapacity(WLEffectScaleSettings st)
|
||||
{
|
||||
int want = Mathf.Max(32, st.trackCapacity);
|
||||
if (s_id != null && s_capacity >= want) return;
|
||||
Array.Resize(ref s_id, want);
|
||||
Array.Resize(ref s_tf, want);
|
||||
Array.Resize(ref s_orig, want);
|
||||
Array.Resize(ref s_factor, want);
|
||||
Array.Resize(ref s_rule, want);
|
||||
Array.Resize(ref s_applied, want);
|
||||
Array.Resize(ref s_off, want);
|
||||
Array.Resize(ref s_offResolved, want);
|
||||
s_capacity = want;
|
||||
}
|
||||
|
||||
/// <summary>파괴된 인스턴스 슬롯을 정리한다(슬롯이 꽉 찼을 때 · 씬 전환 뒤).</summary>
|
||||
static void Compact()
|
||||
{
|
||||
int w = 0;
|
||||
for (int i = 0; i < s_count; i++)
|
||||
{
|
||||
if (s_tf[i] == null) continue;
|
||||
if (w != i)
|
||||
{
|
||||
s_id[w] = s_id[i]; s_tf[w] = s_tf[i]; s_orig[w] = s_orig[i];
|
||||
s_factor[w] = s_factor[i]; s_rule[w] = s_rule[i]; s_applied[w] = s_applied[i];
|
||||
s_off[w] = s_off[i]; s_offResolved[w] = s_offResolved[i];
|
||||
}
|
||||
w++;
|
||||
}
|
||||
s_count = w;
|
||||
s_map.Clear();
|
||||
for (int i = 0; i < s_count; i++) s_map[s_id[i]] = i;
|
||||
TrackedCount = s_count;
|
||||
}
|
||||
|
||||
static void HookScene()
|
||||
{
|
||||
if (s_sceneHooked) return;
|
||||
s_sceneHooked = true;
|
||||
UnityEngine.SceneManagement.SceneManager.activeSceneChanged += OnSceneChanged;
|
||||
}
|
||||
|
||||
static void OnSceneChanged(UnityEngine.SceneManagement.Scene a, UnityEngine.SceneManagement.Scene b)
|
||||
{
|
||||
s_map.Clear(); s_count = 0; TrackedCount = 0; ActiveScaled = 0;
|
||||
}
|
||||
|
||||
/// <summary>프로브용: 카운터·추적을 비운다(적용분은 먼저 원복한다).</summary>
|
||||
public static void ResetDiagnostics()
|
||||
{
|
||||
RestoreAll();
|
||||
s_map.Clear(); s_count = 0; s_nextSweep = 0f;
|
||||
SweepCount = TrackedCount = RuledCount = 0;
|
||||
AppliedCount = RestoredCount = SkippedCount = ActiveScaled = 0;
|
||||
HeightResolved = HeightMissed = 0;
|
||||
LastName = ""; LastFactor = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8115b1d4f8e35c709b2d9f6e4c803150
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// EffectScaleGuardRunner.cs — 이펙트 크기 가드 틱 러너(숨은 오브젝트 1개 · 코루틴 0)
|
||||
//
|
||||
// PD 지시 #813 · 발주서 WL-811s §1-3
|
||||
//
|
||||
// · SO 가 꺼져 있으면 **오브젝트를 만들지 않는다**(C8: 꺼짐 = 흔적 0).
|
||||
// · **LateUpdate** 로 돈다 — 원본 스폰(Update·코루틴) 뒤 · 렌더 앞이라 활성화된 그 프레임에 배율이 걸린다.
|
||||
// · unscaled 시간으로 판정한다(히트스톱 timeScale 0 중에도 착탄 이펙트가 뜬다).
|
||||
// · 기존 러너(811def WLReactionRunner · 813m3 NullMaterialGuardRunner)를 건드리지 않는다 = 병합 충돌 0.
|
||||
//
|
||||
// 🔴 어셈블리 주의: Assets/WL/Combat/ 에 .asmdef 를 만들지 말 것.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace WL.Combat.Reaction
|
||||
{
|
||||
/// <summary>EffectScaleGuard 전용 러너. DontDestroyOnLoad · LateUpdate 1개.</summary>
|
||||
public sealed class EffectScaleGuardRunner : MonoBehaviour
|
||||
{
|
||||
static EffectScaleGuardRunner s_runner;
|
||||
|
||||
public static bool Exists { get { return s_runner != null; } }
|
||||
|
||||
public static void Ensure()
|
||||
{
|
||||
if (s_runner != null) return;
|
||||
if (!Application.isPlaying) return;
|
||||
var go = new GameObject("__WLEffectScaleGuard");
|
||||
DontDestroyOnLoad(go);
|
||||
s_runner = go.AddComponent<EffectScaleGuardRunner>();
|
||||
}
|
||||
|
||||
void LateUpdate() { EffectScaleGuard.Tick(Time.unscaledTime); }
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
static void Boot()
|
||||
{
|
||||
if (WLEffectScaleSettings.Enabled) Ensure(); // 꺼져 있으면 오브젝트 0(C8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8115c2e509f46d81ac3ea07f5d914261
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// WLEffectScaleSettings.cs — 원본 풀 스폰 이펙트(상태이상·히트) 크기 값의 단일 출처(SOT · C45)
|
||||
//
|
||||
// PD 지시 #813 추가(2026-09-10) 「상태이상 효과(스턴) 크기 및 플레이어 스킬이 몬스터·플레이어 크기에 비해
|
||||
// 비정상적으로 큼 → 캐릭터/몬스터 크기를 고려해 1차로 조절」 · 발주서 WL-811s §1-3
|
||||
//
|
||||
// ■ 왜 필요한가 (전부 실측 · `AgentScripts/staging/WL811s/MEASURE.txt`)
|
||||
// 액터 실높이 = PC(Ai01 × ClassConfig.f_Scale 0.7) **1.191 m** · 존 잡몹 **0.54~0.73 m** ·
|
||||
// 엘리트(× 1.3) 1.22~2.07 m · 보스 Anubis(× 1.5) **3.13 m**
|
||||
// 이펙트 원 지름(프리팹 인스턴스 → ParticleSystem.Simulate t 스윕 → 파티클 실좌표 90 백분위) =
|
||||
// Effect_Stun **5.58 m**(PC 높이의 4.7배 · 잡몹 높이의 10배) · Effect_Hit_10501 8.84 m ·
|
||||
// Effect_Barrier 22.0 m … → PD 지적 그대로 「대상보다 이펙트가 크다」가 수치로 확인된다.
|
||||
//
|
||||
// ■ 규칙(Lead 제안 · 이 에셋은 그 규칙을 **상한**으로 적용한다 = 하향만 · 이미 작은 이펙트는 그대로)
|
||||
// 목표 최대 지름 = 기준 높이 × 계수 (상태이상 0.6 · 착탄 0.8) → 배율 = 목표 지름 ÷ 프리팹 원 지름
|
||||
// 보스 **패턴**(Effect_*Charging · Effect_Anubis_Skill1End 등)은 규칙 없음 = 원본 100 %(발주서 §1-1).
|
||||
//
|
||||
// ■ 어떻게 (813m3 NullMaterialGuard 의 「원본 이펙트 풀 루트 스윕」 방식 그대로 · 원본 훅 0)
|
||||
// 상태이상·히트 이펙트는 전부 원본 `InGameInfo.Show_Effect` → `dic_str_Effect` 풀이 스폰한다(WL 코드가 아니다).
|
||||
// 그래서 스폰 지점 훅 대신 **풀 루트(`InGameInfo.tf_Effects`)의 직속 자식**을 LateUpdate 에서 훑어
|
||||
// 활성화된 인스턴스에 `localScale = 원 스케일 × 배율` 을 **대입**(누적 곱이 아니라 대입 = 멱등)하고,
|
||||
// 원본 `TurnOff_GO.Off_Imm` 이 `SetActive(false)` 로 반납하면 원 스케일로 되돌린다.
|
||||
//
|
||||
// 에셋 경로(고정): Assets/WL/Combat/Settings/Resources/WL/WLEffectScaleSettings.asset
|
||||
// C8 롤백: 에셋이 없거나 enabled = false → 러너도 스윕도 없고 적용분은 전부 원복 = 원본 100 %.
|
||||
//
|
||||
// 🔴 원본 프리팹·머티리얼은 고치지 않는다(발주서 §2 금지). 런타임 인스턴스의 localScale 만 만진다.
|
||||
// 🔴 어셈블리 주의: Assets/WL/Combat/ 에 .asmdef 를 만들지 말 것.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace WL.Combat.Reaction
|
||||
{
|
||||
/// <summary>인스턴스 이름(끝의 "(Clone)" 제외)과 규칙 문자열을 어떻게 맞추나.</summary>
|
||||
public enum EffectScaleMatch
|
||||
{
|
||||
/// <summary>이름 전체가 같다(기본).</summary>
|
||||
Exact = 0,
|
||||
/// <summary>이름이 이 문자열로 시작한다(계열 일괄 규칙).</summary>
|
||||
Prefix = 1,
|
||||
/// <summary>이름 안에 이 문자열이 있다.</summary>
|
||||
Contains = 2
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct EffectScaleRule
|
||||
{
|
||||
[Tooltip("프리팹 이름(Assets/Res_Addr/Effect/<이름>.prefab). 인스턴스는 '<이름>(Clone)' 이다.")]
|
||||
public string prefab;
|
||||
|
||||
[Tooltip("매칭 방식. 규칙은 배열 순서대로 검사해 처음 맞는 것을 쓴다(Exact 를 앞에, Prefix 를 뒤에).")]
|
||||
public EffectScaleMatch match;
|
||||
|
||||
[Tooltip("고정 배율(프리팹 원 localScale × 이 값). 1 = 원본 그대로 · 0 이하 = 이 규칙 무시.")]
|
||||
public float scale;
|
||||
|
||||
[Tooltip("scaleByTargetHeight 가 켜졌고 대상 액터를 찾았을 때 targetRatio·baseDiameter 로 배율을 다시 계산한다.")]
|
||||
public bool useTargetHeight;
|
||||
|
||||
[Tooltip("대상 높이 × 이 계수 = 목표 최대 지름(m). Lead 규칙: 상태이상 0.6 · 착탄 0.8.")]
|
||||
public float targetRatio;
|
||||
|
||||
[Tooltip("프리팹 원 지름 실측(m · 파티클 실좌표 90 백분위 · MEASURE.txt [B]). 0 이면 높이 기반 계산을 못 한다.")]
|
||||
public float baseDiameter;
|
||||
|
||||
[Tooltip("표시용 메모(데이터 · 코드가 읽지 않는다).")]
|
||||
public string note;
|
||||
}
|
||||
|
||||
[CreateAssetMenu(fileName = "WLEffectScaleSettings", menuName = "WL/Effect Scale Guard Settings", order = 327)]
|
||||
public sealed class WLEffectScaleSettings : ScriptableObject
|
||||
{
|
||||
public const string ResourcesPath = "WL/WLEffectScaleSettings";
|
||||
|
||||
static WLEffectScaleSettings s_cached;
|
||||
static bool s_lookupDone;
|
||||
|
||||
/// <summary>설정 에셋(1회 로드 후 캐시). 없으면 null.</summary>
|
||||
public static WLEffectScaleSettings Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!s_lookupDone) { s_cached = Resources.Load<WLEffectScaleSettings>(ResourcesPath); s_lookupDone = true; }
|
||||
return s_cached;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>QA·A/B 전용 런타임 스위치. 0 = 에셋의 enabled · 1 = 강제 on · -1 = 강제 off.</summary>
|
||||
public static int RuntimeOverride;
|
||||
|
||||
/// <summary>가드가 살아 있는가. false 면 스윕도 러너도 없다(C8).</summary>
|
||||
public static bool Enabled
|
||||
{
|
||||
get
|
||||
{
|
||||
var i = Instance;
|
||||
if (i == null || RuntimeOverride < 0) return false;
|
||||
return RuntimeOverride > 0 || i.enabled;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>테스트용: 캐시를 비운다.</summary>
|
||||
public static void ClearCache() { s_cached = null; s_lookupDone = false; }
|
||||
|
||||
[Header("전체 스위치")]
|
||||
[Tooltip("끄면 러너도 스윕도 없고 이미 적용된 배율은 전부 원복된다 = 원본 100 %. C8 롤백 1순위.")]
|
||||
public bool enabled = true;
|
||||
|
||||
[Tooltip("적용·원복을 Console 에 남긴다(인스턴스당 1줄 · GC 발생 · 기본 off).")]
|
||||
public bool verboseLog = false;
|
||||
|
||||
[Header("스윕 (813m3 NullMaterialGuard 와 같은 풀 루트 스윕)")]
|
||||
[Tooltip("원본 이펙트 풀 `InGameInfo.tf_Effects` 를 훑는다 — 상태이상·히트가 전부 여기다. 끄면 이 발주는 무효.")]
|
||||
public bool sweepIngameEffects = true;
|
||||
|
||||
[Tooltip("원본 오브젝트 풀 `InGameInfo.tf_Objs` 도 훑는다(드랍·813f 등급 빔 · 기본 off).")]
|
||||
public bool sweepIngameObjs = false;
|
||||
|
||||
[Tooltip("811i SkillSpectacle 의 Live/Pool 루트도 훑는다. 🔴 811i 는 이미 행마다 scale 을 곱하므로 " +
|
||||
"켜면 이중 축소가 된다 — 기본 off 를 유지할 것.")]
|
||||
public bool sweepSkillSpectacle = false;
|
||||
|
||||
[Tooltip("메인 PC 하위를 훑는다(813k 레벨업 폭발이 PC 에 붙는다 · 813w4 가 이미 0.5 로 낮췄으므로 기본 off).")]
|
||||
public bool sweepMainPc = false;
|
||||
|
||||
[Tooltip("스윕 주기(초 · unscaled). 0 = 매 프레임(권장) — 활성화 프레임에 바로 적용해야 1프레임 원본 크기 깜빡임이 없다.")]
|
||||
public float sweepIntervalSeconds = 0f;
|
||||
|
||||
[Tooltip("한 스윕에서 **처음 보는** 인스턴스를 몇 개까지 등록하나(이름 읽기 = 유일한 할당 지점). 남으면 다음 스윕.")]
|
||||
public int maxNewPerSweep = 24;
|
||||
|
||||
[Tooltip("추적 슬롯 수(사전 할당). 넘으면 죽은 인스턴스를 정리하고, 그래도 모자라면 새 인스턴스를 건너뛴다.")]
|
||||
public int trackCapacity = 512;
|
||||
|
||||
[Tooltip("원본 `TurnOff_GO` 가 반납(SetActive(false))할 때 원 스케일로 되돌린다. 끄면 적용된 채로 풀에 남는다(멱등이라 안전하지만 C8 이 더 깔끔해진다).")]
|
||||
public bool restoreOnReturn = true;
|
||||
|
||||
[Header("대상 높이 기반 (옵션 · 기본 off = 아래 rules[].scale 고정 배율)")]
|
||||
[Tooltip("켜면 이펙트가 붙은 대상 액터의 실높이(발밑→머리)로 배율을 다시 계산한다. " +
|
||||
"대상을 못 찾으면(위치 지정 스폰 등) 고정 배율로 떨어진다.")]
|
||||
public bool scaleByTargetHeight = false;
|
||||
|
||||
[Tooltip("고정 배율을 역산할 때 쓴 기준 높이(m). 실측 PC(Ai01 × f_Scale 0.7) = 1.191 m.")]
|
||||
public float referenceHeight = 1.191f;
|
||||
|
||||
[Tooltip("대상 높이 기반 계산의 배율 하한(너무 작아져 안 보이는 것 방지).")]
|
||||
public float minScale = 0.05f;
|
||||
|
||||
[Tooltip("대상 높이 기반 계산의 배율 상한(보스처럼 큰 대상에서 원본보다 커지는 것 방지).")]
|
||||
public float maxScale = 1.5f;
|
||||
|
||||
[Header("규칙 (배열 순서대로 검사 · 처음 맞는 것 · 안 맞으면 손대지 않는다 = 원본 100 %)")]
|
||||
public EffectScaleRule[] rules = new EffectScaleRule[0];
|
||||
|
||||
/// <summary>인스턴스 이름으로 규칙을 찾는다(선형 · alloc 0). 없으면 −1.</summary>
|
||||
public int FindRule(string instanceName, int coreLength)
|
||||
{
|
||||
if (rules == null || string.IsNullOrEmpty(instanceName)) return -1;
|
||||
for (int i = 0; i < rules.Length; i++)
|
||||
{
|
||||
if (rules[i].scale <= 0f && !rules[i].useTargetHeight) continue;
|
||||
if (NameMatches(instanceName, coreLength, rules[i].prefab, rules[i].match)) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>"<이름>(Clone)" 에서 "(Clone)" 을 뺀 유효 길이(부분 문자열을 만들지 않는다 = alloc 0).</summary>
|
||||
public static int CoreLength(string n)
|
||||
{
|
||||
if (string.IsNullOrEmpty(n)) return 0;
|
||||
int len = n.Length;
|
||||
if (len > 7 && string.CompareOrdinal(n, len - 7, "(Clone)", 0, 7) == 0) len -= 7;
|
||||
return len;
|
||||
}
|
||||
|
||||
/// <summary>부분 문자열 생성 없이 이름을 맞춘다(alloc 0).</summary>
|
||||
public static bool NameMatches(string n, int coreLength, string pattern, EffectScaleMatch match)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pattern) || coreLength <= 0) return false;
|
||||
int pl = pattern.Length;
|
||||
if (pl > coreLength) return false;
|
||||
switch (match)
|
||||
{
|
||||
case EffectScaleMatch.Exact:
|
||||
return pl == coreLength && string.CompareOrdinal(n, 0, pattern, 0, pl) == 0;
|
||||
case EffectScaleMatch.Prefix:
|
||||
return string.CompareOrdinal(n, 0, pattern, 0, pl) == 0;
|
||||
default:
|
||||
for (int i = 0; i + pl <= coreLength; i++)
|
||||
if (string.CompareOrdinal(n, i, pattern, 0, pl) == 0) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8115a0c3e7d24b6f9a1c8e5d3b7f2049
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,428 @@
|
|||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8115a0c3e7d24b6f9a1c8e5d3b7f2049, type: 3}
|
||||
m_Name: WLEffectScaleSettings
|
||||
m_EditorClassIdentifier: Assembly-CSharp::WL.Combat.Reaction.WLEffectScaleSettings
|
||||
enabled: 1
|
||||
verboseLog: 0
|
||||
sweepIngameEffects: 1
|
||||
sweepIngameObjs: 0
|
||||
sweepSkillSpectacle: 0
|
||||
sweepMainPc: 0
|
||||
sweepIntervalSeconds: 0
|
||||
maxNewPerSweep: 24
|
||||
trackCapacity: 512
|
||||
restoreOnReturn: 1
|
||||
scaleByTargetHeight: 0
|
||||
referenceHeight: 1.191
|
||||
minScale: 0.05
|
||||
maxScale: 1.5
|
||||
rules:
|
||||
- prefab: Effect_Stun
|
||||
match: 0
|
||||
scale: 0.124
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.6
|
||||
baseDiameter: 5.776
|
||||
note: 상태이상 0.6
|
||||
- prefab: Effect_Poison
|
||||
match: 0
|
||||
scale: 0.383
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.6
|
||||
baseDiameter: 1.866
|
||||
note: 상태이상 0.6
|
||||
- prefab: Effect_Burn
|
||||
match: 0
|
||||
scale: 0.227
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.6
|
||||
baseDiameter: 3.151
|
||||
note: 상태이상 0.6
|
||||
- prefab: Effect_Freezing
|
||||
match: 0
|
||||
scale: 0.157
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.6
|
||||
baseDiameter: 4.549
|
||||
note: 상태이상 0.6
|
||||
- prefab: Effect_Frost
|
||||
match: 0
|
||||
scale: 0.094
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.6
|
||||
baseDiameter: 7.595
|
||||
note: 상태이상 0.6
|
||||
- prefab: Effect_Curse
|
||||
match: 0
|
||||
scale: 0.179
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.6
|
||||
baseDiameter: 4
|
||||
note: 상태이상 0.6
|
||||
- prefab: Effect_Slow
|
||||
match: 0
|
||||
scale: 0.238
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.6
|
||||
baseDiameter: 3
|
||||
note: 상태이상 0.6
|
||||
- prefab: Effect_SilenceCurse
|
||||
match: 0
|
||||
scale: 0.213
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.6
|
||||
baseDiameter: 3.351
|
||||
note: 상태이상 0.6
|
||||
- prefab: Effect_Blood
|
||||
match: 0
|
||||
scale: 0.099
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.6
|
||||
baseDiameter: 7.199
|
||||
note: 상태이상 0.6
|
||||
- prefab: Effect_HPBurn
|
||||
match: 0
|
||||
scale: 0.208
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.6
|
||||
baseDiameter: 3.436
|
||||
note: 상태이상 0.6
|
||||
- prefab: Effect_ManaBurn
|
||||
match: 0
|
||||
scale: 0.303
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.6
|
||||
baseDiameter: 2.355
|
||||
note: 상태이상 0.6
|
||||
- prefab: Effect_DefenceDecrease
|
||||
match: 0
|
||||
scale: 0.169
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.6
|
||||
baseDiameter: 4.226
|
||||
note: 상태이상 0.6
|
||||
- prefab: Effect_Barrier
|
||||
match: 0
|
||||
scale: 0.032
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.6
|
||||
baseDiameter: 22
|
||||
note: 상태이상 0.6
|
||||
- prefab: Effect_ManaShield
|
||||
match: 0
|
||||
scale: 0.512
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.6
|
||||
baseDiameter: 1.395
|
||||
note: 상태이상 0.6
|
||||
- prefab: Effect_Cure
|
||||
match: 0
|
||||
scale: 0.136
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.6
|
||||
baseDiameter: 5.249
|
||||
note: 상태이상 0.6
|
||||
- prefab: Effect_SpeedUp
|
||||
match: 0
|
||||
scale: 0.238
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.6
|
||||
baseDiameter: 3
|
||||
note: 상태이상 0.6
|
||||
- prefab: Effect_StatUp
|
||||
match: 0
|
||||
scale: 0.32
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.6
|
||||
baseDiameter: 2.231
|
||||
note: 상태이상 0.6
|
||||
- prefab: Effect_Reincarnation
|
||||
match: 0
|
||||
scale: 0.212
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.6
|
||||
baseDiameter: 3.372
|
||||
note: 상태이상 0.6
|
||||
- prefab: Effect_VampirHeal
|
||||
match: 0
|
||||
scale: 0.089
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.6
|
||||
baseDiameter: 8
|
||||
note: 상태이상 0.6
|
||||
- prefab: Effect_Hit_10101
|
||||
match: 0
|
||||
scale: 0.27
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 3.53
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_10102
|
||||
match: 0
|
||||
scale: 0.829
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 1.15
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_10201
|
||||
match: 0
|
||||
scale: 0.216
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 4.421
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_10202
|
||||
match: 0
|
||||
scale: 0.718
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 1.327
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_10301
|
||||
match: 0
|
||||
scale: 0.708
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 1.345
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_10302
|
||||
match: 0
|
||||
scale: 0.44
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 2.167
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_10303
|
||||
match: 0
|
||||
scale: 0.627
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 1.519
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_10401
|
||||
match: 0
|
||||
scale: 0.839
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 1.136
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_10402
|
||||
match: 0
|
||||
scale: 0.524
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 1.818
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_10501
|
||||
match: 0
|
||||
scale: 0.108
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 8.826
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_10502
|
||||
match: 0
|
||||
scale: 0.335
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 2.847
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_10601
|
||||
match: 0
|
||||
scale: 0.383
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 2.488
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_AnubisSkill1
|
||||
match: 0
|
||||
scale: 0.642
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 1.484
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_AnubisSkill2
|
||||
match: 0
|
||||
scale: 0.6
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 1.587
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_Bead
|
||||
match: 0
|
||||
scale: 0.791
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 1.205
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_BlackHole
|
||||
match: 0
|
||||
scale: 0.089
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 10.757
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_BrotherPenguin_Attack
|
||||
match: 0
|
||||
scale: 0.451
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 2.112
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_Electronic
|
||||
match: 0
|
||||
scale: 0.497
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 1.919
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_Energyball
|
||||
match: 0
|
||||
scale: 0.352
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 2.705
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_Fireball
|
||||
match: 0
|
||||
scale: 0.272
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 3.503
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_Firebolt
|
||||
match: 0
|
||||
scale: 0.493
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 1.933
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_FlowerDryad_Attack
|
||||
match: 0
|
||||
scale: 1
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 0.933
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_IceBolt
|
||||
match: 0
|
||||
scale: 0.322
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 2.961
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_IceGolem_Attack
|
||||
match: 0
|
||||
scale: 0.941
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 1.013
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_Mob_HellfireFlames
|
||||
match: 0
|
||||
scale: 0.479
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 1.991
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_RookieArcherSkill1
|
||||
match: 0
|
||||
scale: 0.529
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 1.8
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_Shaga_Attack
|
||||
match: 0
|
||||
scale: 0.688
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 1.385
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_Skill_CrossSlash
|
||||
match: 0
|
||||
scale: 1
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 0.638
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_Skill_DaggerThrow
|
||||
match: 0
|
||||
scale: 0.368
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 2.59
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_Skill_EnergySlashFlash
|
||||
match: 0
|
||||
scale: 0.193
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 4.925
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_Skill_Frozen
|
||||
match: 0
|
||||
scale: 0.159
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 6
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_Skill_SpinSlash
|
||||
match: 0
|
||||
scale: 1
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 0.463
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_SummonGolem
|
||||
match: 0
|
||||
scale: 0.819
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 1.163
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_SummonWorm
|
||||
match: 0
|
||||
scale: 0.819
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 1.164
|
||||
note: 착탄 0.8
|
||||
- prefab: Effect_Hit_SweepAttack
|
||||
match: 0
|
||||
scale: 0.486
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 1.961
|
||||
note: 착탄 0.8
|
||||
- prefab: ShineBulletHit
|
||||
match: 0
|
||||
scale: 0.353
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 2.7
|
||||
note: 착탄 0.8
|
||||
- prefab: WaveShotHit
|
||||
match: 0
|
||||
scale: 0.84
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 1.134
|
||||
note: 착탄 0.8
|
||||
- prefab: WindBullet_Hit
|
||||
match: 0
|
||||
scale: 0.335
|
||||
useTargetHeight: 1
|
||||
targetRatio: 0.8
|
||||
baseDiameter: 2.84
|
||||
note: 착탄 0.8
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8115d3f61a057e92bd4fb1806ea25372
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -71,7 +71,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
body:
|
||||
prefab: Effect_Slash_10101_1
|
||||
scale: 1.2
|
||||
scale: 0.459
|
||||
anchor: 1
|
||||
forwardOffset: 0.6
|
||||
useCasterRotation: 1
|
||||
|
|
@ -79,7 +79,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
impact:
|
||||
prefab: Effect_Hit_10101
|
||||
scale: 1
|
||||
scale: 0.27
|
||||
anchor: 1
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -92,7 +92,7 @@ MonoBehaviour:
|
|||
label: 회심의 일격
|
||||
charge:
|
||||
prefab: Effect_Berserk
|
||||
scale: 1
|
||||
scale: 0.999
|
||||
anchor: 2
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -100,7 +100,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
body:
|
||||
prefab: Effect_Slash_10101_2
|
||||
scale: 1.3
|
||||
scale: 0.335
|
||||
anchor: 1
|
||||
forwardOffset: 0.8
|
||||
useCasterRotation: 1
|
||||
|
|
@ -108,7 +108,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
impact:
|
||||
prefab: Effect_Hit_10101
|
||||
scale: 1.2
|
||||
scale: 0.27
|
||||
anchor: 1
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -121,7 +121,7 @@ MonoBehaviour:
|
|||
label: 돌진 베기
|
||||
charge:
|
||||
prefab: Effect_PCDash
|
||||
scale: 1
|
||||
scale: 0.702
|
||||
anchor: 2
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 1
|
||||
|
|
@ -129,7 +129,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
body:
|
||||
prefab: Effect_Slash_10101_3
|
||||
scale: 1.4
|
||||
scale: 0.121
|
||||
anchor: 1
|
||||
forwardOffset: 1
|
||||
useCasterRotation: 1
|
||||
|
|
@ -137,7 +137,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
impact:
|
||||
prefab: Effect_Hit_SweepAttack
|
||||
scale: 1
|
||||
scale: 0.486
|
||||
anchor: 1
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -150,7 +150,7 @@ MonoBehaviour:
|
|||
label: 돌개 바람
|
||||
charge:
|
||||
prefab: Effect_Howling
|
||||
scale: 1.2
|
||||
scale: 0.251
|
||||
anchor: 2
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -158,7 +158,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
body:
|
||||
prefab: Effect_WLSwingArc
|
||||
scale: 1.5
|
||||
scale: 0.483
|
||||
anchor: 1
|
||||
forwardOffset: 0.5
|
||||
useCasterRotation: 1
|
||||
|
|
@ -166,7 +166,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
impact:
|
||||
prefab: WindBullet_Hit
|
||||
scale: 1.2
|
||||
scale: 0.335
|
||||
anchor: 1
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -187,7 +187,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
body:
|
||||
prefab: Effect_Slash_10102_1
|
||||
scale: 1.2
|
||||
scale: 0.461
|
||||
anchor: 1
|
||||
forwardOffset: 0.6
|
||||
useCasterRotation: 1
|
||||
|
|
@ -195,7 +195,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
impact:
|
||||
prefab: Effect_Hit_10102
|
||||
scale: 1
|
||||
scale: 0.829
|
||||
anchor: 1
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -208,7 +208,7 @@ MonoBehaviour:
|
|||
label: 검기 분출
|
||||
charge:
|
||||
prefab: Effect_Casting_Energyball
|
||||
scale: 1
|
||||
scale: 0.667
|
||||
anchor: 2
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -216,7 +216,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
body:
|
||||
prefab: Effect_Slash_10102_2
|
||||
scale: 1.3
|
||||
scale: 0.403
|
||||
anchor: 1
|
||||
forwardOffset: 0.8
|
||||
useCasterRotation: 1
|
||||
|
|
@ -224,7 +224,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
impact:
|
||||
prefab: Effect_Hit_Skill_EnergySlashFlash
|
||||
scale: 1
|
||||
scale: 0.193
|
||||
anchor: 1
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -237,7 +237,7 @@ MonoBehaviour:
|
|||
label: 칼날 방패
|
||||
charge:
|
||||
prefab: Effect_Casting_ManaShield
|
||||
scale: 1
|
||||
scale: 0.539
|
||||
anchor: 2
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -245,7 +245,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
body:
|
||||
prefab: Effect_SwordShield
|
||||
scale: 1.35
|
||||
scale: 0.175
|
||||
anchor: 1
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -266,7 +266,7 @@ MonoBehaviour:
|
|||
label: 마나 폭발
|
||||
charge:
|
||||
prefab: Effect_Casting_Meteor
|
||||
scale: 1.2
|
||||
scale: 0.793
|
||||
anchor: 2
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -274,7 +274,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
body:
|
||||
prefab: Effect_STR_EXTRA_SPLASH
|
||||
scale: 1.6
|
||||
scale: 0.217
|
||||
anchor: 1
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -282,7 +282,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
impact:
|
||||
prefab: Effect_Hit_Energyball
|
||||
scale: 1.2
|
||||
scale: 0.352
|
||||
anchor: 1
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -303,7 +303,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
body:
|
||||
prefab: Effect_Slash_10501_1
|
||||
scale: 1.2
|
||||
scale: 0.335
|
||||
anchor: 1
|
||||
forwardOffset: 0.6
|
||||
useCasterRotation: 1
|
||||
|
|
@ -311,7 +311,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
impact:
|
||||
prefab: Effect_Hit_10501
|
||||
scale: 1
|
||||
scale: 0.108
|
||||
anchor: 1
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -324,7 +324,7 @@ MonoBehaviour:
|
|||
label: 강력한 일격
|
||||
charge:
|
||||
prefab: Effect_PCDash
|
||||
scale: 1
|
||||
scale: 0.702
|
||||
anchor: 2
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 1
|
||||
|
|
@ -332,7 +332,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
body:
|
||||
prefab: Effect_Slash_10501_2
|
||||
scale: 1.4
|
||||
scale: 0.316
|
||||
anchor: 2
|
||||
forwardOffset: 0.8
|
||||
useCasterRotation: 1
|
||||
|
|
@ -340,7 +340,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
impact:
|
||||
prefab: Effect_Hit_SweepAttack
|
||||
scale: 1
|
||||
scale: 0.486
|
||||
anchor: 1
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -361,7 +361,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
body:
|
||||
prefab: Effect_Heal
|
||||
scale: 1.3
|
||||
scale: 0.823
|
||||
anchor: 1
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -382,7 +382,7 @@ MonoBehaviour:
|
|||
label: 휘몰아치는 파도
|
||||
charge:
|
||||
prefab: Effect_Howling
|
||||
scale: 1.2
|
||||
scale: 0.251
|
||||
anchor: 2
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -390,7 +390,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
body:
|
||||
prefab: Effect_Slash_10501_3
|
||||
scale: 1.8
|
||||
scale: 0.157
|
||||
anchor: 1
|
||||
forwardOffset: 1
|
||||
useCasterRotation: 1
|
||||
|
|
@ -398,7 +398,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
impact:
|
||||
prefab: WaveShotHit
|
||||
scale: 1.2
|
||||
scale: 0.84
|
||||
anchor: 1
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -419,7 +419,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
body:
|
||||
prefab: Effect_Slash_10502_1
|
||||
scale: 1.2
|
||||
scale: 0.37
|
||||
anchor: 1
|
||||
forwardOffset: 0.6
|
||||
useCasterRotation: 1
|
||||
|
|
@ -448,7 +448,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
body:
|
||||
prefab: Effect_Slash_10502_2
|
||||
scale: 1.5
|
||||
scale: 0.344
|
||||
anchor: 1
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 1
|
||||
|
|
@ -469,7 +469,7 @@ MonoBehaviour:
|
|||
label: 도발
|
||||
charge:
|
||||
prefab: Effect_Howling
|
||||
scale: 1
|
||||
scale: 0.251
|
||||
anchor: 2
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -498,7 +498,7 @@ MonoBehaviour:
|
|||
label: 심판의 빛
|
||||
charge:
|
||||
prefab: Effect_Casting_Meteor
|
||||
scale: 1.2
|
||||
scale: 0.793
|
||||
anchor: 2
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -506,7 +506,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
body:
|
||||
prefab: ShineBulletFlash
|
||||
scale: 1.5
|
||||
scale: 1.075
|
||||
anchor: 1
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
@ -514,7 +514,7 @@ MonoBehaviour:
|
|||
lifetime: 0
|
||||
impact:
|
||||
prefab: ShineBulletHit
|
||||
scale: 1.2
|
||||
scale: 0.353
|
||||
anchor: 1
|
||||
forwardOffset: 0
|
||||
useCasterRotation: 0
|
||||
|
|
|
|||
Loading…
Reference in New Issue