diff --git a/tools/unity_uitune.cs b/tools/unity_uitune.cs index 6a46972..cbf77c8 100644 --- a/tools/unity_uitune.cs +++ b/tools/unity_uitune.cs @@ -1,20 +1,30 @@ // LIVE UI TUNER — paste into UnityExplorer's C# Console. Edit knobs, Run, look, repeat. -// Nothing persists; a scene change or relaunch reverts everything. +// Nothing persists across a relaunch. // -// This game has TWO canvas families, tuned by DIFFERENT properties: -// 1. ScreenSpaceOverlay + ScaleWithScreenSize ("Canvas", "Canvas in front") -// -> the HUD. Controlled by matchWidthOrHeight + referenceResolution. -// 2. WorldSpace + ConstantPixelSize ("NamePlate", etc.) -// -> in-world labels. Unity IGNORES referenceResolution here; -// ONLY scaleFactor does anything. +// TWO canvas families, each needing a DIFFERENT lever: // -// The tuner prints the knob values it actually used, so you can confirm your edit took. +// 1. ScreenSpaceOverlay + ScaleWithScreenSize ("Canvas", "Canvas in front") +// The HUD. Lever = matchWidthOrHeight + referenceResolution. +// KEEP MATCH AT 1.0 — lowering it re-introduces width-based scaling and +// undoes the shrink (match .8 + ref 1350 => 1.281, vs match 1 => 1.067). +// +// 2. WorldSpace canvases ("NamePlate", etc.) +// In-world labels. CanvasScaler.scaleFactor DOES NOT WORK here: for a +// WorldSpace canvas, CanvasScaler.HandleWorldCanvas() overwrites +// scaleFactor with dynamicPixelsPerUnit every frame. The real lever is +// the canvas transform's localScale. +// +// NOTE: Canvas.scaleFactor is recomputed in CanvasScaler.Update(), so reading it +// right after writing returns LAST frame's value. We print the mathematically +// expected scale instead, and echo BEFORE state (which is accurate) each run. // ---- KNOBS ----------------------------------------------------------------- -float MATCH = 1.0f; // ScaleWithScreenSize only. 0=scale by width, 1=by height -float HUD_SCALE = 0.85f; // ScaleWithScreenSize only. <1 = smaller HUD + more room -float WORLD_SCALE = 0.8f; // ConstantPixelSize only. <1 = smaller in-world labels -bool LIST_ONLY = false; // true = report only, change nothing +float MATCH = 1.0f; // HUD. Leave at 1.0. +float HUD_SCALE = 0.85f; // HUD. <1 = smaller HUD + more layout room. +float WORLD_SCALE = 0f; // In-world labels. 0 = don't touch. + // Absolute multiplier vs the BASELINE printed below. + // Run once with 0 to read baseline, then e.g. 0.8f. +bool LIST_ONLY = false; // true = report only, change nothing // ---------------------------------------------------------------------------- var log = new System.Text.StringBuilder(); @@ -25,39 +35,48 @@ log.AppendLine($"SCREEN {UnityEngine.Screen.width}x{UnityEngine.Screen.height} " foreach (var cs in UnityEngine.Object.FindObjectsOfType()) { var cv = cs.GetComponent(); - string render = cv ? cv.renderMode.ToString() : "?"; - log.AppendLine($"BEFORE {cs.name} [{cs.uiScaleMode}/{render}] ref={cs.referenceResolution} " + - $"match={cs.matchWidthOrHeight} scaleFactor={cs.scaleFactor}"); + if (cv == null) continue; - if (!LIST_ONLY) + if (cv.renderMode == UnityEngine.RenderMode.WorldSpace) { - if (cs.uiScaleMode == UnityEngine.UI.CanvasScaler.ScaleMode.ScaleWithScreenSize) + var ls = cv.transform.localScale; + log.AppendLine($"WORLD {cs.name}: localScale={ls} (dynPPU={cs.dynamicPixelsPerUnit})"); + if (!LIST_ONLY && WORLD_SCALE > 0f) { - cs.matchWidthOrHeight = MATCH; - // effective scale = screenH / refH => refH = 1080 / HUD_SCALE - if (HUD_SCALE > 0f && System.Math.Abs(HUD_SCALE - 1f) > 0.0001f) + // Store the pristine scale on first touch so repeated runs don't compound. + var marker = cv.transform.Find("__uwp_baseline"); + float bx; + if (marker == null) { - var r = cs.referenceResolution; - cs.referenceResolution = new UnityEngine.Vector2(r.x, 1080f / HUD_SCALE); - log.AppendLine($" APPLIED ref {r} -> {cs.referenceResolution}"); - } - } - else if (cs.uiScaleMode == UnityEngine.UI.CanvasScaler.ScaleMode.ConstantPixelSize) - { - // referenceResolution is ignored in this mode; scaleFactor is the only lever - if (WORLD_SCALE > 0f && System.Math.Abs(WORLD_SCALE - 1f) > 0.0001f) - { - float old = cs.scaleFactor; - cs.scaleFactor = WORLD_SCALE; - log.AppendLine($" APPLIED scaleFactor {old} -> {cs.scaleFactor}"); + var go = new UnityEngine.GameObject("__uwp_baseline"); + go.transform.SetParent(cv.transform, false); + go.transform.localScale = ls; // stash baseline here + bx = ls.x; } + else bx = marker.localScale.x; + + cv.transform.localScale = new UnityEngine.Vector3(bx, bx, bx) * WORLD_SCALE; + log.AppendLine($" APPLIED localScale baseline={bx:F5} -> {cv.transform.localScale}"); } + continue; } - float sf = cv ? cv.scaleFactor : 0f; - if (sf > 0) - log.AppendLine($"AFTER {cs.name}: effectiveScale={sf:F4} " + - $"logicalCanvas={UnityEngine.Screen.width / sf:F0}x{UnityEngine.Screen.height / sf:F0}"); + log.AppendLine($"BEFORE {cs.name} [{cs.uiScaleMode}/{cv.renderMode}] ref={cs.referenceResolution} " + + $"match={cs.matchWidthOrHeight}"); + + if (!LIST_ONLY && cs.uiScaleMode == UnityEngine.UI.CanvasScaler.ScaleMode.ScaleWithScreenSize) + { + cs.matchWidthOrHeight = MATCH; + if (HUD_SCALE > 0f && System.Math.Abs(HUD_SCALE - 1f) > 0.0001f) + cs.referenceResolution = new UnityEngine.Vector2(cs.referenceResolution.x, 1080f / HUD_SCALE); + + // expected scale (accurate NOW; Canvas.scaleFactor lags by a frame) + var r = cs.referenceResolution; + double exp = System.Math.Pow(UnityEngine.Screen.width / r.x, 1.0 - MATCH) * + System.Math.Pow(UnityEngine.Screen.height / r.y, MATCH); + log.AppendLine($" APPLIED ref={r} match={MATCH} => expectedScale={exp:F4} " + + $"logicalCanvas={UnityEngine.Screen.width / exp:F0}x{UnityEngine.Screen.height / exp:F0}"); + } } UnityExplorer.ExplorerCore.Log(log.ToString());