// run_script entry points for the "background looks dark" investigation (2026-09-05 · WL = himminji base · Unity 6000.3 · URP 17) // unity command run_script --file AgentScripts/LightProbe.cs --entry LightProbe.Report // unity command run_script --file AgentScripts/LightProbe.cs --entry LightProbe.Render --args '["00_current"]' // unity command run_script --file AgentScripts/LightProbe.cs --entry LightProbe.SetAmbient --args '["trilight", 1.0]' // skybox | flat | trilight | update // unity command run_script --file AgentScripts/LightProbe.cs --entry LightProbe.SetTint --args '[0.55, 0.8, 1.0]' // Toon Pro shadow/middle/light tint (property block · not persisted) // unity command run_script --file AgentScripts/LightProbe.cs --entry LightProbe.SetLight --args '[1.0]' // directional light intensity // unity command run_script --file AgentScripts/LightProbe.cs --entry LightProbe.Invoke --args '["TitleInfo", "OnClick_DevLogin"]' // PowerShell 5.1: escape the JSON quotes — --args '[\"trilight\", 1.0]' // Everything here is read-only or runtime-only (property blocks / RenderSettings in Play mode revert on stop). Renders go to Screenshots_WL/dark/ (outside Assets). using System; using System.Collections.Generic; using System.Reflection; using System.Text; using UnityEditor; using UnityEngine; using UnityEngine.Rendering; public static class LightProbe { const string OutDir = "Screenshots_WL/dark"; static void Section(StringBuilder sb, string name, Action body) { try { body(); } catch (Exception e) { sb.Append("[").Append(name).Append(" failed: ").Append(e.GetType().Name).Append(' ').Append(e.Message).Append("]\n"); } } // property-or-field reader without compile-time dependency (URP/Core RP types) static object Member(object obj, string name) { if (obj == null) return null; var t = obj.GetType(); var pi = t.GetProperty(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (pi != null) return pi.GetValue(obj); var fi = t.GetField(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (fi != null) return fi.GetValue(obj); return "(no " + name + ")"; } // ---------- state report ---------- public static object Report() { var sb = new StringBuilder(); Section(sb, "scene", () => { sb.Append("playing=").Append(Application.isPlaying).Append(" activeScene=").Append(UnityEngine.SceneManagement.SceneManager.GetActiveScene().name).Append(" loadedScenes="); for (int i = 0; i < UnityEngine.SceneManagement.SceneManager.sceneCount; i++) sb.Append(UnityEngine.SceneManagement.SceneManager.GetSceneAt(i).name).Append(','); sb.Append('\n'); }); Section(sb, "renderSettings", () => { sb.Append("ambientMode=").Append(RenderSettings.ambientMode).Append(" ambientIntensity=").Append(RenderSettings.ambientIntensity.ToString("F2")) .Append(" reflectionIntensity=").Append(RenderSettings.reflectionIntensity.ToString("F2")).Append(" fog=").Append(RenderSettings.fog).Append('\n'); sb.Append("ambientLight=").Append(C(RenderSettings.ambientLight)).Append(" sky=").Append(C(RenderSettings.ambientSkyColor)) .Append(" equator=").Append(C(RenderSettings.ambientEquatorColor)).Append(" ground=").Append(C(RenderSettings.ambientGroundColor)).Append('\n'); sb.Append("skybox=").Append(RenderSettings.skybox ? RenderSettings.skybox.name + " (" + RenderSettings.skybox.shader.name + ")" : "null") .Append(" sun=").Append(RenderSettings.sun ? RenderSettings.sun.name : "null").Append('\n'); var dirs = new[] { Vector3.up, Vector3.down, Vector3.forward, Vector3.right }; var res = new Color[dirs.Length]; RenderSettings.ambientProbe.Evaluate(dirs, res); sb.Append("ambientProbe up=").Append(C(res[0])).Append(" down=").Append(C(res[1])).Append(" fwd=").Append(C(res[2])).Append(" right=").Append(C(res[3])).Append('\n'); }); Section(sb, "quality", () => { sb.Append("quality=").Append(QualitySettings.GetQualityLevel()).Append(':').Append(QualitySettings.names[QualitySettings.GetQualityLevel()]) .Append(" shadows=").Append(QualitySettings.shadows).Append(" colorSpace=").Append(QualitySettings.activeColorSpace) .Append(" pipeline=").Append(GraphicsSettings.currentRenderPipeline ? GraphicsSettings.currentRenderPipeline.name : "null").Append('\n'); var rp = GraphicsSettings.currentRenderPipeline; if (rp != null) foreach (var pn in new[] { "supportsMainLightShadows", "supportsHDR", "msaaSampleCount", "shadowDistance", "useRenderingLayers", "supportsCameraDepthTexture" }) sb.Append(" urp.").Append(pn).Append('=').Append(Member(rp, pn)).Append('\n'); }); Section(sb, "lights", () => { var lights = UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); sb.Append("lights=").Append(lights.Length).Append('\n'); foreach (var l in lights) sb.Append(" ").Append(Path(l.transform)).Append(" type=").Append(l.type).Append(" enabled=").Append(l.enabled).Append(" activeInHierarchy=").Append(l.gameObject.activeInHierarchy) .Append(" intensity=").Append(l.intensity.ToString("F2")).Append(" color=").Append(C(l.color)).Append(" euler=").Append(l.transform.eulerAngles.ToString("F0")) .Append(" shadows=").Append(l.shadows).Append(" cullingMask=").Append(l.cullingMask).Append(" renderingLayerMask=").Append(l.renderingLayerMask).Append('\n'); }); Section(sb, "cameras", () => { var cams = UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); sb.Append("cameras=").Append(cams.Length).Append(" main=").Append(Camera.main ? Path(Camera.main.transform) : "null").Append('\n'); var acdType = Type.GetType("UnityEngine.Rendering.Universal.UniversalAdditionalCameraData, Unity.RenderPipelines.Universal.Runtime"); foreach (var c in cams) { sb.Append(" ").Append(Path(c.transform)).Append(" enabled=").Append(c.enabled).Append(" depth=").Append(c.depth).Append(" pos=").Append(c.transform.position.ToString("F1")) .Append(" euler=").Append(c.transform.eulerAngles.ToString("F0")).Append(" fov=").Append(c.fieldOfView.ToString("F1")).Append(" ortho=").Append(c.orthographic) .Append(" cullingMask=").Append(c.cullingMask).Append(" clearFlags=").Append(c.clearFlags); var acd = acdType != null ? c.GetComponent(acdType) : null; if (acd != null) foreach (var pn in new[] { "renderType", "renderPostProcessing", "volumeLayerMask", "antialiasing", "renderShadows" }) sb.Append(' ').Append(pn).Append('=').Append(Member(acd, pn)); sb.Append('\n'); } }); Section(sb, "volumes", () => { var volType = Type.GetType("UnityEngine.Rendering.Volume, Unity.RenderPipelines.Core.Runtime"); if (volType == null) { sb.Append("volumes: type not found\n"); return; } var vols = UnityEngine.Object.FindObjectsByType(volType, FindObjectsInactive.Include, FindObjectsSortMode.None); sb.Append("volumes=").Append(vols.Length).Append('\n'); foreach (var v in vols) { var beh = (Behaviour)v; var prof = Member(v, "sharedProfile") as ScriptableObject; sb.Append(" ").Append(Path(beh.transform)).Append(" enabled=").Append(beh.enabled).Append(" activeInHierarchy=").Append(beh.gameObject.activeInHierarchy) .Append(" global=").Append(Member(v, "isGlobal")).Append(" weight=").Append(Member(v, "weight")).Append(" priority=").Append(Member(v, "priority")) .Append(" profile=").Append(prof ? prof.name : "null"); var comps = prof != null ? Member(prof, "components") as System.Collections.IList : null; if (comps != null) { sb.Append(" components=["); foreach (var comp in comps) { var so = comp as ScriptableObject; if (so == null) continue; var active = Member(so, "active"); sb.Append(so.GetType().Name).Append(active is bool b && !b ? "(off)" : "").Append(','); } sb.Append(']'); } sb.Append('\n'); } }); Section(sb, "census", () => { var rends = UnityEngine.Object.FindObjectsByType(FindObjectsSortMode.None); var byShader = new Dictionary(); int slots = 0; foreach (var r in rends) { if (r is ParticleSystemRenderer) continue; foreach (var m in r.sharedMaterials) { if (m == null || m.shader == null) continue; slots++; byShader[m.shader.name] = byShader.TryGetValue(m.shader.name, out var n) ? n + 1 : 1; } } sb.Append("renderers=").Append(rends.Length).Append(" materialSlots=").Append(slots).Append('\n'); foreach (var kv in byShader) sb.Append(" ").Append(kv.Value).Append(" ").Append(kv.Key).Append('\n'); }); return sb.ToString(); } // ---------- render from the main camera's pose ---------- public static object Render(string tag) { var cam = Camera.main; if (cam == null) return "no Camera.main"; return RenderFrom(cam, tag); } static object RenderFrom(Camera cam, string tag) { System.IO.Directory.CreateDirectory(OutDir); int w = 1280, h = 720; var rt = new RenderTexture(w, h, 24, RenderTextureFormat.ARGB32); var go = new GameObject("__lightProbeCam"); var c = go.AddComponent(); c.CopyFrom(cam); c.targetTexture = rt; c.enabled = false; c.Render(); var prev = RenderTexture.active; RenderTexture.active = rt; var tex = new Texture2D(w, h, TextureFormat.RGB24, false); tex.ReadPixels(new Rect(0, 0, w, h), 0, 0); tex.Apply(); RenderTexture.active = prev; var px = tex.GetPixels32(); double sum = 0; int dark = 0, clip = 0; foreach (var p in px) { double l = 0.2126 * p.r + 0.7152 * p.g + 0.0722 * p.b; sum += l; if (l < 40) dark++; if (l > 245) clip++; } float mean = (float)(sum / px.Length / 255.0); float darkRatio = (float)dark / px.Length; float clipRatio = (float)clip / px.Length; var path = System.IO.Path.Combine(OutDir, tag + ".png"); System.IO.File.WriteAllBytes(path, tex.EncodeToPNG()); UnityEngine.Object.DestroyImmediate(tex); c.targetTexture = null; UnityEngine.Object.DestroyImmediate(go); rt.Release(); UnityEngine.Object.DestroyImmediate(rt); return path + " meanLum=" + mean.ToString("F3") + " darkRatio(<40/255)=" + darkRatio.ToString("F3") + " clipRatio(>245/255)=" + clipRatio.ToString("F3") + " camPos=" + cam.transform.position.ToString("F1") + " camEuler=" + cam.transform.eulerAngles.ToString("F0"); } // Trilight with explicit linear colors (A/B of candidate MapData defaults without recompiling) public static object SetTrilightRGB(float sr, float sg, float sb, float er, float eg, float eb, float gr, float gg, float gb) { RenderSettings.ambientMode = AmbientMode.Trilight; RenderSettings.ambientSkyColor = new Color(sr, sg, sb, 1f); RenderSettings.ambientEquatorColor = new Color(er, eg, eb, 1f); RenderSettings.ambientGroundColor = new Color(gr, gg, gb, 1f); var dirs = new[] { Vector3.up, Vector3.down }; var res = new Color[2]; RenderSettings.ambientProbe.Evaluate(dirs, res); return "trilight sky=" + C(RenderSettings.ambientSkyColor) + " equator=" + C(RenderSettings.ambientEquatorColor) + " ground=" + C(RenderSettings.ambientGroundColor) + " probe up=" + C(res[0]) + " down=" + C(res[1]); } // Edit-mode only: set the directional light intensity inside a map prefab asset (data lives in the prefab · C45) public static object SetPrefabDirectionalLight(string prefabPath, float intensity) { if (Application.isPlaying) return "refused: not in Play mode (asset edit)"; var root = PrefabUtility.LoadPrefabContents(prefabPath); int n = 0; var before = new StringBuilder(); foreach (var l in root.GetComponentsInChildren(true)) if (l.type == LightType.Directional) { before.Append(l.name).Append('=').Append(l.intensity.ToString("F2")).Append(' '); l.intensity = intensity; n++; } PrefabUtility.SaveAsPrefabAsset(root, prefabPath); PrefabUtility.UnloadPrefabContents(root); return prefabPath + " directional lights " + n + " (was " + before.ToString().Trim() + ") -> " + intensity; } public static object InvokeInt(string typeName, string method, int arg) { var t = Type.GetType(typeName + ", Assembly-CSharp"); if (t == null) return "type not found: " + typeName; var obj = UnityEngine.Object.FindFirstObjectByType(t, FindObjectsInactive.Include); if (obj == null) return "instance not found: " + typeName; var mi = t.GetMethod(method, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(int) }, null); if (mi == null) return "method(int) not found: " + method; var r = mi.Invoke(obj, new object[] { arg }); return "invoked " + typeName + "." + method + "(" + arg + ") -> " + (r ?? "void"); } // Dump public instance fields (Color/float/int/bool/enum/string) of the first live instance of a game type public static object DumpFields(string typeName) { var t = Type.GetType(typeName + ", Assembly-CSharp"); if (t == null) return "type not found: " + typeName; var objs = UnityEngine.Object.FindObjectsByType(t, FindObjectsInactive.Include, FindObjectsSortMode.None); if (objs.Length == 0) return "no instance: " + typeName; var sb = new StringBuilder(); foreach (var o in objs) { var comp = o as Component; sb.Append(comp != null ? Path(comp.transform) : o.name).Append(" (").Append(o.GetInstanceID()).Append(")\n"); foreach (var fi in t.GetFields(BindingFlags.Public | BindingFlags.Instance)) { var ft = fi.FieldType; if (ft == typeof(Color) || ft == typeof(float) || ft == typeof(int) || ft == typeof(bool) || ft.IsEnum || ft == typeof(string)) { var v = fi.GetValue(o); sb.Append(" ").Append(fi.Name).Append(" = ").Append(v is Color c ? C(c) : (v ?? "null").ToString()).Append('\n'); } } } return sb.ToString(); } // Edit-mode only: write MapData ambient colors into a map prefab (values live in the prefab · C45) public static object SetPrefabMapDataAmbient(string prefabPath, float sr, float sg, float sb, float er, float eg, float eb, float gr, float gg, float gb) { if (Application.isPlaying) return "refused: not in Play mode (asset edit)"; var t = Type.GetType("MapData, Assembly-CSharp"); if (t == null) return "MapData type not found"; var root = PrefabUtility.LoadPrefabContents(prefabPath); var md = root.GetComponentInChildren(t, true); if (md == null) { PrefabUtility.UnloadPrefabContents(root); return "no MapData in " + prefabPath; } t.GetField("ambientSky").SetValue(md, new Color(sr, sg, sb, 1f)); t.GetField("ambientEquator").SetValue(md, new Color(er, eg, eb, 1f)); t.GetField("ambientGround").SetValue(md, new Color(gr, gg, gb, 1f)); PrefabUtility.SaveAsPrefabAsset(root, prefabPath); PrefabUtility.UnloadPrefabContents(root); return prefabPath + " MapData ambient -> sky(" + sr + "," + sg + "," + sb + ") equator(" + er + "," + eg + "," + eb + ") ground(" + gr + "," + gg + "," + gb + ")"; } // Edit-mode: read the compiled C# default of a MonoBehaviour field by instantiating a throwaway component (detects stale compiles) public static object CheckDefault(string typeName, string field) { var t = Type.GetType(typeName + ", Assembly-CSharp"); if (t == null) return "type not found: " + typeName; var go = new GameObject("__defaultProbe"); try { var c = go.AddComponent(t); var fi = t.GetField(field); if (fi == null) return "field not found: " + field; var v = fi.GetValue(c); return typeName + "." + field + " compiled default = " + (v is Color col ? C(col) : (v ?? "null").ToString()); } finally { UnityEngine.Object.DestroyImmediate(go); } } // Edit-mode: force asset re-import of changed scripts and request compilation (the editor does not auto-refresh while unfocused) public static object RefreshAndCompile() { if (Application.isPlaying) return "refused: Play mode"; AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation(); return "AssetDatabase.Refresh + RequestScriptCompilation issued"; } // Edit-mode: force re-import of map prefabs so in-memory prefab assets drop stale serialized field values // (after adding/changing a [SerializeField] default, assets already loaded in the editor keep the OLD value across domain reloads) public static object ReimportMapPrefabs() { if (Application.isPlaying) return "refused: Play mode"; var guids = AssetDatabase.FindAssets("t:Prefab", new[] { "Assets/Res_Addr/Map" }); var sb = new StringBuilder(); foreach (var g in guids) { var p = AssetDatabase.GUIDToAssetPath(g); AssetDatabase.ImportAsset(p, ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport); sb.Append(System.IO.Path.GetFileNameWithoutExtension(p)).Append(' '); } Resources.UnloadUnusedAssets(); return "reimported " + guids.Length + ": " + sb.ToString().Trim(); } // Edit-mode: add a game component (by type name · Assembly-CSharp) to a prefab root if missing, then save the prefab public static object AddComponentToPrefabRoot(string prefabPath, string typeName) { if (Application.isPlaying) return "refused: Play mode"; var t = Type.GetType(typeName + ", Assembly-CSharp"); if (t == null) return "type not found: " + typeName; var root = PrefabUtility.LoadPrefabContents(prefabPath); try { if (root.GetComponent(t) != null) return prefabPath + " already has " + typeName; root.AddComponent(t); PrefabUtility.SaveAsPrefabAsset(root, prefabPath); return prefabPath + " += " + typeName + " (root)"; } finally { PrefabUtility.UnloadPrefabContents(root); } } // Dump ALL instance fields (public + private) incl. List/List/List of the first live instance public static object DumpFieldsAll(string typeName) { var t = Type.GetType(typeName + ", Assembly-CSharp"); if (t == null) return "type not found: " + typeName; var o = UnityEngine.Object.FindFirstObjectByType(t, FindObjectsInactive.Include); if (o == null) return "no instance: " + typeName; var sb = new StringBuilder(); var comp = o as Component; sb.Append(comp != null ? Path(comp.transform) + " activeInHierarchy=" + comp.gameObject.activeInHierarchy : o.name).Append('\n'); for (var tt = t; tt != null && tt != typeof(MonoBehaviour); tt = tt.BaseType) foreach (var fi in tt.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)) { var v = fi.GetValue(o); string s; if (v == null) s = "null"; else if (v is System.Collections.IList list && !(v is Array && ((Array)v).Rank > 1)) { var parts = new List(); foreach (var it in list) parts.Add(it is UnityEngine.Object uo ? (uo ? uo.name : "null") : (it ?? "null").ToString()); s = "[" + string.Join(",", parts) + "] (" + list.Count + ")"; } else if (v is UnityEngine.Object uo2) s = uo2 ? uo2.name + (uo2 is GameObject g ? " active=" + g.activeInHierarchy : "") : "null"; else if (v is Color c) s = C(c); else s = v.ToString(); if (s.Length > 160) s = s.Substring(0, 160) + "…"; sb.Append(" ").Append(fi.Name).Append(" = ").Append(s).Append('\n'); } return sb.ToString(); } // Edit-mode: set MapData ambient mode/intensity/updateEnvironment flag in a map prefab (C45 · values live in the prefab) public static object SetPrefabMapDataAmbientMode(string prefabPath, string mode, float intensity, bool updateEnv) { if (Application.isPlaying) return "refused: not in Play mode (asset edit)"; var t = Type.GetType("MapData, Assembly-CSharp"); if (t == null) return "MapData type not found"; AmbientMode am; switch (mode) { case "skybox": am = AmbientMode.Skybox; break; case "flat": am = AmbientMode.Flat; break; case "trilight": am = AmbientMode.Trilight; break; default: return "unknown mode " + mode; } var root = PrefabUtility.LoadPrefabContents(prefabPath); try { var md = root.GetComponentInChildren(t, true); if (md == null) return "no MapData in " + prefabPath; t.GetField("ambientMode").SetValue(md, am); t.GetField("ambientIntensity").SetValue(md, intensity); t.GetField("updateEnvironmentFromSkybox").SetValue(md, updateEnv); var sky = t.GetField("material_skybox").GetValue(md) as Material; PrefabUtility.SaveAsPrefabAsset(root, prefabPath); return prefabPath + " MapData ambientMode=" + am + " intensity=" + intensity + " updateEnv=" + updateEnv + " skybox=" + (sky ? sky.name : "NULL"); } finally { PrefabUtility.UnloadPrefabContents(root); } } // Edit-mode: point an AnimatorController state's motion to an AnimationClip sub-asset (by clip name) of an FBX/anim asset public static object SetControllerStateMotion(string controllerPath, string stateName, string clipAssetPath, string clipName) { if (Application.isPlaying) return "refused: Play mode"; var ctrl = AssetDatabase.LoadAssetAtPath(controllerPath); if (ctrl == null) return "controller not found: " + controllerPath; AnimationClip clip = null; foreach (var o in AssetDatabase.LoadAllAssetRepresentationsAtPath(clipAssetPath)) if (o is AnimationClip ac && (string.IsNullOrEmpty(clipName) || ac.name == clipName)) { clip = ac; break; } if (clip == null) clip = AssetDatabase.LoadAssetAtPath(clipAssetPath); if (clip == null) return "clip not found: " + clipAssetPath + " / " + clipName; int n = 0; string was = ""; foreach (var layer in ctrl.layers) foreach (var cs in layer.stateMachine.states) if (cs.state.name == stateName) { was = cs.state.motion ? cs.state.motion.name : "null"; cs.state.motion = clip; n++; } if (n == 0) return "state not found: " + stateName; EditorUtility.SetDirty(ctrl); AssetDatabase.SaveAssets(); return controllerPath + " state '" + stateName + "' motion " + was + " -> " + clip.name + " (" + clip.length.ToString("F2") + "s · " + n + " state(s))"; } // Play-mode: what is the player's animator playing right now? public static object PlayerAnimInfo() { var mv = Type.GetType("MyValue, Assembly-CSharp"); if (mv == null) return "MyValue not found"; var pcProp = mv.GetProperty("MyPC", BindingFlags.Public | BindingFlags.Static) ?? null; object pc = pcProp != null ? pcProp.GetValue(null) : mv.GetField("MyPC", BindingFlags.Public | BindingFlags.Static)?.GetValue(null); var comp = pc as Component; if (comp == null) return "MyPC null"; var anim = comp.GetComponentInChildren(); if (anim == null) return "no Animator on " + Path(comp.transform); var sb = new StringBuilder(); sb.Append("pc=").Append(Path(comp.transform)).Append(" pos=").Append(comp.transform.position.ToString("F2")) .Append(" controller=").Append(anim.runtimeAnimatorController ? anim.runtimeAnimatorController.name : "null").Append(" speed=").Append(anim.speed).Append('\n'); var info = anim.GetCurrentAnimatorClipInfo(0); foreach (var ci in info) sb.Append(" clip=").Append(ci.clip.name).Append(" w=").Append(ci.weight.ToString("F2")).Append(" len=").Append(ci.clip.length.ToString("F2")).Append('\n'); var st = anim.GetCurrentAnimatorStateInfo(0); sb.Append(" stateHash=").Append(st.shortNameHash).Append(" normalizedTime=").Append(st.normalizedTime.ToString("F2")).Append('\n'); return sb.ToString(); } // Play-mode: are weapons attached under the player's weapon sockets? public static object PlayerWeaponInfo() { var mv = Type.GetType("MyValue, Assembly-CSharp"); var pc = mv?.GetProperty("MyPC", BindingFlags.Public | BindingFlags.Static)?.GetValue(null) as Component; if (pc == null) return "MyPC null"; var fi = pc.GetType().GetField("tfs_weapon", BindingFlags.Public | BindingFlags.Instance); var sockets = fi?.GetValue(pc) as Transform[]; if (sockets == null) return "tfs_weapon not found"; var sb = new StringBuilder(); int total = 0; for (int i = 0; i < sockets.Length; i++) { var s = sockets[i]; if (s == null) { sb.Append(" [").Append(i).Append("] null\n"); continue; } sb.Append(" [").Append(i).Append("] ").Append(Path(s)).Append(" children=").Append(s.childCount); for (int c = 0; c < s.childCount; c++) { var ch = s.GetChild(c); total++; sb.Append(" · ").Append(ch.name).Append(ch.gameObject.activeInHierarchy ? "(on)" : "(off)"); var r = ch.GetComponentInChildren(); if (r) sb.Append(" renderer=").Append(r.enabled ? "on" : "off"); } sb.Append('\n'); } return "weapon objects=" + total + "\n" + sb.ToString(); } // Play-mode: drive the player's joystick path n times (same frame) and report distance → speed = dist / (n · dt) public static object JoystickBurst(int n) { var mv = Type.GetType("MyValue, Assembly-CSharp"); var pcProp = mv?.GetProperty("MyPC", BindingFlags.Public | BindingFlags.Static); var pc = pcProp?.GetValue(null) as Component; if (pc == null) return "MyPC null"; var mi = pc.GetType().GetMethod("Run_byJoystick", BindingFlags.Public | BindingFlags.Instance); var gm = pc.GetType().GetMethod("Get_MoveSpeed", BindingFlags.Public | BindingFlags.Instance); if (mi == null) return "Run_byJoystick not found"; var before = pc.transform.position; var dir = pc.transform.forward; dir.y = 0; dir.Normalize(); for (int i = 0; i < n; i++) mi.Invoke(pc, new object[] { dir }); var after = pc.transform.position; float dist = Vector3.Distance(before, after); float dt = Time.deltaTime; return "n=" + n + " dt=" + dt.ToString("F4") + " dist=" + dist.ToString("F3") + " -> speed≈" + (dt > 0 ? (dist / (n * dt)).ToString("F2") : "?") + " m/s · Get_MoveSpeed=" + (gm != null ? gm.Invoke(pc, null).ToString() : "?"); } // Play-mode: list active UI Images/RawImages covering most of the screen (overlay/fade suspects) with color & alpha public static object OverlayCensus() { var sb = new StringBuilder(); int n = 0; foreach (var g in UnityEngine.Object.FindObjectsByType(FindObjectsSortMode.None)) { if (!g.isActiveAndEnabled) continue; var rt = g.rectTransform; var canvas = g.canvas; if (canvas == null) continue; var corners = new Vector3[4]; rt.GetWorldCorners(corners); var cam = canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera; Vector2 min = new Vector2(float.MaxValue, float.MaxValue), max = new Vector2(float.MinValue, float.MinValue); foreach (var c in corners) { Vector2 sp = cam ? (Vector2)cam.WorldToScreenPoint(c) : (Vector2)c; min = Vector2.Min(min, sp); max = Vector2.Max(max, sp); } float cover = Mathf.Clamp01((max.x - min.x) / Screen.width) * Mathf.Clamp01((max.y - min.y) / Screen.height); if (cover < 0.6f) continue; var col = g.color; var cg = g.GetComponentInParent(); float a = col.a * (cg ? cg.alpha : 1f); if (a <= 0.01f) continue; n++; sb.Append(" ").Append(Path(g.transform)).Append(" type=").Append(g.GetType().Name).Append(" cover=").Append(cover.ToString("F2")) .Append(" color=").Append(C(col)).Append(" a=").Append(a.ToString("F2")).Append(" canvas=").Append(canvas.name).Append(" order=").Append(canvas.sortingOrder) .Append(" mat=").Append(g.material ? g.material.name : "null"); if (g is UnityEngine.UI.Image im) sb.Append(" sprite=").Append(im.sprite ? im.sprite.name : "null"); if (g is UnityEngine.UI.RawImage ri) sb.Append(" tex=").Append(ri.texture ? ri.texture.name : "null"); sb.Append('\n'); } return "fullscreen-ish visible graphics = " + n + "\n" + sb.ToString(); } // Edit-mode: add (or update) a directional light child on a map prefab root · values live in the prefab (C45) public static object AddDirectionalLightToPrefab(string prefabPath, string goName, float intensity, float r, float g, float b, float rotX, float rotY, float rotZ) { if (Application.isPlaying) return "refused: Play mode"; var root = PrefabUtility.LoadPrefabContents(prefabPath); try { Light light = null; foreach (var l in root.GetComponentsInChildren(true)) if (l.type == LightType.Directional) { light = l; break; } string action; if (light == null) { var go = new GameObject(goName); go.transform.SetParent(root.transform, false); light = go.AddComponent(); light.type = LightType.Directional; light.shadows = LightShadows.Soft; action = "added"; } else action = "updated " + light.name; light.intensity = intensity; light.color = new Color(r, g, b, 1f); light.transform.rotation = Quaternion.Euler(rotX, rotY, rotZ); PrefabUtility.SaveAsPrefabAsset(root, prefabPath); return prefabPath + " directional light " + action + ": intensity=" + intensity + " color=" + C(light.color) + " euler=(" + rotX + "," + rotY + "," + rotZ + ")"; } finally { PrefabUtility.UnloadPrefabContents(root); } } // Edit-mode: set a bool field on MapData inside a map prefab (e.g. showWeaponInLobby) public static object SetPrefabMapDataBool(string prefabPath, string field, bool value) { if (Application.isPlaying) return "refused: Play mode"; var t = Type.GetType("MapData, Assembly-CSharp"); var fi = t?.GetField(field); if (fi == null) return "MapData field not found: " + field + " (recompile first?)"; var root = PrefabUtility.LoadPrefabContents(prefabPath); try { var md = root.GetComponentInChildren(t, true); if (md == null) return "no MapData"; fi.SetValue(md, value); PrefabUtility.SaveAsPrefabAsset(root, prefabPath); return prefabPath + " MapData." + field + "=" + value; } finally { PrefabUtility.UnloadPrefabContents(root); } } // Edit-mode: set a float field on the first component of a given type (Assembly-CSharp) inside a prefab public static object SetPrefabComponentFloat(string prefabPath, string typeName, string field, float value) { if (Application.isPlaying) return "refused: Play mode"; var t = Type.GetType(typeName + ", Assembly-CSharp"); var fi = t?.GetField(field, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (fi == null) return "field not found: " + typeName + "." + field; var root = PrefabUtility.LoadPrefabContents(prefabPath); try { var c = root.GetComponentInChildren(t, true); if (c == null) return "no " + typeName + " in " + prefabPath; var was = fi.GetValue(c); fi.SetValue(c, Convert.ChangeType(value, fi.FieldType)); PrefabUtility.SaveAsPrefabAsset(root, prefabPath); return prefabPath + " " + typeName + "." + field + " " + was + " -> " + value; } finally { PrefabUtility.UnloadPrefabContents(root); } } // Edit-mode: set a float serialized property (supports paths like arr_cCamData.Array.data[0].cameraHeight) on the first component of a type inside a prefab public static object SetPrefabSerializedFloat(string prefabPath, string typeName, string propertyPath, float value) { if (Application.isPlaying) return "refused: Play mode"; var t = Type.GetType(typeName + ", Assembly-CSharp"); if (t == null) return "type not found: " + typeName; var root = PrefabUtility.LoadPrefabContents(prefabPath); try { var c = root.GetComponentInChildren(t, true); if (c == null) return "no " + typeName + " in " + prefabPath; var ser = new SerializedObject(c); var p = ser.FindProperty(propertyPath); if (p == null) return "property not found: " + propertyPath; string was = p.propertyType == SerializedPropertyType.Float ? p.floatValue.ToString("F3") : p.propertyType.ToString(); if (p.propertyType != SerializedPropertyType.Float) return "not a float: " + propertyPath + " (" + p.propertyType + ")"; p.floatValue = value; ser.ApplyModifiedPropertiesWithoutUndo(); PrefabUtility.SaveAsPrefabAsset(root, prefabPath); return prefabPath + " " + typeName + "." + propertyPath + " " + was + " -> " + value; } finally { PrefabUtility.UnloadPrefabContents(root); } } // Edit-mode: set a float field on a ScriptableObject asset (e.g. CameraLeadSettings.asset) public static object SetAssetFloat(string assetPath, string field, float value) { if (Application.isPlaying) return "refused: Play mode"; var so = AssetDatabase.LoadMainAssetAtPath(assetPath) as ScriptableObject; if (so == null) return "asset not found: " + assetPath; var ser = new SerializedObject(so); var p = ser.FindProperty(field); if (p == null) return "field not found: " + field + " (recompiled?)"; string was = p.propertyType == SerializedPropertyType.Float ? p.floatValue.ToString("F3") : p.propertyType.ToString(); if (p.propertyType == SerializedPropertyType.Float) p.floatValue = value; else if (p.propertyType == SerializedPropertyType.Boolean) p.boolValue = value > 0.5f; else return "unsupported type " + p.propertyType; ser.ApplyModifiedPropertiesWithoutUndo(); EditorUtility.SetDirty(so); AssetDatabase.SaveAssets(); return assetPath + " " + field + " " + was + " -> " + value; } // Edit-mode: set MapData.ambientIntensity only public static object SetPrefabAmbientIntensity(string prefabPath, float intensity) { if (Application.isPlaying) return "refused: Play mode"; var t = Type.GetType("MapData, Assembly-CSharp"); var root = PrefabUtility.LoadPrefabContents(prefabPath); try { var md = root.GetComponentInChildren(t, true); if (md == null) return "no MapData"; t.GetField("ambientIntensity").SetValue(md, intensity); PrefabUtility.SaveAsPrefabAsset(root, prefabPath); return prefabPath + " ambientIntensity=" + intensity; } finally { PrefabUtility.UnloadPrefabContents(root); } } // Edit-mode: enable/disable every Volume (post-processing) component inside a prefab · reversible data change public static object SetPrefabVolumesEnabled(string prefabPath, bool enabled) { if (Application.isPlaying) return "refused: Play mode"; var volType = Type.GetType("UnityEngine.Rendering.Volume, Unity.RenderPipelines.Core.Runtime"); if (volType == null) return "Volume type not found"; var root = PrefabUtility.LoadPrefabContents(prefabPath); try { var sb = new StringBuilder(); int n = 0; foreach (var c in root.GetComponentsInChildren(volType, true)) { var beh = (Behaviour)c; beh.enabled = enabled; n++; var prof = Member(c, "sharedProfile") as ScriptableObject; sb.Append(Path(beh.transform)).Append(" profile=").Append(prof ? prof.name : "null").Append(" -> enabled=").Append(enabled).Append("; "); } if (n == 0) return "no Volume in " + prefabPath; PrefabUtility.SaveAsPrefabAsset(root, prefabPath); return prefabPath + " volumes=" + n + ": " + sb.ToString(); } finally { PrefabUtility.UnloadPrefabContents(root); } } // Edit-mode: clone a VolumeProfile asset (keeps all overrides) so we never edit the pack's original public static object ClonePostProfile(string srcPath, string dstPath) { if (Application.isPlaying) return "refused: Play mode"; var dir = System.IO.Path.GetDirectoryName(dstPath).Replace('\\', '/'); if (!AssetDatabase.IsValidFolder(dir)) { var parent = System.IO.Path.GetDirectoryName(dir).Replace('\\', '/'); AssetDatabase.CreateFolder(parent, System.IO.Path.GetFileName(dir)); } if (AssetDatabase.LoadMainAssetAtPath(dstPath) != null) return dstPath + " already exists"; bool ok = AssetDatabase.CopyAsset(srcPath, dstPath); AssetDatabase.SaveAssets(); return (ok ? "cloned " : "FAILED ") + srcPath + " -> " + dstPath; } // Edit-mode: set a volume parameter on a profile: component type short name (e.g. ColorAdjustments), parameter field (e.g. postExposure), value. // kind: "float" | "bool"(active flag of the component when field=="active") | "int" public static object SetProfileParam(string profilePath, string componentType, string field, float value) { if (Application.isPlaying) return "refused: Play mode"; var prof = AssetDatabase.LoadMainAssetAtPath(profilePath) as ScriptableObject; if (prof == null) return "profile not found: " + profilePath; var comps = Member(prof, "components") as System.Collections.IList; if (comps == null) return "no components list"; foreach (var c in comps) { var so = c as ScriptableObject; if (so == null || so.GetType().Name != componentType) continue; var ser = new SerializedObject(so); if (field == "active") { var a = ser.FindProperty("active"); if (a == null) return "no active"; a.boolValue = value > 0.5f; ser.ApplyModifiedPropertiesWithoutUndo(); EditorUtility.SetDirty(so); AssetDatabase.SaveAssets(); return componentType + ".active=" + a.boolValue; } var pv = ser.FindProperty(field + ".m_Value"); var po = ser.FindProperty(field + ".m_OverrideState"); if (pv == null) return componentType + "." + field + " not found"; string was = pv.propertyType == SerializedPropertyType.Float ? pv.floatValue.ToString("F3") : pv.propertyType == SerializedPropertyType.Integer ? pv.intValue.ToString() : pv.propertyType.ToString(); if (pv.propertyType == SerializedPropertyType.Float) pv.floatValue = value; else if (pv.propertyType == SerializedPropertyType.Integer) pv.intValue = (int)value; else return "unsupported type " + pv.propertyType; if (po != null) po.boolValue = true; ser.ApplyModifiedPropertiesWithoutUndo(); EditorUtility.SetDirty(so); AssetDatabase.SaveAssets(); return componentType + "." + field + " " + was + " -> " + value; } return "component not found: " + componentType; } // Edit-mode: assign a profile to every Volume in a prefab and set enabled public static object SetPrefabVolumeProfile(string prefabPath, string profilePath, bool enabled) { if (Application.isPlaying) return "refused: Play mode"; var volType = Type.GetType("UnityEngine.Rendering.Volume, Unity.RenderPipelines.Core.Runtime"); var prof = AssetDatabase.LoadMainAssetAtPath(profilePath); if (prof == null) return "profile not found: " + profilePath; var root = PrefabUtility.LoadPrefabContents(prefabPath); try { int n = 0; var sb = new StringBuilder(); foreach (var c in root.GetComponentsInChildren(volType, true)) { var ser = new SerializedObject(c); var sp = ser.FindProperty("sharedProfile"); if (sp == null) return "Volume has no 'sharedProfile' serialized property (fields: " + string.Join(",", ListProps(ser)) + ")"; sp.objectReferenceValue = prof; var en = ser.FindProperty("m_Enabled"); if (en != null) en.boolValue = enabled; ser.ApplyModifiedPropertiesWithoutUndo(); ((Behaviour)c).enabled = enabled; n++; sb.Append(Path(((Component)c).transform)).Append(' '); } PrefabUtility.SaveAsPrefabAsset(root, prefabPath); return prefabPath + " volumes=" + n + " (" + sb.ToString().Trim() + ") -> profile " + prof.name + " enabled=" + enabled; } finally { PrefabUtility.UnloadPrefabContents(root); } } public static object ReadStatic(string typeName, string member) { var t = Type.GetType(typeName + ", Assembly-CSharp"); if (t == null) return "type not found: " + typeName; var pi = t.GetProperty(member, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (pi != null) return member + " = " + (pi.GetValue(null) ?? "null"); var fi = t.GetField(member, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (fi != null) return member + " = " + (fi.GetValue(null) ?? "null"); return "static member not found: " + member; } // Render through the REAL main camera (post-processing/HDR included) into a temp RT → PNG + luminance. Truth for brightness. public static object RenderMain(string tag) { var cam = Camera.main; if (cam == null) return "no Camera.main"; System.IO.Directory.CreateDirectory(OutDir); int w = 1280, h = 720; var rt = new RenderTexture(w, h, 24, RenderTextureFormat.ARGB32); var prevTarget = cam.targetTexture; cam.targetTexture = rt; cam.Render(); cam.targetTexture = prevTarget; var prev = RenderTexture.active; RenderTexture.active = rt; var tex = new Texture2D(w, h, TextureFormat.RGB24, false); tex.ReadPixels(new Rect(0, 0, w, h), 0, 0); tex.Apply(); RenderTexture.active = prev; var px = tex.GetPixels32(); double sum = 0; int dark = 0, clip = 0; foreach (var p in px) { double l = 0.2126 * p.r + 0.7152 * p.g + 0.0722 * p.b; sum += l; if (l < 40) dark++; if (l > 245) clip++; } var path = System.IO.Path.Combine(OutDir, tag + ".png"); System.IO.File.WriteAllBytes(path, tex.EncodeToPNG()); UnityEngine.Object.DestroyImmediate(tex); rt.Release(); UnityEngine.Object.DestroyImmediate(rt); return path + " meanLum=" + ((float)(sum / px.Length / 255.0)).ToString("F3") + " darkRatio(<40/255)=" + ((float)dark / px.Length).ToString("F3") + " clipRatio(>245/255)=" + ((float)clip / px.Length).ToString("F3"); } // ---------- runtime experiments (revert when Play stops) ---------- public static object SetAmbient(string mode, float intensity) { switch (mode) { case "skybox": RenderSettings.ambientMode = AmbientMode.Skybox; RenderSettings.ambientIntensity = intensity; DynamicGI.UpdateEnvironment(); break; case "update": DynamicGI.UpdateEnvironment(); break; case "flat": RenderSettings.ambientMode = AmbientMode.Flat; RenderSettings.ambientLight = Color.white * intensity; break; case "trilight": RenderSettings.ambientMode = AmbientMode.Trilight; RenderSettings.ambientSkyColor = Color.white * intensity; RenderSettings.ambientEquatorColor = (Color)new Color32(180, 148, 123, 255) * intensity; RenderSettings.ambientGroundColor = (Color)new Color32(79, 79, 115, 255) * intensity; break; default: return "unknown mode " + mode; } var dirs = new[] { Vector3.up, Vector3.down }; var res = new Color[2]; RenderSettings.ambientProbe.Evaluate(dirs, res); return "mode=" + RenderSettings.ambientMode + " intensity=" + RenderSettings.ambientIntensity + " probe up=" + C(res[0]) + " down=" + C(res[1]); } public static object SetTint(float shadow, float middle, float light) { int n = 0; var mpb = new MaterialPropertyBlock(); foreach (var r in UnityEngine.Object.FindObjectsByType(FindObjectsSortMode.None)) { bool toon = false; foreach (var m in r.sharedMaterials) if (m != null && m.shader != null && m.shader.name == "Toon Shaders Pro/URP/Toon") { toon = true; break; } if (!toon) continue; r.GetPropertyBlock(mpb); mpb.SetColor("_ShadowTint", new Color(shadow, shadow, shadow, 1)); mpb.SetColor("_MiddleTint", new Color(middle, middle, middle, 1)); mpb.SetColor("_LightTint", new Color(light, light, light, 1)); r.SetPropertyBlock(mpb); n++; } return "propertyBlock applied to " + n + " Toon Pro renderers (shadow=" + shadow + " middle=" + middle + " light=" + light + ")"; } public static object ClearTint() { int n = 0; foreach (var r in UnityEngine.Object.FindObjectsByType(FindObjectsSortMode.None)) { r.SetPropertyBlock(null); n++; } return "cleared property blocks on " + n + " renderers"; } public static object SetLight(float intensity) { int n = 0; foreach (var l in UnityEngine.Object.FindObjectsByType(FindObjectsSortMode.None)) if (l.type == LightType.Directional) { l.intensity = intensity; n++; } return "directional lights set to " + intensity + ": " + n; } // ---------- reflection helper to drive game UI without compile-time dependency ---------- public static object Invoke(string typeName, string method) { var t = Type.GetType(typeName + ", Assembly-CSharp"); if (t == null) return "type not found: " + typeName; var obj = UnityEngine.Object.FindFirstObjectByType(t, FindObjectsInactive.Include); if (obj == null) return "instance not found: " + typeName; var mi = t.GetMethod(method, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (mi == null) return "method not found: " + method; var r = mi.Invoke(obj, null); return "invoked " + typeName + "." + method + " -> " + (r ?? "void"); } public static object FindUI(string nameContains) { var sb = new StringBuilder(); foreach (var go in UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None)) if (go.name.IndexOf(nameContains, StringComparison.OrdinalIgnoreCase) >= 0) sb.Append(Path(go.transform)).Append(" active=").Append(go.activeInHierarchy).Append('\n'); return sb.Length == 0 ? "none" : sb.ToString(); } public static object ClickButton(string goName) { foreach (var b in UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None)) if (b.gameObject.name == goName) { b.onClick.Invoke(); return "clicked " + Path(b.transform) + " active=" + b.gameObject.activeInHierarchy; } return "button not found: " + goName; } // ---------- utils ---------- static List ListProps(SerializedObject so) { var names = new List(); var it = so.GetIterator(); if (it.NextVisible(true)) do { names.Add(it.propertyPath); } while (it.NextVisible(false)); return names; } static string C(Color c) => "(" + c.r.ToString("F2") + "," + c.g.ToString("F2") + "," + c.b.ToString("F2") + ")"; static string Path(Transform t) { var s = t.name; while (t.parent != null) { t = t.parent; s = t.name + "/" + s; } return s; } // ───────────── #760 dev-login (경합 방지) ───────────── // 배경: OnClick_DevLogin 을 테이블 로딩 전(Input_ID 가 비어 있을 때) 호출하면 PlayerPrefs(Option_UserID) 에 "" 가 // 저장되고, 이후 LoginWithCustomID 가 InvalidParams 로 거부되어 "받아오는 중" 에 멈춘다(2026-09-06 실측). static Type WlTypeByName(string name) { foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) { Type t = null; try { t = asm.GetType(name); } catch { } if (t != null) return t; } return null; } public static object GetPref(string key) { return key + " = '" + PlayerPrefs.GetString(key, "") + "' has=" + PlayerPrefs.HasKey(key); } public static object SetPref(string key, string val) { PlayerPrefs.SetString(key, val); PlayerPrefs.Save(); return key + " := '" + PlayerPrefs.GetString(key) + "'"; } /// 테이블 로딩이 끝난 뒤에만 OnClick_DevLogin 호출. Input_ID 가 비어 있으면 id(기본 TestID) 로 채운다. public static object DevLogin(string id) { if (!Application.isPlaying) return "not playing"; var tcType = WlTypeByName("TableChecker"); if (tcType != null) { object ins = null; var pi = tcType.GetProperty("Ins", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); if (pi != null) ins = pi.GetValue(null); if (ins == null) { var fi = tcType.GetField("Ins", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); if (fi != null) ins = fi.GetValue(null); } var m = tcType.GetMethod("CheckAllLoad"); if (ins != null && m != null && !(bool)m.Invoke(ins, null)) return "tables not loaded yet"; } var tType = WlTypeByName("TitleInfo"); if (tType == null) return "TitleInfo type not found"; var objs = UnityEngine.Object.FindObjectsByType(tType, FindObjectsInactive.Include, FindObjectsSortMode.None); if (objs.Length == 0) return "TitleInfo instance not found"; var ti = objs[0]; var fInput = tType.GetField("Input_ID", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); var input = fInput != null ? fInput.GetValue(ti) as TMPro.TMP_InputField : null; if (input == null) return "Input_ID null"; if (string.IsNullOrEmpty(input.text)) input.text = string.IsNullOrEmpty(id) ? "TestID" : id; var call = tType.GetMethod("OnClick_DevLogin"); if (call == null) return "OnClick_DevLogin not found"; call.Invoke(ti, null); return "dev login invoked id='" + input.text + "'"; } }