// 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()) { var cv = cs.GetComponent(); 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());