Files
ckochandClaude Opus 4.8 b9e68fd284 UI tuner: fix stale readback and World-Space label lever
Two defects in the tuner, both visible in the in-game logs:

1. AFTER lines read Canvas.scaleFactor immediately after writing, but that is
   recomputed in CanvasScaler.Update(), so it reported last frame's value
   (showed 1.3333 after ref was set to 1350). Now prints the mathematically
   expected scale instead.

2. WORLD_SCALE set CanvasScaler.scaleFactor on WorldSpace canvases, which
   HandleWorldCanvas() overwrites with dynamicPixelsPerUnit every frame - the
   log showed 'APPLIED scaleFactor 1 -> 0.8' followed by effectiveScale=1.0.
   World-space label size is driven by the canvas transform localScale, so it
   now scales that, stashing a baseline child so repeated runs don't compound.

Also documents that MATCH must stay 1.0: match .8 + ref 1350 yields 1.281 vs
1.067 at match 1, largely cancelling the shrink.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 16:04:56 -04:00

83 lines
4.1 KiB
C#

// LIVE UI TUNER — paste into UnityExplorer's C# Console. Edit knobs, Run, look, repeat.
// Nothing persists across a relaunch.
//
// TWO canvas families, each needing a DIFFERENT lever:
//
// 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; // 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();
log.AppendLine($"KNOBS match={MATCH} hudScale={HUD_SCALE} worldScale={WORLD_SCALE} listOnly={LIST_ONLY}");
log.AppendLine($"SCREEN {UnityEngine.Screen.width}x{UnityEngine.Screen.height} " +
$"aspect={(float)UnityEngine.Screen.width / UnityEngine.Screen.height:F4}");
foreach (var cs in UnityEngine.Object.FindObjectsOfType<UnityEngine.UI.CanvasScaler>())
{
var cv = cs.GetComponent<UnityEngine.Canvas>();
if (cv == null) continue;
if (cv.renderMode == UnityEngine.RenderMode.WorldSpace)
{
var ls = cv.transform.localScale;
log.AppendLine($"WORLD {cs.name}: localScale={ls} (dynPPU={cs.dynamicPixelsPerUnit})");
if (!LIST_ONLY && WORLD_SCALE > 0f)
{
// 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 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;
}
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());