2026-09-08 15:20:16 +00:00
// WL811def_Probe.cs — #811d/e/f 전투 연출 3종 검증 프로브 (읽기 전용 · 에셋 무수정 · 가짜 이벤트 발행)
/ /
// 헤드리스에서는 PlayFab 초기화가 실패해 인게임에 못 들어간다(811b 실측) → Play 의존 검증은 **Title Play 에서 가짜 이벤트**로 대체한다.
// 가짜 = RealCamera 1개(빈 GO) · MobActor/PCActor 컴포넌트(비활성 · Awake 만 · 실 몹 머티리얼 T_Aspethis_D = MK Toon) · CombatEvents.Raise* 직접 호출.
/ /
// unity command run_script --file AgentScripts/WL811def_Probe.cs --entry WL811def_Probe.Dump // 3 SO 값 · 구독 상태 (Edit/Play)
// unity command run_script --file AgentScripts/WL811def_Probe.cs --entry WL811def_Probe.Props // ⓔ 몹 프리팹 299 → 셰이더별 프로퍼티 실측 표 (Edit)
// unity command run_script --file AgentScripts/WL811def_Probe.cs --entry WL811def_Probe.KillCam // ⓑ 줌·슬로모 발동/복원 ms · 쿨 · 히트스톱 스킵 (Title Play)
// unity command run_script --file AgentScripts/WL811def_Probe.cs --entry WL811def_Probe.BossCam // ⓒ Spawned(isBoss) → 오빗 1.2 s (Title Play)
// unity command run_script --file AgentScripts/WL811def_Probe.cs --entry WL811def_Probe.ComboLead // 3타 리드 × 1.3 → 복원 (Title Play)
// unity command run_script --file AgentScripts/WL811def_Probe.cs --entry WL811def_Probe.Chain // ⓓ 3 s 안 2·3·5킬 티어 · 폭발 슬롯 ≤1 (Title Play)
// unity command run_script --file AgentScripts/WL811def_Probe.cs --entry WL811def_Probe.Merge // ⓓ 합산 윈도우 0.25 s (Title Play)
// unity command run_script --file AgentScripts/WL811def_Probe.cs --entry WL811def_Probe.Blink // ⓔ 10회 피격 → MPB on/off 20 · 머티리얼 인스턴스 0 · 잔류 0 (Title Play)
// unity command run_script --file AgentScripts/WL811def_Probe.cs --entry WL811def_Probe.GcCheck // ⓕ 구독자별 100회 GC 증가 (Title Play)
// unity command run_script --file AgentScripts/WL811def_Probe.cs --entry WL811def_Probe.Toggle // ⓖ 3 SO 런타임 off → 연출 0 (Title Play)
// unity command run_script --file AgentScripts/WL811def_Probe.cs --entry WL811def_Probe.Status // 카운터 전부
2026-09-08 16:08:47 +00:00
// unity command run_script --file AgentScripts/WL811def_Probe.cs --entry WL811def_Probe.FixCheck // 리뷰 FIX-1/2/3 회귀 (Title Play)
2026-09-08 15:20:16 +00:00
// unity command run_script --file AgentScripts/WL811def_Probe.cs --entry WL811def_Probe.Budget --args '[5.0]' // 병합 후 실전투(메인 에디터): ON/OFF fps·setPass 비교
// 결과: Screenshots_WL/WL811def/<이름>_<HHmmss>.txt + Console
using System ;
using System.Collections ;
using System.Collections.Generic ;
using System.Text ;
using UnityEngine ;
using UnityEngine.AI ;
using WL.Combat.Core ;
using WL.Combat.Reaction ;
using WL.CombatCamera ;
public static class WL811def_Probe
{
const string OutDir = "Screenshots_WL/WL811def" ;
const string SampleMat = "Assets/ResWork/Suriyun/Monster Pack Pyramid/Materials/Aspethis/T_Aspethis_D.mat" ;
const string DissolveMat = "Assets/ThirdParty/Dissolve Edge by Moonflower Carnivore/Materials/mat_DissolveEdge.mat" ;
public static void KillCam ( ) { Launch ( h = > h . Co_KillCam ( ) ) ; }
public static void BossCam ( ) { Launch ( h = > h . Co_BossCam ( ) ) ; }
public static void ComboLead ( ) { Launch ( h = > h . Co_ComboLead ( ) ) ; }
public static void Chain ( ) { Launch ( h = > h . Co_Chain ( ) ) ; }
public static void Merge ( ) { Launch ( h = > h . Co_Merge ( ) ) ; }
public static void Blink ( ) { Launch ( h = > h . Co_Blink ( ) ) ; }
public static void GcCheck ( ) { Launch ( h = > h . Co_Gc ( ) ) ; }
public static void Toggle ( ) { Launch ( h = > h . Co_Toggle ( ) ) ; }
public static void Budget ( float seconds ) { Launch ( h = > h . Co_Budget ( seconds < = 0f ? 5f : seconds ) ) ; }
2026-09-08 16:08:47 +00:00
public static void FixCheck ( ) { Launch ( h = > h . Co_FixCheck ( ) ) ; }
2026-09-08 15:20:16 +00:00
public static object Dump ( )
{
var sb = new StringBuilder ( ) ;
sb . AppendLine ( "# WL811def Dump " + DateTime . Now . ToString ( "HH:mm:ss" ) + " playing=" + Application . isPlaying + " core=" + WLCombatCoreSettings . Enabled ) ;
var cam = WLCombatCameraSettings . Instance ; var re = WLReactionSettings . Instance ; var bl = WLHitBlinkSettings . Instance ;
sb . AppendLine ( "camera asset=" + ( cam ! = null ? "found" : "NULL" ) + " Enabled=" + WLCombatCameraSettings . Enabled + " subscribed=" + CombatCameraDirector . Subscribed ) ;
sb . AppendLine ( "reaction asset=" + ( re ! = null ? "found" : "NULL" ) + " Enabled=" + WLReactionSettings . Enabled + " chainSub=" + KillChain . Subscribed + " mergeSub=" + DamageAggregator . Subscribed ) ;
sb . AppendLine ( "blink asset=" + ( bl ! = null ? "found" : "NULL" ) + " Enabled=" + WLHitBlinkSettings . Enabled + " subscribed=" + MobHitFlash . Subscribed ) ;
sb . AppendLine ( "subs: Killed=" + CombatEvents . Killed . Count + " Spawned=" + CombatEvents . Spawned . Count + " ComboStage=" + CombatEvents . ComboStage . Count + " HitConfirmed=" + CombatEvents . HitConfirmed . Count ) ;
var core = WLCombatCoreSettings . Instance ;
if ( core ! = null ) sb . AppendLine ( "core: cinematicDefaultScale=" + core . cinematicDefaultScale + " killCamCooldownSeconds=" + core . killCamCooldownSeconds + " clusterBurstMax=" + core . clusterBurstMax + " dissolveMax=" + core . dissolveMax ) ;
if ( cam ! = null ) sb . AppendLine ( "## WLCombatCameraSettings\n" + JsonUtility . ToJson ( cam , true ) ) ;
if ( re ! = null ) sb . AppendLine ( "## WLReactionSettings\n" + JsonUtility . ToJson ( re , true ) ) ;
if ( bl ! = null ) sb . AppendLine ( "## WLHitBlinkSettings\n" + JsonUtility . ToJson ( bl , true ) ) ;
Save ( "dump" , sb ) ; return sb . ToString ( ) ;
}
/// <summary>ⓔ 몹 프리팹 전수(Res_Addr/Mobs) → 비파티클 렌더러의 셰이더별 프로퍼티 존재(Shader.FindPropertyIndex) 표. Edit 모드.</summary>
public static object Props ( )
{
var sb = new StringBuilder ( ) ;
sb . AppendLine ( "# WL811def Props " + DateTime . Now . ToString ( "HH:mm:ss" ) ) ;
string [ ] names = { "_AlbedoColor" , "_BaseColor" , "_Color" , "_EmissionColor" , "_EdgeAroundHDR" , "_EdgeAround" , "_Progress" } ;
var byShader = new Dictionary < string , int [ ] > ( ) ; // [rendererCount, prefabCount, prop0..]
var lastPrefab = new Dictionary < string , string > ( ) ;
int prefabs = 0 , renderers = 0 , particle = 0 ;
foreach ( var folder in new [ ] { "Assets/Res_Addr/Mobs/Mob" , "Assets/Res_Addr/Mobs/Elite" , "Assets/Res_Addr/Mobs/FieldBoss" } )
{
var guids = UnityEditor . AssetDatabase . FindAssets ( "t:Prefab" , new [ ] { folder } ) ;
foreach ( var g in guids )
{
var path = UnityEditor . AssetDatabase . GUIDToAssetPath ( g ) ;
var go = UnityEditor . AssetDatabase . LoadAssetAtPath < GameObject > ( path ) ;
if ( go = = null ) continue ;
prefabs + + ;
var seen = new HashSet < string > ( ) ;
foreach ( var r in go . GetComponentsInChildren < Renderer > ( true ) )
{
if ( r is ParticleSystemRenderer ) { particle + + ; continue ; }
renderers + + ;
var m = r . sharedMaterial ; var sh = m ! = null ? m . shader : null ;
string key = sh ! = null ? sh . name : "(null shader)" ;
int [ ] row ;
if ( ! byShader . TryGetValue ( key , out row ) )
{
row = new int [ 2 + names . Length ] ;
if ( sh ! = null ) for ( int i = 0 ; i < names . Length ; i + + ) row [ 2 + i ] = sh . FindPropertyIndex ( names [ i ] ) > = 0 ? 1 : 0 ;
byShader [ key ] = row ;
}
row [ 0 ] + + ;
if ( seen . Add ( key ) ) { row [ 1 ] + + ; lastPrefab [ key ] = System . IO . Path . GetFileNameWithoutExtension ( path ) ; }
}
}
}
sb . AppendLine ( "prefabs=" + prefabs + " renderers(non-particle)=" + renderers + " particleRenderers(skipped)=" + particle ) ;
sb . AppendLine ( string . Format ( "{0,-45} {1,5} {2,5} | {3}" , "shader" , "rend" , "pref" , string . Join ( " " , names ) ) ) ;
foreach ( var kv in byShader )
{
var row = kv . Value ; var cols = new StringBuilder ( ) ;
for ( int i = 0 ; i < names . Length ; i + + ) cols . Append ( string . Format ( "{0,-" + names [ i ] . Length + "} " , row [ 2 + i ] = = 1 ? "Y" : "-" ) ) ;
sb . AppendLine ( string . Format ( "{0,-45} {1,5} {2,5} | {3} e.g. {4}" , kv . Key , row [ 0 ] , row [ 1 ] , cols . ToString ( ) . TrimEnd ( ) , lastPrefab [ kv . Key ] ) ) ;
}
var dis = UnityEditor . AssetDatabase . LoadAssetAtPath < Material > ( DissolveMat ) ;
if ( dis ! = null & & dis . shader ! = null )
{
var cols = new StringBuilder ( ) ;
for ( int i = 0 ; i < names . Length ; i + + ) cols . Append ( names [ i ] + "=" + ( dis . shader . FindPropertyIndex ( names [ i ] ) > = 0 ? "Y" : "-" ) + " " ) ;
sb . AppendLine ( "dissolve mat=" + dis . name + " shader=" + dis . shader . name + " | " + cols + "| _EdgeAroundHDR=" + dis . GetFloat ( "_EdgeAroundHDR" ) + " _EdgeAround=" + dis . GetFloat ( "_EdgeAround" ) ) ;
}
var found = Shader . Find ( WLHitBlinkSettings . Instance ! = null ? WLHitBlinkSettings . Instance . dissolveShaderName : "MoonflowerCarnivore/Dissolve Edge" ) ;
sb . AppendLine ( "Shader.Find(dissolveShaderName)=" + ( found ! = null ? found . name : "NULL" ) ) ;
Save ( "props" , sb ) ; return sb . ToString ( ) ;
}
public static object Status ( )
{
var sb = new StringBuilder ( ) ;
sb . AppendLine ( "# WL811def Status " + DateTime . Now . ToString ( "HH:mm:ss" ) + " timeScale=" + Time . timeScale . ToString ( "F2" ) + " arbiter=" + TimeScaleArbiter . Active ) ;
sb . AppendLine ( "camera: killCam=" + CombatCameraDirector . KillCamCount + " skip(role/cool/runcap/guard/nocam/busy)=" + CombatCameraDirector . KillCamSkippedRole + "/" + CombatCameraDirector . KillCamSkippedCooldown + "/" + CombatCameraDirector . KillCamSkippedRunCap + "/" + CombatCameraDirector . KillCamSkippedGuard + "/" + CombatCameraDirector . KillCamSkippedNoCam + "/" + CombatCameraDirector . KillCamSkippedBusy
2026-09-08 16:08:47 +00:00
+ " abandoned=" + CombatCameraDirector . KillCamAbandoned + " rebased=" + CombatCameraDirector . KillCamRebased + " pending=" + CombatCameraDirector . KillCamPending
+ " boss=" + CombatCameraDirector . BossCamCount + " bossSkip=" + CombatCameraDirector . BossCamSkipped + " preset(applied/restored/abandoned/active)=" + CombatCameraDirector . BossPresetApplied + "/" + CombatCameraDirector . BossPresetRestored + "/" + CombatCameraDirector . BossPresetAbandoned + "/" + CombatCameraDirector . BossPresetActive + " lead=" + CombatCameraDirector . ComboLeadCount
2026-09-08 15:20:16 +00:00
+ " active(kill/boss)=" + CombatCameraDirector . KillCamActive + "/" + CombatCameraDirector . BossCamActive + " zoom=" + CombatCameraDirector . CurrentZoomScale . ToString ( "F3" ) + " lastKillCam=" + CombatCameraDirector . LastKillCamMeasured . ToString ( "F3" ) + " lastBoss=" + CombatCameraDirector . LastBossCamMeasured . ToString ( "F3" ) + " slowmo=" + CombatCameraDirector . LastSlowMoResult + " run=" + CombatCameraDirector . RunCount + " last=" + CombatCameraDirector . LastInfo ) ;
sb . AppendLine ( "chain: kills=" + KillChain . KillCount + " tiers=" + KillChain . TierCount + " chains=" + KillChain . ChainCount + " best=" + KillChain . BestChainEver + " cur=" + KillChain . ChainKills + " burst=" + KillChain . BurstCount + " denied=" + KillChain . BurstDeniedBudget + " outOfView=" + KillChain . BurstSkippedView + " noInfo=" + KillChain . BurstNoInGameInfo + " held=" + KillChain . BurstHeld + " budget=" + EffectBudget . Count ( EffectBudgetKind . ClusterBurst ) + "/" + EffectBudget . Limit ( EffectBudgetKind . ClusterBurst ) + " last=" + KillChain . LastInfo ) ;
2026-09-08 16:08:47 +00:00
sb . AppendLine ( "merge: hits=" + DamageAggregator . HitCount + " flush=" + DamageAggregator . FlushCount + " maxMerged=" + DamageAggregator . MaxHitsMerged + " evict=" + DamageAggregator . EvictCount + " active=" + DamageAggregator . ActiveSlots + " reentrant=" + DamageAggregator . ReentrantHits + " dispatching=" + DamageAggregator . Dispatching ) ;
2026-09-08 15:20:16 +00:00
sb . AppendLine ( "blink: on=" + MobHitFlash . OnCount + " off=" + MobHitFlash . OffCount + " extend=" + MobHitFlash . ExtendCount + " throttled=" + MobHitFlash . ThrottledCount + " tint=" + MobHitFlash . TintCount + " clear=" + MobHitFlash . ClearCount + " skipFilter=" + MobHitFlash . SkippedFilter + " noProp=" + MobHitFlash . SkippedNoProperty + " states=" + MobHitFlash . StateCount + " active=" + MobHitFlash . ActiveCount + " reflect=" + MobHitFlash . RendererFieldViaReflection + " dissolve(resolved/hdr/range)=" + MobHitFlash . DissolveResolved + "/" + MobHitFlash . DissolveHasHdr + "/" + MobHitFlash . DissolveHasRange ) ;
sb . AppendLine ( "lead multiplier=" + CameraMoveLead . LeadMultiplier . ToString ( "F2" ) + " runner(reaction)=" + WLReactionRunner . Exists ) ;
Save ( "status" , sb ) ; return sb . ToString ( ) ;
}
// ───────────────────────────────────────────── 공용
static void Save ( string name , StringBuilder sb )
{
try
{
System . IO . Directory . CreateDirectory ( OutDir ) ;
string path = System . IO . Path . Combine ( OutDir , name + "_" + DateTime . Now . ToString ( "HHmmss" ) + ".txt" ) ;
System . IO . File . WriteAllText ( path , sb . ToString ( ) , new UTF8Encoding ( false ) ) ;
Debug . Log ( "[WL811def] 저장 " + path ) ;
}
catch ( Exception e ) { Debug . LogWarning ( "[WL811def] 저장 실패 " + e . Message ) ; }
Debug . Log ( sb . ToString ( ) ) ;
}
static void Launch ( Func < Host , IEnumerator > make )
{
if ( ! Application . isPlaying ) { Debug . LogWarning ( "[WL811def] Play 모드에서만(Title Play). Edit 모드는 Dump/Props/Status 만." ) ; return ; }
var old = GameObject . Find ( "__wl811def_probe" ) ;
if ( old ! = null ) UnityEngine . Object . DestroyImmediate ( old ) ;
var go = new GameObject ( "__wl811def_probe" ) ;
var h = go . AddComponent < Host > ( ) ;
h . StartCoroutine ( make ( h ) ) ;
}
/// <summary>가짜 몹: 비활성 GO 에 컴포넌트를 얹고(NavMeshAgent 는 비활성 → 네비메시 에러 0) 활성화해 Awake(arr_Renderer 캐시)만 돌린다. Start/Update 없음.</summary>
static MobActor MakeMob ( string name , eSubRol sub , Vector3 pos , Material mat )
{
var go = new GameObject ( name ) ;
go . SetActive ( false ) ;
go . transform . position = pos ;
go . AddComponent < Rigidbody > ( ) . isKinematic = true ;
go . AddComponent < BoxCollider > ( ) ;
go . AddComponent < Animator > ( ) ;
var agent = go . AddComponent < NavMeshAgent > ( ) ; agent . enabled = false ;
var cube = GameObject . CreatePrimitive ( PrimitiveType . Cube ) ;
cube . name = "Body" ; cube . transform . SetParent ( go . transform , false ) ;
UnityEngine . Object . Destroy ( cube . GetComponent < Collider > ( ) ) ;
if ( mat ! = null ) cube . GetComponent < MeshRenderer > ( ) . sharedMaterial = mat ;
var mob = go . AddComponent < MobActor > ( ) ; mob . enabled = false ;
mob . m_SubRole = sub ;
go . SetActive ( true ) ;
2026-09-08 15:29:26 +00:00
InitCC ( mob ) ;
2026-09-08 15:20:16 +00:00
return mob ;
}
2026-09-08 15:29:26 +00:00
/// <summary>실 액터는 Set/On_Regen 이 Init_CC 로 dic_ccData 를 채운다 — 가짜는 리플렉션으로 같은 메서드를 1회 호출(Get_MoveSpeed 의 KeyNotFound 방지 · 1차 실측 76건).</summary>
static void InitCC ( Actor a )
{
var m = typeof ( Actor ) . GetMethod ( "Init_CC" , System . Reflection . BindingFlags . Instance | System . Reflection . BindingFlags . NonPublic | System . Reflection . BindingFlags . Public ) ;
if ( m ! = null ) m . Invoke ( a , null ) ; else Debug . LogWarning ( "[WL811def] Init_CC 미발견" ) ;
}
2026-09-08 15:20:16 +00:00
static PCActor MakePC ( string name , Vector3 pos )
{
var go = new GameObject ( name ) ;
go . SetActive ( false ) ;
go . transform . position = pos ;
go . AddComponent < Rigidbody > ( ) . isKinematic = true ;
go . AddComponent < BoxCollider > ( ) ;
go . AddComponent < Animator > ( ) ;
var agent = go . AddComponent < NavMeshAgent > ( ) ; agent . enabled = false ;
var cube = GameObject . CreatePrimitive ( PrimitiveType . Cube ) ;
cube . transform . SetParent ( go . transform , false ) ;
UnityEngine . Object . Destroy ( cube . GetComponent < Collider > ( ) ) ;
var pc = go . AddComponent < PCActor > ( ) ; pc . enabled = false ;
go . SetActive ( true ) ;
2026-09-08 15:29:26 +00:00
InitCC ( pc ) ;
2026-09-08 15:20:16 +00:00
return pc ;
}
2026-09-08 16:08:47 +00:00
/// <summary>진단용 읽기 — RealCamera.list_tfCamactionTarget(private · 인트로 CamStatus 0 에서만 소비)의 길이 확인(FIX-3).</summary>
static List < Transform > CamActionList ( RealCamera cam )
{
var f = typeof ( RealCamera ) . GetField ( "list_tfCamactionTarget" , System . Reflection . BindingFlags . Instance | System . Reflection . BindingFlags . NonPublic | System . Reflection . BindingFlags . Public ) ;
return f ! = null & & cam ! = null ? f . GetValue ( cam ) as List < Transform > : null ;
}
2026-09-08 15:20:16 +00:00
static Material LoadSampleMat ( )
{
return UnityEditor . AssetDatabase . LoadAssetAtPath < Material > ( SampleMat ) ; // 에디터 전용 프로브(Roslyn run_script 에 UNITY_EDITOR 심볼이 없을 수 있어 #if 미사용)
}
static DamageInfo MakeDamage ( double dmg , bool cri , bool kill )
{
var d = new DamageInfo { Beater = null , Critical = cri ? eStat . CRI : eStat . None , IsDeadByThisDamage = kill } ;
d . Damage = dmg ; d . BaseDmg = dmg ;
return d ;
}
public class Host : MonoBehaviour
{
StringBuilder _sb ; float _t0 ;
RealCamera _cam ; Transform _dummy ; bool _camInstalled ; RealCamera _prevCam ;
readonly List < GameObject > _trash = new List < GameObject > ( ) ;
string Pre ( ) { return string . Format ( "{0,7:F0} | f{1,-5} | ts={2:F2} | " , ( Time . unscaledTime - _t0 ) * 1000f , Time . frameCount , Time . timeScale ) ; }
static string N ( UnityEngine . Object o ) { return o ! = null ? o . name : "null" ; }
IEnumerator Wait ( float s ) { float t = Time . unscaledTime ; while ( Time . unscaledTime - t < s ) yield return null ; }
Vector3 Front ( float dist )
{
var c = Camera . main ;
return c ! = null ? c . transform . position + c . transform . forward * dist : Vector3 . zero ;
}
/// <summary>가짜 RealCamera(빈 GO · 카메라 컴포넌트 없음): 원본 프리팹 [0] 값(3/3/2/78 → 기준서 65) · CamStatus 기본 1 · Set_Target(1인자)로 Update_Cam 만 돈다.</summary>
void InstallCamera ( )
{
_prevCam = MyValue . _RealCamera ;
var go = new GameObject ( "__wl811def_cam" ) ; _trash . Add ( go ) ;
_cam = go . AddComponent < RealCamera > ( ) ;
_cam . arr_cCamData = new [ ] { new cCamData { minDistance = 3f , maxDistance = 3f , DistanceUp = 2f , cameraHeight = 65f } , new cCamData { minDistance = 2f , maxDistance = 3f , DistanceUp = 3.5f , cameraHeight = 78f } } ;
_cam . minDistance = 3f ; _cam . maxDistance = 3f ; _cam . DistanceUp = 2f ; _cam . cameraHeight = 65f ; _cam . rotateAround = 30f ; _cam . smooth = 4f ;
var d = new GameObject ( "__wl811def_pc" ) ; _trash . Add ( d ) ; d . transform . position = Front ( 6f ) ;
_dummy = d . transform ;
_cam . Set_Target ( _dummy ) ;
MyValue . _RealCamera = _cam ; _camInstalled = true ;
}
void Cleanup ( )
{
CombatCameraDirector . ReleaseAll ( ) ;
MobHitFlash . RestoreAll ( ) ;
KillChain . ClearAll ( ) ; DamageAggregator . ClearAll ( ) ;
if ( _camInstalled ) { MyValue . _RealCamera = _prevCam ; _camInstalled = false ; }
TimeScaleArbiter . ReleaseAll ( ) ; Time . timeScale = 1f ;
for ( int i = 0 ; i < _trash . Count ; i + + ) if ( _trash [ i ] ! = null ) Destroy ( _trash [ i ] ) ;
_trash . Clear ( ) ;
Destroy ( gameObject ) ;
}
string CamRow ( ) { return "min=" + _cam . minDistance . ToString ( "F3" ) + " max=" + _cam . maxDistance . ToString ( "F3" ) + " up=" + _cam . DistanceUp . ToString ( "F3" ) + " yaw=" + _cam . rotateAround . ToString ( "F2" ) + " target=" + N ( _cam . target ) + " zoom=" + CombatCameraDirector . CurrentZoomScale . ToString ( "F3" ) + " arb=" + TimeScaleArbiter . Active + " camPos=" + _cam . transform . position . ToString ( "F2" ) ; }
/// <summary>ⓑ 킬캠: 엘리트 Killed → 줌(3→2.25→3)·슬로모(0.35→1) 타임라인 · 히트스톱 SkippedHigher · 쿨 안 재발동 0 · ResetRun 뒤 재발동 · 일반 몹 = 스킵.</summary>
public IEnumerator Co_KillCam ( )
{
_sb = new StringBuilder ( ) ; _t0 = Time . unscaledTime ;
_sb . AppendLine ( "# WL811def KillCam " + DateTime . Now . ToString ( "HH:mm:ss" ) + " Enabled=" + WLCombatCameraSettings . Enabled + " core=" + WLCombatCoreSettings . Enabled + " arbiter=" + TimeScaleArbiter . Enabled ) ;
var st = WLCombatCameraSettings . Instance ; var core = WLCombatCoreSettings . Instance ;
_sb . AppendLine ( "# 값: zoom=" + st . killCamZoomScale + " in/hold/out=" + st . killCamZoomInSeconds + "/" + st . killCamHoldSeconds + "/" + st . killCamZoomOutSeconds + " slowmo=" + core . cinematicDefaultScale + "× " + st . killCamSlowMoSeconds + " s cool=" + core . killCamCooldownSeconds + " guardY=" + st . killCamBottomGuardViewportY + " maxPerRun=" + st . killCamMaxPerRun ) ;
CombatCameraDirector . ResetDiagnostics ( ) ; CombatCameraDirector . ResetRun ( ) ; TimeScaleArbiter . ResetDiagnostics ( ) ;
InstallCamera ( ) ;
yield return null ; yield return null ;
var elite = MakeMob ( "__wl811def_elite" , eSubRol . Elite , Front ( 5f ) , null ) ; _trash . Add ( elite . gameObject ) ;
var normal = MakeMob ( "__wl811def_normal" , eSubRol . None , Front ( 5f ) , null ) ; _trash . Add ( normal . gameObject ) ;
yield return null ;
_sb . AppendLine ( Pre ( ) + "before " + CamRow ( ) ) ;
// ① 일반 몹 → 역할 스킵
CombatEvents . RaiseKilled ( normal ) ;
_sb . AppendLine ( Pre ( ) + "① Normal Killed → killCam=" + CombatCameraDirector . KillCamCount + " skipRole=" + CombatCameraDirector . KillCamSkippedRole + " (기대 0 / 1)" ) ;
// ② 엘리트 → 발동 · 타임라인
float minSeen = 99f , tsMin = 9f ; float zoomReach = - 1f , tsRestore = - 1f , camRestore = - 1f ;
float tStart = Time . unscaledTime ;
CombatEvents . RaiseKilled ( elite ) ;
_sb . AppendLine ( Pre ( ) + "② Elite Killed → killCam=" + CombatCameraDirector . KillCamCount + " slowmo=" + CombatCameraDirector . LastSlowMoResult + " viewportY=" + CombatCameraDirector . LastVictimViewportY . ToString ( "F2" ) + " last=" + CombatCameraDirector . LastInfo + " " + CamRow ( ) ) ;
bool hsTried = false ; ArbiterResult hs = ArbiterResult . Invalid ;
float tEnd = tStart + st . killCamZoomInSeconds + st . killCamHoldSeconds + st . killCamZoomOutSeconds + 0.4f ;
int rows = 0 ;
while ( Time . unscaledTime < tEnd )
{
yield return null ;
float t = Time . unscaledTime - tStart ;
if ( ! hsTried & & t > 0.08f ) { hs = TimeScaleArbiter . Request ( TimeLayer . HitStop , 0f , 0.04f ) ; hsTried = true ; _sb . AppendLine ( Pre ( ) + " 슬로모 중 HitStop 요청 → " + hs + " (" + TimeScaleArbiter . LastInfo + ") 기대=SkippedHigher" ) ; }
minSeen = Mathf . Min ( minSeen , _cam . minDistance ) ; tsMin = Mathf . Min ( tsMin , Time . timeScale ) ;
if ( zoomReach < 0f & & Mathf . Approximately ( _cam . minDistance , 3f * st . killCamZoomScale ) ) zoomReach = t ;
if ( tsRestore < 0f & & tsMin < 0.99f & & Time . timeScale > = 0.999f ) tsRestore = t ;
if ( camRestore < 0f & & zoomReach > = 0f & & ! CombatCameraDirector . KillCamActive ) camRestore = t ;
if ( ( rows + + % 4 ) = = 0 | | ! CombatCameraDirector . KillCamActive ) _sb . AppendLine ( Pre ( ) + " " + CamRow ( ) ) ;
if ( camRestore > = 0f & & tsRestore > = 0f & & t > 0.9f ) break ;
}
_sb . AppendLine ( Pre ( ) + " 결과: minDistance 최소=" + minSeen . ToString ( "F3" ) + " (기대 " + ( 3f * st . killCamZoomScale ) . ToString ( "F3" ) + ") 줌 도달 " + ( zoomReach * 1000f ) . ToString ( "F0" ) + " ms · timeScale 최소=" + tsMin . ToString ( "F2" ) + " 복원 " + ( tsRestore * 1000f ) . ToString ( "F0" ) + " ms(기대 ≈" + ( st . killCamSlowMoSeconds * 1000f ) . ToString ( "F0" ) + ") · 카메라 복원 " + ( camRestore * 1000f ) . ToString ( "F0" ) + " ms · measured=" + CombatCameraDirector . LastKillCamMeasured . ToString ( "F3" ) + " s · abandoned=" + CombatCameraDirector . KillCamAbandoned + " · 종료 " + CamRow ( ) + " arbiterMeasured=" + TimeScaleArbiter . LastMeasured . ToString ( "F3" ) ) ;
// ③ 쿨 안 재발동 → 스킵
CombatEvents . RaiseKilled ( elite ) ;
_sb . AppendLine ( Pre ( ) + "③ 쿨 안 Elite Killed → killCam=" + CombatCameraDirector . KillCamCount + " skipCooldown=" + CombatCameraDirector . KillCamSkippedCooldown + " (기대 1 / 1) last=" + CombatCameraDirector . LastInfo ) ;
// ④ ResetRun(쿨·런 초기화) → 재발동 · 보스 킬
var boss = MakeMob ( "__wl811def_boss" , eSubRol . Boss , Front ( 5f ) , null ) ; _trash . Add ( boss . gameObject ) ;
CombatCameraDirector . ResetRun ( ) ;
CombatEvents . RaiseKilled ( boss ) ;
_sb . AppendLine ( Pre ( ) + "④ ResetRun 후 Boss Killed → killCam=" + CombatCameraDirector . KillCamCount + " (기대 2) slowmo=" + CombatCameraDirector . LastSlowMoResult + " " + CamRow ( ) ) ;
yield return Wait ( st . killCamZoomInSeconds + st . killCamHoldSeconds + st . killCamZoomOutSeconds + 0.3f ) ;
_sb . AppendLine ( Pre ( ) + " 복원 확인 " + CamRow ( ) + " active=" + CombatCameraDirector . KillCamActive + " measured=" + CombatCameraDirector . LastKillCamMeasured . ToString ( "F3" ) ) ;
// ⑤ 런 상한: ResetRun 뒤 쿨만 매번 지우며(ResetCooldown) maxPerRun 회 발동 → 다음은 runcap 스킵
int cap = st . killCamMaxPerRun ;
if ( cap > 0 )
{
CombatCameraDirector . ResetRun ( ) ;
int before = CombatCameraDirector . KillCamCount , cool0 = CombatCameraDirector . KillCamSkippedCooldown ;
for ( int i = 0 ; i < cap ; i + + ) { CombatCameraDirector . ResetCooldown ( ) ; CombatEvents . RaiseKilled ( elite ) ; CombatCameraDirector . ReleaseAll ( ) ; TimeScaleArbiter . ReleaseAll ( ) ; yield return null ; }
CombatCameraDirector . ResetCooldown ( ) ;
CombatEvents . RaiseKilled ( elite ) ;
_sb . AppendLine ( Pre ( ) + "⑤ 런 상한 " + cap + ": 발동+" + ( CombatCameraDirector . KillCamCount - before ) + " (기대 " + cap + ") skipRunCap=" + CombatCameraDirector . KillCamSkippedRunCap + " (기대 1) skipCool+" + ( CombatCameraDirector . KillCamSkippedCooldown - cool0 ) + " (기대 0) run=" + CombatCameraDirector . RunCount ) ;
}
_sb . AppendLine ( "# 종료 timeScale=" + Time . timeScale . ToString ( "F2" ) + " arbiter=" + TimeScaleArbiter . Active + " " + CamRow ( ) ) ;
Save ( "killcam" , _sb ) ; Cleanup ( ) ;
}
/// <summary>ⓒ 보스 캠: Spawned(isBoss) → Set_Target(보스) · sin 오빗 · 거리 배율 → 1.2 s 뒤 전부 복원 · arr_cCamData[1] 프리셋.</summary>
public IEnumerator Co_BossCam ( )
{
_sb = new StringBuilder ( ) ; _t0 = Time . unscaledTime ;
var st = WLCombatCameraSettings . Instance ;
_sb . AppendLine ( "# WL811def BossCam " + DateTime . Now . ToString ( "HH:mm:ss" ) + " Enabled=" + WLCombatCameraSettings . Enabled + " sec=" + st . bossCamSeconds + " orbit=" + st . bossCamOrbitDegrees + "° dist× " + st . bossCamDistanceScale + " retarget=" + st . bossCamRetarget + " preset=" + st . bossPresetApply + "(" + st . bossPresetDistance + " m · " + st . bossPresetCameraHeight + "°)" ) ;
CombatCameraDirector . ResetDiagnostics ( ) ;
InstallCamera ( ) ;
yield return null ; yield return null ;
var boss = MakeMob ( "__wl811def_boss" , eSubRol . Boss , Front ( 5f ) + Vector3 . right * 2f , null ) ; _trash . Add ( boss . gameObject ) ;
var normal = MakeMob ( "__wl811def_normal" , eSubRol . None , Front ( 5f ) , null ) ; _trash . Add ( normal . gameObject ) ;
yield return null ;
float yaw0 = _cam . rotateAround ; var preset0 = _cam . arr_cCamData [ 1 ] ;
_sb . AppendLine ( Pre ( ) + "before " + CamRow ( ) + " preset[1]=" + preset0 . minDistance + "/" + preset0 . maxDistance + "/" + preset0 . cameraHeight + "°" ) ;
CombatEvents . RaiseSpawned ( normal ) ;
_sb . AppendLine ( Pre ( ) + "① Normal Spawned → boss=" + CombatCameraDirector . BossCamCount + " (기대 0)" ) ;
float tStart = Time . unscaledTime ; float yawMax = - 999f , yawMin = 999f ; bool sawBossTarget = false ;
CombatEvents . RaiseSpawned ( boss ) ;
_sb . AppendLine ( Pre ( ) + "② Boss Spawned → boss=" + CombatCameraDirector . BossCamCount + " active=" + CombatCameraDirector . BossCamActive + " preset[1]=" + preset0 . minDistance + "/" + preset0 . maxDistance + "/" + preset0 . cameraHeight + "° up=" + preset0 . DistanceUp + " " + CamRow ( ) ) ;
int rows = 0 ; float tRestore = - 1f ;
while ( Time . unscaledTime - tStart < st . bossCamSeconds + 0.5f )
{
yield return null ;
float rel = Mathf . DeltaAngle ( yaw0 , _cam . rotateAround ) ;
yawMax = Mathf . Max ( yawMax , rel ) ; yawMin = Mathf . Min ( yawMin , rel ) ;
if ( ReferenceEquals ( _cam . target , boss . transform ) ) sawBossTarget = true ;
if ( tRestore < 0f & & ! CombatCameraDirector . BossCamActive ) tRestore = Time . unscaledTime - tStart ;
if ( ( rows + + % 6 ) = = 0 | | tRestore > = 0f & & rows % 6 = = 1 ) _sb . AppendLine ( Pre ( ) + " yawRel=" + rel . ToString ( "F2" ) + " " + CamRow ( ) ) ;
if ( tRestore > = 0f & & Time . unscaledTime - tStart > st . bossCamSeconds + 0.2f ) break ;
}
_sb . AppendLine ( Pre ( ) + " 결과: yaw 진폭 max=" + yawMax . ToString ( "F2" ) + " min=" + yawMin . ToString ( "F2" ) + " (기대 ≈+" + st . bossCamOrbitDegrees + ") · 종료 yawRel=" + Mathf . DeltaAngle ( yaw0 , _cam . rotateAround ) . ToString ( "F3" ) + " (기대 0) · 보스 타겟 관측=" + sawBossTarget + " · 복원 " + ( tRestore * 1000f ) . ToString ( "F0" ) + " ms(기대 ≈" + ( st . bossCamSeconds * 1000f ) . ToString ( "F0" ) + ") · measured=" + CombatCameraDirector . LastBossCamMeasured . ToString ( "F3" ) + " · target 복원=" + ReferenceEquals ( _cam . target , _dummy ) + " · " + CamRow ( ) ) ;
Save ( "bosscam" , _sb ) ; Cleanup ( ) ;
}
/// <summary>3타 리드: ComboStage(isLast) → LeadMultiplier 1.3 → comboLeadSeconds 뒤 1.</summary>
public IEnumerator Co_ComboLead ( )
{
_sb = new StringBuilder ( ) ; _t0 = Time . unscaledTime ;
var st = WLCombatCameraSettings . Instance ;
_sb . AppendLine ( "# WL811def ComboLead " + DateTime . Now . ToString ( "HH:mm:ss" ) + " × " + st . comboLeadMultiplier + " " + st . comboLeadSeconds + " s" ) ;
var pc = MakePC ( "__wl811def_pcactor" , Front ( 5f ) ) ; _trash . Add ( pc . gameObject ) ;
yield return null ;
CombatEvents . RaiseComboStage ( pc , 2 , "Attack2_S" , 0f , false ) ;
_sb . AppendLine ( Pre ( ) + "① stage2(isLast=false) → mult=" + CameraMoveLead . LeadMultiplier . ToString ( "F2" ) + " (기대 1.00) count=" + CombatCameraDirector . ComboLeadCount ) ;
float t0 = Time . unscaledTime ;
CombatEvents . RaiseComboStage ( pc , 3 , "Attack3_S" , 0f , true ) ;
_sb . AppendLine ( Pre ( ) + "② stage3(isLast) → mult=" + CameraMoveLead . LeadMultiplier . ToString ( "F2" ) + " (기대 " + st . comboLeadMultiplier . ToString ( "F2" ) + ") count=" + CombatCameraDirector . ComboLeadCount ) ;
float tBack = - 1f ;
while ( Time . unscaledTime - t0 < st . comboLeadSeconds + 0.3f ) { yield return null ; if ( tBack < 0f & & Mathf . Approximately ( CameraMoveLead . LeadMultiplier , 1f ) ) tBack = Time . unscaledTime - t0 ; }
_sb . AppendLine ( Pre ( ) + " 복원 " + ( tBack * 1000f ) . ToString ( "F0" ) + " ms (기대 ≈" + ( st . comboLeadSeconds * 1000f ) . ToString ( "F0" ) + ") mult=" + CameraMoveLead . LeadMultiplier . ToString ( "F2" ) ) ;
Save ( "combolead" , _sb ) ; Cleanup ( ) ;
}
int _tier , _ended ; string _tierLog = "" ;
void OnTier ( in KillChainTierEvent e ) { _tier + + ; _tierLog + = Pre ( ) + " TierReached " + e . name + " tier=" + e . tier + " kills=" + e . kills + " centroid=" + e . centroid . ToString ( "F1" ) + " burst=" + e . burstSpawned + "\n" ; }
void OnEnded ( in KillChainEndedEvent e ) { _ended + + ; _tierLog + = Pre ( ) + " Ended kills=" + e . kills + " best=" + e . bestTier + " dur=" + e . duration . ToString ( "F2" ) + "\n" ; }
/// <summary>ⓓ 연쇄: 3 s 안 3킬 → DOUBLE·TRIPLE · 폭발 슬롯 1(홀드) · 윈도우 만료 Ended · 5킬 RAMPAGE · 홀드 중 폭발 거부.</summary>
public IEnumerator Co_Chain ( )
{
_sb = new StringBuilder ( ) ; _t0 = Time . unscaledTime ; _tier = 0 ; _ended = 0 ; _tierLog = "" ;
var st = WLReactionSettings . Instance ;
_sb . AppendLine ( "# WL811def Chain " + DateTime . Now . ToString ( "HH:mm:ss" ) + " Enabled=" + WLReactionSettings . Enabled + " window=" + st . killChainWindowSeconds + " burstMinTier=" + st . clusterBurstMinTier + " prefab=" + st . clusterBurstPrefab + " hold=" + st . clusterBurstHoldSeconds + " InGameInfo=" + ( InGameInfo . Ins ! = null ) ) ;
KillChain . ResetDiagnostics ( ) ; KillChain . ClearAll ( ) ; EffectBudget . ResetAll ( ) ;
KillChain . TierReached . Add ( OnTier ) ; KillChain . Ended . Add ( OnEnded ) ;
var mobs = new MobActor [ 5 ] ;
for ( int i = 0 ; i < 5 ; i + + ) { mobs [ i ] = MakeMob ( "__wl811def_mob" + i , eSubRol . None , Front ( 5f ) + Vector3 . right * ( i - 2 ) , null ) ; _trash . Add ( mobs [ i ] . gameObject ) ; }
yield return null ;
for ( int i = 0 ; i < 3 ; i + + ) { CombatEvents . RaiseKilled ( mobs [ i ] ) ; yield return Wait ( 0.15f ) ; }
_sb . AppendLine ( Pre ( ) + "① 0.45 s 안 3킬 → tiers=" + _tier + " (기대 2: DOUBLE·TRIPLE) chain=" + KillChain . ChainKills + " burstHeld=" + KillChain . BurstHeld + " budget=" + EffectBudget . Count ( EffectBudgetKind . ClusterBurst ) + " burst/noInfo/denied=" + KillChain . BurstCount + "/" + KillChain . BurstNoInGameInfo + "/" + KillChain . BurstDeniedBudget ) ;
_sb . Append ( _tierLog ) ; _tierLog = "" ;
yield return Wait ( st . clusterBurstHoldSeconds + 0.1f ) ;
_sb . AppendLine ( Pre ( ) + " 홀드 만료 → burstHeld=" + KillChain . BurstHeld + " budget=" + EffectBudget . Count ( EffectBudgetKind . ClusterBurst ) + " (기대 false/0)" ) ;
yield return Wait ( st . killChainWindowSeconds - st . clusterBurstHoldSeconds ) ;
_sb . AppendLine ( Pre ( ) + "② 윈도우 만료 → ended=" + _ended + " (기대 1) chain=" + KillChain . ChainKills + " (기대 0)" ) ;
_sb . Append ( _tierLog ) ; _tierLog = "" ;
int denied0 = KillChain . BurstDeniedBudget ;
for ( int i = 0 ; i < 5 ; i + + ) { CombatEvents . RaiseKilled ( mobs [ i ] ) ; yield return null ; }
_sb . AppendLine ( Pre ( ) + "③ 5킬 연속 → tiers+=" + _tier + " (누적 기대 5) best=" + KillChain . BestChainEver + " burstDenied+" + ( KillChain . BurstDeniedBudget - denied0 ) + " (TRIPLE 슬롯 홀드 중 RAMPAGE 폭발 거부 = 동시 ≤1) budget=" + EffectBudget . Count ( EffectBudgetKind . ClusterBurst ) ) ;
_sb . Append ( _tierLog ) ;
KillChain . TierReached . Remove ( OnTier ) ; KillChain . Ended . Remove ( OnEnded ) ;
Save ( "chain" , _sb ) ; Cleanup ( ) ;
}
int _flush ; string _flushLog = "" ;
void OnFlush ( in DamageMergeEvent e ) { _flush + + ; _flushLog + = Pre ( ) + " Flushed victim=" + N ( e . victim ) + " total=" + e . total . ToString ( "F0" ) + " hits=" + e . hits + " max=" + e . maxSingle . ToString ( "F0" ) + " cri=" + e . anyCritical + " kill=" + e . isKill + " window=" + ( ( e . time - e . firstTime ) * 1000f ) . ToString ( "F0" ) + " ms\n" ; }
/// <summary>ⓓ 합산: 0.25 s 안 3타(10+20+30 · 2타 크리) → 플러시 1(60 · 3타) · 처치 타격 → 즉시 플러시.</summary>
public IEnumerator Co_Merge ( )
{
_sb = new StringBuilder ( ) ; _t0 = Time . unscaledTime ; _flush = 0 ; _flushLog = "" ;
var st = WLReactionSettings . Instance ;
_sb . AppendLine ( "# WL811def Merge " + DateTime . Now . ToString ( "HH:mm:ss" ) + " window=" + st . damageMergeWindowSeconds + " flushOnKill=" + st . damageMergeFlushOnKill ) ;
DamageAggregator . ResetDiagnostics ( ) ; DamageAggregator . ClearAll ( ) ;
DamageAggregator . Flushed . Add ( OnFlush ) ;
var mob = MakeMob ( "__wl811def_mob" , eSubRol . None , Front ( 5f ) , null ) ; _trash . Add ( mob . gameObject ) ;
yield return null ;
CombatEvents . RaiseHitConfirmed ( mob , MakeDamage ( 10 , false , false ) ) ; yield return null ;
CombatEvents . RaiseHitConfirmed ( mob , MakeDamage ( 20 , true , false ) ) ; yield return null ;
CombatEvents . RaiseHitConfirmed ( mob , MakeDamage ( 30 , false , false ) ) ;
double pend ; int ph ; bool has = DamageAggregator . TryGetPending ( mob , out pend , out ph ) ;
_sb . AppendLine ( Pre ( ) + "① 3타 → pending=" + has + " total=" + pend . ToString ( "F0" ) + " hits=" + ph + " flush=" + _flush + " (기대 true/60/3/0)" ) ;
yield return Wait ( st . damageMergeWindowSeconds + 0.1f ) ;
_sb . AppendLine ( Pre ( ) + " 윈도우 만료 → flush=" + _flush + " (기대 1) active=" + DamageAggregator . ActiveSlots ) ;
_sb . Append ( _flushLog ) ; _flushLog = "" ;
CombatEvents . RaiseHitConfirmed ( mob , MakeDamage ( 5 , false , false ) ) ; yield return null ;
CombatEvents . RaiseHitConfirmed ( mob , MakeDamage ( 999 , false , true ) ) ;
_sb . AppendLine ( Pre ( ) + "② 처치 타격 → 즉시 flush=" + _flush + " (기대 2) active=" + DamageAggregator . ActiveSlots ) ;
_sb . Append ( _flushLog ) ;
DamageAggregator . Flushed . Remove ( OnFlush ) ;
Save ( "merge" , _sb ) ; Cleanup ( ) ;
}
/// <summary>ⓔ 점멸: MK Toon 실 머티리얼 큐브 몹 10회 피격(0.1 s 간격) → on/off 20 · HasPropertyBlock 잔류 0 · 머티리얼 수 증가 0 · sharedMaterial 불변 · Killed 엣지 → Spawned 정리.</summary>
public IEnumerator Co_Blink ( )
{
_sb = new StringBuilder ( ) ; _t0 = Time . unscaledTime ;
var st = WLHitBlinkSettings . Instance ;
_sb . AppendLine ( "# WL811def Blink " + DateTime . Now . ToString ( "HH:mm:ss" ) + " Enabled=" + WLHitBlinkSettings . Enabled + " on/off=" + st . blinkOnSeconds + "/" + st . blinkOffSeconds + " color=" + st . blinkColor + " emission=" + st . useEmission ) ;
MobHitFlash . ResetDiagnostics ( ) ;
var mat = LoadSampleMat ( ) ;
_sb . AppendLine ( "# 머티리얼 " + ( mat ! = null ? mat . name + " shader=" + mat . shader . name : "NULL(기본 큐브 머티리얼)" ) ) ;
var mob = MakeMob ( "__wl811def_mob" , eSubRol . Elite , Front ( 5f ) , mat ) ; _trash . Add ( mob . gameObject ) ;
yield return null ;
var rend = mob . GetComponentInChildren < MeshRenderer > ( ) ;
int matsBefore = Resources . FindObjectsOfTypeAll < Material > ( ) . Length ;
int sharedId = rend . sharedMaterial ! = null ? rend . sharedMaterial . GetInstanceID ( ) : 0 ;
_sb . AppendLine ( Pre ( ) + "before block=" + rend . HasPropertyBlock ( ) + " materials=" + matsBefore + " sharedId=" + sharedId ) ;
int onDuringSeen = 0 ;
for ( int i = 0 ; i < 10 ; i + + )
{
CombatEvents . RaiseHitConfirmed ( mob , MakeDamage ( 10 , false , false ) ) ;
bool hasNow = rend . HasPropertyBlock ( ) ;
if ( hasNow ) onDuringSeen + + ;
if ( i < 3 ) _sb . AppendLine ( Pre ( ) + " hit" + ( i + 1 ) + " on=" + MobHitFlash . OnCount + " off=" + MobHitFlash . OffCount + " block=" + hasNow ) ;
yield return Wait ( 0.1f ) ;
}
yield return Wait ( 0.1f ) ;
int matsAfter = Resources . FindObjectsOfTypeAll < Material > ( ) . Length ;
int sharedAfter = rend . sharedMaterial ! = null ? rend . sharedMaterial . GetInstanceID ( ) : 0 ;
_sb . AppendLine ( Pre ( ) + "① 10회 피격 → on=" + MobHitFlash . OnCount + " off=" + MobHitFlash . OffCount + " (기대 10/10 = 20) extend=" + MobHitFlash . ExtendCount + " throttled=" + MobHitFlash . ThrottledCount + " 켜진 직후 block 관측=" + onDuringSeen + "/10 · 잔류 block=" + rend . HasPropertyBlock ( ) + " (기대 False) · 머티리얼 " + matsBefore + "→" + matsAfter + " (증가 " + ( matsAfter - matsBefore ) + " · 기대 0) · sharedId " + sharedId + "→" + sharedAfter + " (기대 동일) · noProp=" + MobHitFlash . SkippedNoProperty + " reflect=" + MobHitFlash . RendererFieldViaReflection ) ;
// 같은 프레임 다중 히트 → 1회 점멸(연장)
int on0 = MobHitFlash . OnCount , ext0 = MobHitFlash . ExtendCount ;
for ( int i = 0 ; i < 5 ; i + + ) CombatEvents . RaiseHitConfirmed ( mob , MakeDamage ( 10 , false , false ) ) ;
yield return Wait ( 0.15f ) ;
_sb . AppendLine ( Pre ( ) + "② 같은 프레임 5히트 → on+" + ( MobHitFlash . OnCount - on0 ) + " extend+" + ( MobHitFlash . ExtendCount - ext0 ) + " (기대 1 / 4) block=" + rend . HasPropertyBlock ( ) ) ;
// Killed → 디졸브 엣지 MPB(Shader.Find 폴백) → Spawned → 정리
CombatEvents . RaiseKilled ( mob ) ;
_sb . AppendLine ( Pre ( ) + "③ Killed(Elite) → tint=" + MobHitFlash . TintCount + " dissolve resolved/hdr/range=" + MobHitFlash . DissolveResolved + "/" + MobHitFlash . DissolveHasHdr + "/" + MobHitFlash . DissolveHasRange + " block=" + rend . HasPropertyBlock ( ) + " (기대 True) noProp=" + MobHitFlash . SkippedNoProperty ) ;
var mpb = new MaterialPropertyBlock ( ) ; rend . GetPropertyBlock ( mpb ) ;
_sb . AppendLine ( Pre ( ) + " MPB _EdgeAroundHDR=" + mpb . GetFloat ( "_EdgeAroundHDR" ) . ToString ( "F2" ) + " _EdgeAround=" + mpb . GetFloat ( "_EdgeAround" ) . ToString ( "F3" ) + " _Progress 포함=" + mpb . HasFloat ( "_Progress" ) + " (기대 False)" ) ;
CombatEvents . RaiseSpawned ( mob ) ;
_sb . AppendLine ( Pre ( ) + "④ Spawned → clear=" + MobHitFlash . ClearCount + " block=" + rend . HasPropertyBlock ( ) + " (기대 False)" ) ;
_sb . AppendLine ( "# GC: 100 피격 → " + GcOf ( ( ) = > CombatEvents . RaiseHitConfirmed ( mob , MakeDamage ( 10 , false , false ) ) , 100 ) + " (DamageInfo 생성 포함 · 프로브 측 할당)" ) ;
Save ( "blink" , _sb ) ; Cleanup ( ) ;
}
static string GcOf ( Action act , int n )
{
for ( int i = 0 ; i < 10 ; i + + ) act ( ) ;
long m0 = GC . GetTotalMemory ( false ) ; long p0 = UnityEngine . Profiling . Profiler . GetMonoUsedSizeLong ( ) ;
for ( int i = 0 ; i < n ; i + + ) act ( ) ;
long m1 = GC . GetTotalMemory ( false ) ; long p1 = UnityEngine . Profiling . Profiler . GetMonoUsedSizeLong ( ) ;
return "GC.GetTotalMemory delta=" + ( m1 - m0 ) + " B · MonoUsed delta=" + ( p1 - p0 ) + " B" ;
}
/// <summary>ⓕ 구독자별 100회 GC: DamageInfo 는 미리 1개 만들어 재사용(프로브 측 할당 0). 카메라/반응/점멸을 RuntimeDisabled 로 하나씩 켜서 잰다.</summary>
public IEnumerator Co_Gc ( )
{
_sb = new StringBuilder ( ) ; _t0 = Time . unscaledTime ;
_sb . AppendLine ( "# WL811def GcCheck " + DateTime . Now . ToString ( "HH:mm:ss" ) + " (기준 각 < 2048 B)" ) ;
InstallCamera ( ) ; yield return null ; yield return null ;
var mat = LoadSampleMat ( ) ;
var elite = MakeMob ( "__wl811def_elite" , eSubRol . Elite , Front ( 5f ) , mat ) ; _trash . Add ( elite . gameObject ) ;
var boss = MakeMob ( "__wl811def_boss" , eSubRol . Boss , Front ( 5f ) , mat ) ; _trash . Add ( boss . gameObject ) ;
var pc = MakePC ( "__wl811def_pcactor" , Front ( 5f ) ) ; _trash . Add ( pc . gameObject ) ;
yield return null ;
var dmg = MakeDamage ( 10 , false , false ) ;
bool c0 = WLCombatCameraSettings . RuntimeDisabled , r0 = WLReactionSettings . RuntimeDisabled , b0 = WLHitBlinkSettings . RuntimeDisabled ;
Action < bool , bool , bool > set = ( c , r , b ) = > { WLCombatCameraSettings . RuntimeDisabled = ! c ; WLReactionSettings . RuntimeDisabled = ! r ; WLHitBlinkSettings . RuntimeDisabled = ! b ; } ;
// 워밍업(상태·버퍼 1회 할당)
set ( true , true , true ) ;
CombatEvents . RaiseKilled ( elite ) ; CombatEvents . RaiseHitConfirmed ( elite , dmg ) ; CombatEvents . RaiseComboStage ( pc , 3 , "Attack3_S" , 0f , true ) ; CombatEvents . RaiseSpawned ( boss ) ;
yield return null ; CombatCameraDirector . ReleaseAll ( ) ; TimeScaleArbiter . ReleaseAll ( ) ; yield return null ;
set ( true , false , false ) ; CombatCameraDirector . ResetRun ( ) ;
_sb . AppendLine ( "camera Killed× 100(쿨로 1회 발동+99 스킵): " + GcOf ( ( ) = > CombatEvents . RaiseKilled ( elite ) , 100 ) ) ;
CombatCameraDirector . ReleaseAll ( ) ; TimeScaleArbiter . ReleaseAll ( ) ; yield return null ;
_sb . AppendLine ( "camera ComboStage× 100: " + GcOf ( ( ) = > CombatEvents . RaiseComboStage ( pc , 3 , "Attack3_S" , 0f , true ) , 100 ) ) ;
_sb . AppendLine ( "camera Spawned(boss)× 100(1회 발동+99 busy): " + GcOf ( ( ) = > CombatEvents . RaiseSpawned ( boss ) , 100 ) ) ;
CombatCameraDirector . ReleaseAll ( ) ; yield return null ;
set ( false , true , false ) ; KillChain . ClearAll ( ) ; DamageAggregator . ClearAll ( ) ;
_sb . AppendLine ( "reaction Killed× 100(연쇄 100 · 티어 4): " + GcOf ( ( ) = > CombatEvents . RaiseKilled ( elite ) , 100 ) ) ;
_sb . AppendLine ( "reaction HitConfirmed× 100(합산 1슬롯): " + GcOf ( ( ) = > CombatEvents . RaiseHitConfirmed ( elite , dmg ) , 100 ) ) ;
KillChain . ClearAll ( ) ; DamageAggregator . ClearAll ( ) ; yield return null ;
set ( false , false , true ) ;
_sb . AppendLine ( "blink HitConfirmed× 100(on 1+연장 99): " + GcOf ( ( ) = > CombatEvents . RaiseHitConfirmed ( elite , dmg ) , 100 ) ) ;
_sb . AppendLine ( "blink Killed× 100(엣지 세팅): " + GcOf ( ( ) = > CombatEvents . RaiseKilled ( elite ) , 100 ) ) ;
_sb . AppendLine ( "blink Spawned× 100(정리): " + GcOf ( ( ) = > CombatEvents . RaiseSpawned ( elite ) , 100 ) ) ;
yield return null ;
2026-09-08 15:29:26 +00:00
set ( false , false , false ) ;
_sb . AppendLine ( "core-only(3 SO off) Killed× 100: " + GcOf ( ( ) = > CombatEvents . RaiseKilled ( elite ) , 100 ) + " ← CombatEvents.RaiseKilled 의 Log(문자열 결합 · victim.name) 몫" ) ;
_sb . AppendLine ( "core-only(3 SO off) HitConfirmed× 100: " + GcOf ( ( ) = > CombatEvents . RaiseHitConfirmed ( elite , dmg ) , 100 ) ) ;
2026-09-08 15:20:16 +00:00
set ( true , true , true ) ; CombatCameraDirector . ResetRun ( ) ;
_sb . AppendLine ( "all3 Killed× 100: " + GcOf ( ( ) = > CombatEvents . RaiseKilled ( elite ) , 100 ) ) ;
_sb . AppendLine ( "all3 HitConfirmed× 100: " + GcOf ( ( ) = > CombatEvents . RaiseHitConfirmed ( elite , dmg ) , 100 ) ) ;
WLCombatCameraSettings . RuntimeDisabled = c0 ; WLReactionSettings . RuntimeDisabled = r0 ; WLHitBlinkSettings . RuntimeDisabled = b0 ;
_sb . AppendLine ( "# 원복 RuntimeDisabled cam/re/blink=" + c0 + "/" + r0 + "/" + b0 ) ;
Save ( "gc" , _sb ) ; Cleanup ( ) ;
}
/// <summary>ⓖ C8: 3 SO 런타임 off → 같은 이벤트에 카운터 0 · 카메라 필드 불변 · MPB 0 · timeScale 1 → on 복귀.</summary>
public IEnumerator Co_Toggle ( )
{
_sb = new StringBuilder ( ) ; _t0 = Time . unscaledTime ;
_sb . AppendLine ( "# WL811def Toggle " + DateTime . Now . ToString ( "HH:mm:ss" ) ) ;
InstallCamera ( ) ; yield return null ; yield return null ;
var mat = LoadSampleMat ( ) ;
var elite = MakeMob ( "__wl811def_elite" , eSubRol . Elite , Front ( 5f ) , mat ) ; _trash . Add ( elite . gameObject ) ;
var boss = MakeMob ( "__wl811def_boss" , eSubRol . Boss , Front ( 5f ) , mat ) ; _trash . Add ( boss . gameObject ) ;
var pc = MakePC ( "__wl811def_pcactor" , Front ( 5f ) ) ; _trash . Add ( pc . gameObject ) ;
yield return null ;
var rend = elite . GetComponentInChildren < MeshRenderer > ( ) ;
CombatCameraDirector . ResetDiagnostics ( ) ; CombatCameraDirector . ResetRun ( ) ; KillChain . ResetDiagnostics ( ) ; KillChain . ClearAll ( ) ; DamageAggregator . ResetDiagnostics ( ) ; DamageAggregator . ClearAll ( ) ; MobHitFlash . ResetDiagnostics ( ) ;
WLCombatCameraSettings . RuntimeDisabled = true ; WLReactionSettings . RuntimeDisabled = true ; WLHitBlinkSettings . RuntimeDisabled = true ;
float minB = _cam . minDistance , yawB = _cam . rotateAround ;
CombatEvents . RaiseKilled ( elite ) ; CombatEvents . RaiseSpawned ( boss ) ; CombatEvents . RaiseComboStage ( pc , 3 , "Attack3_S" , 0f , true ) ;
for ( int i = 0 ; i < 3 ; i + + ) CombatEvents . RaiseHitConfirmed ( elite , MakeDamage ( 10 , false , false ) ) ;
yield return null ; yield return null ;
_sb . AppendLine ( Pre ( ) + "OFF: killCam=" + CombatCameraDirector . KillCamCount + " boss=" + CombatCameraDirector . BossCamCount + " lead=" + CombatCameraDirector . ComboLeadCount + " mult=" + CameraMoveLead . LeadMultiplier . ToString ( "F2" ) + " chainKills=" + KillChain . KillCount + " tiers=" + KillChain . TierCount + " mergeHits=" + DamageAggregator . HitCount + " blinkOn=" + MobHitFlash . OnCount + " block=" + rend . HasPropertyBlock ( ) + " cam(min/yaw) " + minB . ToString ( "F2" ) + "→" + _cam . minDistance . ToString ( "F2" ) + " / " + yawB . ToString ( "F2" ) + "→" + _cam . rotateAround . ToString ( "F2" ) + " timeScale=" + Time . timeScale . ToString ( "F2" ) + " (기대 전부 0 · 불변 · 1.00)" ) ;
WLCombatCameraSettings . RuntimeDisabled = false ; WLReactionSettings . RuntimeDisabled = false ; WLHitBlinkSettings . RuntimeDisabled = false ;
CombatEvents . RaiseKilled ( elite ) ; CombatEvents . RaiseComboStage ( pc , 3 , "Attack3_S" , 0f , true ) ;
for ( int i = 0 ; i < 3 ; i + + ) CombatEvents . RaiseHitConfirmed ( elite , MakeDamage ( 10 , false , false ) ) ;
yield return null ;
_sb . AppendLine ( Pre ( ) + "ON : killCam=" + CombatCameraDirector . KillCamCount + " lead=" + CombatCameraDirector . ComboLeadCount + " mult=" + CameraMoveLead . LeadMultiplier . ToString ( "F2" ) + " chainKills=" + KillChain . KillCount + " mergeHits=" + DamageAggregator . HitCount + " blinkOn=" + MobHitFlash . OnCount + " block=" + rend . HasPropertyBlock ( ) + " timeScale=" + Time . timeScale . ToString ( "F2" ) + " (기대 1/1/1.30/1/3/1/True/0.35)" ) ;
yield return Wait ( 1.2f ) ;
_sb . AppendLine ( Pre ( ) + "복원: timeScale=" + Time . timeScale . ToString ( "F2" ) + " block=" + rend . HasPropertyBlock ( ) + " min=" + _cam . minDistance . ToString ( "F2" ) + " mult=" + CameraMoveLead . LeadMultiplier . ToString ( "F2" ) ) ;
Save ( "toggle" , _sb ) ; Cleanup ( ) ;
}
2026-09-08 16:08:47 +00:00
/// <summary>리뷰 WL-811def FIX 회귀: ① 처치 스택의 Init_Cam 덮어쓰기 뒤에도 킬캠 줌이 유지되나(FIX-1)
/// ② arr_cCamData[1] 프리셋이 busy 검사 뒤에만 써지고 보스 처치·사라짐에 원복되나(FIX-2) ③ list_tfCamactionTarget 길이가 불변인가(FIX-3).</summary>
public IEnumerator Co_FixCheck ( )
{
_sb = new StringBuilder ( ) ; _t0 = Time . unscaledTime ;
var st = WLCombatCameraSettings . Instance ;
_sb . AppendLine ( "# WL811def FixCheck " + DateTime . Now . ToString ( "HH:mm:ss" ) + " Enabled=" + WLCombatCameraSettings . Enabled
+ " zoom=" + st . killCamZoomScale + " scaleUp=" + st . killCamScaleDistanceUp + " preset=" + st . bossPresetApply + "(" + st . bossPresetDistance + " m · " + st . bossPresetCameraHeight + "°) retarget=" + st . bossCamRetarget + " bossCam=" + st . bossCamSeconds + " s" ) ;
CombatCameraDirector . ResetDiagnostics ( ) ; CombatCameraDirector . ResetRun ( ) ;
InstallCamera ( ) ;
yield return null ; yield return null ;
var boss = MakeMob ( "__wl811def_boss" , eSubRol . Boss , Front ( 5f ) , null ) ; _trash . Add ( boss . gameObject ) ;
var boss2 = MakeMob ( "__wl811def_boss2" , eSubRol . Boss , Front ( 5f ) + Vector3 . right * 2f , null ) ; _trash . Add ( boss2 . gameObject ) ;
var list = CamActionList ( _cam ) ;
int list0 = list ! = null ? list . Count : - 1 ;
var d1 = _cam . arr_cCamData [ 1 ] ;
float p0Min = d1 . minDistance , p0Max = d1 . maxDistance , p0H = d1 . cameraHeight , p0Up = d1 . DistanceUp ;
yield return null ;
_sb . AppendLine ( Pre ( ) + "before " + CamRow ( ) + " | [1]=" + p0Min . ToString ( "F2" ) + "/" + p0Max . ToString ( "F2" ) + "/" + p0H . ToString ( "F0" ) + "° up=" + p0Up . ToString ( "F2" ) + " · camActionList=" + list0 ) ;
// ───── ① FIX-1: 실전투 보스 처치 스택 재현 — RaiseKilled 직후 같은 프레임에 Set_LockTarget(null) → Init_Cam() 이 궤도 3필드를 덮어쓴다
_cam . Set_LockTarget ( boss . transform ) ; // 원본 MonsterSimpleInfo.OnClick_Lock 경로(보스만 락타겟) → Init_Cam → arr_cCamData[1]
yield return null ;
_sb . AppendLine ( Pre ( ) + "① 보스 락타겟 → " + CamRow ( ) + " (Init_Cam 이 [1] 값을 씀)" ) ;
float lockedMin = _cam . minDistance ;
CombatEvents . RaiseKilled ( boss ) ;
_cam . Set_LockTarget ( null ) ; // ← 같은 스택 · MonsterSimpleInfo.DeadTarget → Off() 가 하는 일 → Init_Cam → [0] 값
_sb . AppendLine ( Pre ( ) + " Killed + 같은 프레임 Set_LockTarget(null) → pending=" + CombatCameraDirector . KillCamPending + " (기대 True · 시작을 미룸) · 궤도 " + lockedMin . ToString ( "F2" ) + "→" + _cam . minDistance . ToString ( "F2" ) + " (외부 덮어쓰기) killCam=" + CombatCameraDirector . KillCamCount ) ;
float expectZoom = _cam . arr_cCamData [ 0 ] . minDistance * st . killCamZoomScale ;
float zoomMin = 99f ; float zoomReach = - 1f ; float tK = Time . unscaledTime ; int rows = 0 ;
while ( Time . unscaledTime - tK < st . killCamZoomInSeconds + st . killCamHoldSeconds + st . killCamZoomOutSeconds + 0.4f )
{
yield return null ;
zoomMin = Mathf . Min ( zoomMin , _cam . minDistance ) ;
if ( zoomReach < 0f & & Mathf . Approximately ( _cam . minDistance , expectZoom ) ) zoomReach = ( Time . unscaledTime - tK ) * 1000f ;
if ( ( rows + + % 8 ) = = 0 ) _sb . AppendLine ( Pre ( ) + " " + CamRow ( ) ) ;
if ( zoomReach > = 0f & & ! CombatCameraDirector . KillCamActive ) break ;
}
_sb . AppendLine ( Pre ( ) + " 결과: 줌 최소=" + zoomMin . ToString ( "F3" ) + " (기대 " + expectZoom . ToString ( "F3" ) + " = [0] " + _cam . arr_cCamData [ 0 ] . minDistance . ToString ( "F2" ) + " × " + st . killCamZoomScale + ") 도달 " + zoomReach . ToString ( "F0" ) + " ms · rebased=" + CombatCameraDirector . KillCamRebased
+ " (기대 1 = base 재취득) · abandoned=" + CombatCameraDirector . KillCamAbandoned + " (기대 0 · 수정 전엔 1) · measured=" + CombatCameraDirector . LastKillCamMeasured . ToString ( "F3" ) + " s · 복원 " + CamRow ( ) ) ;
// ───── ② FIX-2: 프리셋 스냅샷·복원 · busy 검사 뒤 적용
CombatCameraDirector . ResetRun ( ) ;
CombatEvents . RaiseSpawned ( boss ) ;
_sb . AppendLine ( Pre ( ) + "② Boss Spawned → bossCam=" + CombatCameraDirector . BossCamCount + " presetApplied=" + CombatCameraDirector . BossPresetApplied + " active=" + CombatCameraDirector . BossPresetActive
+ " [1]=" + d1 . minDistance . ToString ( "F2" ) + "/" + d1 . maxDistance . ToString ( "F2" ) + "/" + d1 . cameraHeight . ToString ( "F0" ) + "° up=" + d1 . DistanceUp . ToString ( "F2" ) + " (기대 " + st . bossPresetDistance + "/" + st . bossPresetDistance + "/" + st . bossPresetCameraHeight + "°)" ) ;
int applied0 = CombatCameraDirector . BossPresetApplied , skip0 = CombatCameraDirector . BossCamSkipped ;
yield return null ;
CombatEvents . RaiseSpawned ( boss2 ) ; // 오빗 진행 중 = busy → 스킵 · 프리셋도 추가 적용 0(수정 전엔 프리셋만 다시 써졌다)
_sb . AppendLine ( Pre ( ) + " 진행 중 두 번째 Boss Spawned → bossSkip+" + ( CombatCameraDirector . BossCamSkipped - skip0 ) + " (기대 1) presetApplied+" + ( CombatCameraDirector . BossPresetApplied - applied0 ) + " (기대 0 = busy 검사 뒤로 이동) last=" + CombatCameraDirector . LastInfo ) ;
yield return Wait ( st . bossCamSeconds + 0.3f ) ;
_sb . AppendLine ( Pre ( ) + " 오빗 종료 → bossActive=" + CombatCameraDirector . BossCamActive + " presetActive=" + CombatCameraDirector . BossPresetActive + " (기대 False/True = 프리셋은 보스전 동안 유지) [1]=" + d1 . minDistance . ToString ( "F2" ) + "/" + d1 . cameraHeight . ToString ( "F0" ) + "°"
+ " · ③ camActionList " + list0 + "→" + ( list ! = null ? list . Count : - 1 ) + " (기대 동일 = Set_Target 누적 0) · target 복원=" + ReferenceEquals ( _cam . target , _dummy ) + " · " + CamRow ( ) ) ;
CombatCameraDirector . ResetCooldown ( ) ;
CombatEvents . RaiseKilled ( boss ) ; // 보스 처치 → 프리셋 즉시 원복(쿨 스킵과 무관하게 복원 경로가 먼저다)
_sb . AppendLine ( Pre ( ) + " 보스 처치 → restored=" + CombatCameraDirector . BossPresetRestored + " (기대 1) abandoned=" + CombatCameraDirector . BossPresetAbandoned + " (기대 0) active=" + CombatCameraDirector . BossPresetActive
+ " [1]=" + d1 . minDistance . ToString ( "F2" ) + "/" + d1 . maxDistance . ToString ( "F2" ) + "/" + d1 . cameraHeight . ToString ( "F0" ) + "° up=" + d1 . DistanceUp . ToString ( "F2" ) + " (기대 원값 " + p0Min . ToString ( "F2" ) + "/" + p0Max . ToString ( "F2" ) + "/" + p0H . ToString ( "F0" ) + "°/" + p0Up . ToString ( "F2" ) + ")" ) ;
yield return Wait ( st . killCamZoomInSeconds + st . killCamHoldSeconds + st . killCamZoomOutSeconds + 0.3f ) ;
// ②-2 사라짐(풀 반납 = SetActive(false)) 경로도 원복하는가
CombatCameraDirector . ResetRun ( ) ;
CombatEvents . RaiseSpawned ( boss2 ) ;
_sb . AppendLine ( Pre ( ) + " 재적용(boss2) → active=" + CombatCameraDirector . BossPresetActive + " [1]=" + d1 . minDistance . ToString ( "F2" ) + "/" + d1 . cameraHeight . ToString ( "F0" ) + "°" ) ;
boss2 . gameObject . SetActive ( false ) ; // Actor.Off() 와 같은 상태(IsDead() = !activeInHierarchy)
yield return null ; yield return null ;
_sb . AppendLine ( Pre ( ) + " 보스 사라짐(비활성) → restored=" + CombatCameraDirector . BossPresetRestored + " (기대 2) active=" + CombatCameraDirector . BossPresetActive + " (기대 False) [1]=" + d1 . minDistance . ToString ( "F2" ) + "/" + d1 . maxDistance . ToString ( "F2" ) + "/" + d1 . cameraHeight . ToString ( "F0" ) + "° up=" + d1 . DistanceUp . ToString ( "F2" ) ) ;
yield return Wait ( st . bossCamSeconds + 0.2f ) ;
_sb . AppendLine ( Pre ( ) + "# 종료 " + CamRow ( ) + " | [1]=" + d1 . minDistance . ToString ( "F2" ) + "/" + d1 . maxDistance . ToString ( "F2" ) + "/" + d1 . cameraHeight . ToString ( "F0" ) + "° up=" + d1 . DistanceUp . ToString ( "F2" )
+ " · camActionList=" + ( list ! = null ? list . Count : - 1 ) + " (기대 " + list0 + ") · abandoned=" + CombatCameraDirector . KillCamAbandoned + " rebased=" + CombatCameraDirector . KillCamRebased + " presetApplied/restored/abandoned=" + CombatCameraDirector . BossPresetApplied + "/" + CombatCameraDirector . BossPresetRestored + "/" + CombatCameraDirector . BossPresetAbandoned
+ " · merge reentrant=" + DamageAggregator . ReentrantHits + " dispatching=" + DamageAggregator . Dispatching + " · reactionRunner=" + WLReactionRunner . Exists ) ;
Save ( "fixcheck" , _sb ) ; Cleanup ( ) ;
}
2026-09-08 15:20:16 +00:00
/// <summary>병합 후 실전투(메인 에디터 · Lead QA): 3 SO ON n초 / OFF n초 UnityStats 비교(WL785_Budget 스타일).</summary>
public IEnumerator Co_Budget ( float sec )
{
_sb = new StringBuilder ( ) ; _t0 = Time . unscaledTime ;
_sb . AppendLine ( "# WL811def Budget " + DateTime . Now . ToString ( "HH:mm:ss" ) + " " + sec + " s × 2" ) ;
for ( int pass = 0 ; pass < 2 ; pass + + )
{
bool on = pass = = 0 ;
WLCombatCameraSettings . RuntimeDisabled = ! on ; WLReactionSettings . RuntimeDisabled = ! on ; WLHitBlinkSettings . RuntimeDisabled = ! on ;
yield return Wait ( 0.3f ) ;
int n = 0 , mSet = 0 , mDraw = 0 ; long aSet = 0 , aDraw = 0 ; float worst = 0f , sum = 0f ; long gc0 = GC . GetTotalMemory ( false ) ;
int kc0 = CombatCameraDirector . KillCamCount , on0 = MobHitFlash . OnCount , fl0 = DamageAggregator . FlushCount , tr0 = KillChain . TierCount ;
float t = Time . unscaledTime ;
while ( Time . unscaledTime - t < sec )
{
yield return null ;
float dt = Time . unscaledDeltaTime ; worst = Mathf . Max ( worst , dt ) ; sum + = dt ; n + + ;
int sp = UnityEditor . UnityStats . setPassCalls , d = UnityEditor . UnityStats . drawCalls ;
mSet = Mathf . Max ( mSet , sp ) ; mDraw = Mathf . Max ( mDraw , d ) ; aSet + = sp ; aDraw + = d ;
}
_sb . AppendLine ( ( on ? "연출 ON " : "연출 OFF" ) + " avgFps=" + ( n / Mathf . Max ( sum , 0.001f ) ) . ToString ( "F1" ) + " worstFrame=" + ( worst * 1000f ) . ToString ( "F1" ) + " ms · setPass max/avg=" + mSet + "/" + ( n > 0 ? aSet / n : 0 ) + " draw max/avg=" + mDraw + "/" + ( n > 0 ? aDraw / n : 0 ) + " · GC+" + ( GC . GetTotalMemory ( false ) - gc0 ) + " B · killCam+" + ( CombatCameraDirector . KillCamCount - kc0 ) + " blinkOn+" + ( MobHitFlash . OnCount - on0 ) + " flush+" + ( DamageAggregator . FlushCount - fl0 ) + " tier+" + ( KillChain . TierCount - tr0 ) + " frames=" + n ) ;
}
WLCombatCameraSettings . RuntimeDisabled = false ; WLReactionSettings . RuntimeDisabled = false ; WLHitBlinkSettings . RuntimeDisabled = false ;
Save ( "budget" , _sb ) ; Destroy ( gameObject ) ;
}
}
}