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>
This commit is contained in:
+56
-37
@@ -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<UnityEngine.UI.CanvasScaler>())
|
||||
{
|
||||
var cv = cs.GetComponent<UnityEngine.Canvas>();
|
||||
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());
|
||||
|
||||
Reference in New Issue
Block a user