champions-of-luneraria/Assets/Luneraria/Tests/UI/PlayMode/UiToolkitProbeTests.cs
Martin Stickelbroeck 4dee9b18d2 U3A HUD-Grundlagen: UI Toolkit, Sichtmodell, menschlicher Sitz
Beginn des Ports des G3-HUD (Referenz luneraria/g3, 1b540f2).

- Entscheidung UI Toolkit statt uGUI nach kurzer Bewertung und einer Probe:
  Panel rendert im Batchmodus in eine 1920x1080-Textur (Skalierung wie Godot
  canvas_items/expand, 1600x960), gefaste Verlaufsplatten, alle 50 Symbole,
  Schriften aus den variablen Fonts der Referenz.
- Sichtmodell portiert (LuneHudModel, LuneLaneThreat, LuneStats) mit den
  Privatsphäre-Regeln der Referenz: Gold, Entwurf, Rückgängig und neue
  Buchungen nur für den eigenen Sitz; Lanes, Gebäude, Boss und Wellen aus
  öffentlichem Zustand.
- Menschlicher Sitz in LuneMatchRunner/LuneMatchDriver: Bots planen die
  übrigen fünf Sitze, die Planung wartet auf Bereit; Tempo 1x/2x/4x und Pause
  nur im Kampf. Bot-Partien unverändert.
- Theme-Grundlagen: Farben und Zahlenformate, Platten (forged_style.gd),
  Vektorsymbole (lune_glyphs.gd) mit Painter2D, statische SDF-Atlanten aus
  den benannten Instanzen von Cantarell und Cinzel (OFL).
- Tests: EditMode 23/23 (12 neue: gleiches öffentliches Bild für alle Sitze,
  fremde Entwürfe privat, private Boss-Lane-Wahl, Lane-Bedrohung, Bosszustände,
  nächste Welle, Bereit-Sperre, Formate, menschlicher Sitz); PlayMode 3/3,
  Fingerabdrücke der Bot-Partien unverändert (DCE95C96556F3A1A, 7B8E43AEFC0D60C5).
- Dokumentation: U3-UI (Bewertung, U3A), U5-VISUAL (Vorgaben des
  Projektinhabers: stilisierter Realismus für die ganze Welt, erst in U5).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 23:57:45 +02:00

133 lines
6.6 KiB
C#

using System.Collections;
using System.IO;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.TextCore.Text;
using UnityEngine.UIElements;
namespace Luneraria.UI.Tests
{
/// <summary>
/// U3A decision probe: a UI Toolkit panel renders into a texture in batch mode, with the plate
/// drawing, all glyphs and the three font weights (Regular, Regular with a 0.25 px outline for
/// the G3 weight 560, Bold). Writes tools/out/ui-spike.png (docs/port/screenshots/u3/00).
/// </summary>
public sealed class UiToolkitProbeTests
{
sealed class Plate : VisualElement
{
readonly LunePlateStyle _style;
public Plate(LunePlateStyle style)
{
_style = style;
style.ApplyPadding(this);
generateVisualContent += context => LunePlates.Draw(context, new Rect(0, 0, layout.width, layout.height), _style);
}
}
sealed class Glyph : VisualElement
{
public Glyph(string name, Color color, float edge)
{
this.style.width = edge;
this.style.height = edge;
generateVisualContent += context => LuneGlyphs.Draw(context.painter2D, name, new Rect(0, 0, layout.width, layout.height), color);
}
}
static Label Text(string value, FontAsset font, float size, Color color, float outline = 0f, float spacing = 0f)
{
var label = new Label(value);
label.style.unityFontDefinition = FontDefinition.FromSDFFont(font);
label.style.fontSize = size;
label.style.color = color;
label.style.letterSpacing = spacing;
label.style.marginLeft = 0;
label.style.paddingLeft = 0;
if (outline > 0f)
{
label.style.unityTextOutlineWidth = outline;
label.style.unityTextOutlineColor = color;
}
label.style.textShadow = new TextShadow { offset = new Vector2(0f, 1f), blurRadius = 1f, color = new Color(0f, 0f, 0f, .55f) };
return label;
}
[UnityTest]
public IEnumerator PanelRendersIntoTexture()
{
#if UNITY_EDITOR
var assets = UnityEditor.AssetDatabase.LoadAssetAtPath<LuneUiAssets>("Assets/Luneraria/Settings/LuneUi.asset");
#else
LuneUiAssets assets = null;
#endif
Assert.IsNotNull(assets);
var target = new RenderTexture(1920, 1080, 0, RenderTextureFormat.ARGB32);
var settings = ScriptableObject.CreateInstance<PanelSettings>();
settings.themeStyleSheet = assets.Theme;
settings.targetTexture = target;
settings.scaleMode = PanelScaleMode.ScaleWithScreenSize;
settings.referenceResolution = new Vector2Int(1600, 960);
settings.screenMatchMode = PanelScreenMatchMode.Expand;
settings.clearColor = true;
settings.colorClearValue = new Color(.28f, .36f, .22f, 1f);
var host = new GameObject("UiSpike");
var document = host.AddComponent<UIDocument>();
document.panelSettings = settings;
yield return null;
VisualElement root = document.rootVisualElement;
var panel = new Plate(LunePlateStyle.Hud(LuneUiTheme.SlateTop, LuneUiTheme.SlateBottom, LuneUiTheme.Edge, 1f, 6f, 12f, 10f));
panel.style.position = Position.Absolute;
panel.style.left = 12;
panel.style.top = 12;
panel.style.width = 760;
root.Add(panel);
panel.Add(Text("WELLE 21", assets.Display, 30, LuneUiTheme.Hex("f4e6c4"), 0f, 3f));
panel.Add(Text("DEIN ANCIENT · BLAU", assets.TextBold, 14, LuneUiTheme.BronzeLight, 0f, 2f));
panel.Add(Text("Regular 400: Einheiten mit „Grotte“ entsenden. 12.000 / 12.000", assets.Text, 15, LuneUiTheme.Text));
panel.Add(Text("Regular + 0,25 Kontur: Einheiten mit „Grotte“ entsenden. 12.000 / 12.000", assets.Text, 15, LuneUiTheme.Text, .25f));
panel.Add(Text("Regular + 0,45 Kontur: Einheiten mit „Grotte“ entsenden. 12.000 / 12.000", assets.Text, 15, LuneUiTheme.Text, .45f));
panel.Add(Text("Bold 700: Einheiten mit „Grotte“ entsenden. 12.000 / 12.000", assets.TextBold, 15, LuneUiTheme.Text));
panel.Add(Text("235", assets.TextBold, 24, LuneUiTheme.Gold));
var glyphs = new VisualElement();
glyphs.style.flexDirection = FlexDirection.Row;
glyphs.style.flexWrap = Wrap.Wrap;
glyphs.style.marginTop = 8;
panel.Add(glyphs);
foreach (string name in LuneGlyphs.Names)
{
var glyph = new Glyph(name, name.StartsWith("emblem_1") || name == "shape_1" ? LuneUiTheme.Team[1] : name.StartsWith("emblem_0") || name == "shape_0" ? LuneUiTheme.Team[0] : LuneUiTheme.Text, 28f);
glyph.style.marginRight = 6;
glyph.style.marginBottom = 6;
glyphs.Add(glyph);
}
var buttons = new VisualElement();
buttons.style.flexDirection = FlexDirection.Row;
panel.Add(buttons);
var primary = new Plate(LunePlateStyle.Menu(LuneUiTheme.Hex("e8c47e"), LuneUiTheme.Hex("b98a4a"), LuneUiTheme.Hex("fff0cc"), 1.5f, 9f, 30f, 15f));
primary.Add(Text("BEREIT", assets.Display, 22, LuneUiTheme.Ink, 0f, 2f));
buttons.Add(primary);
var card = new Plate(LunePlateStyle.Hud(LuneUiTheme.Hex("19222c"), LuneUiTheme.Hex("10161d"), LuneUiTheme.Hex("34424f"), 1f, 7f, 6f, 6f));
card.style.width = 116;
card.style.height = 132;
card.style.marginLeft = 10;
buttons.Add(card);
for (int frame = 0; frame < 6; frame++)
yield return null;
RenderTexture.active = target;
var image = new Texture2D(1920, 1080, TextureFormat.RGBA32, false);
image.ReadPixels(new Rect(0, 0, 1920, 1080), 0, 0);
image.Apply();
RenderTexture.active = null;
string file = Path.Combine(Path.GetDirectoryName(Application.dataPath), "tools/out/ui-spike.png");
Directory.CreateDirectory(Path.GetDirectoryName(file));
File.WriteAllBytes(file, image.EncodeToPNG());
Color corner = image.GetPixel(1900, 20);
Color panelPixel = image.GetPixel(100, 1080 - 100);
Debug.Log("LUNE_SPIKE corner " + corner + " panel " + panelPixel + " layout " + panel.layout);
Object.Destroy(host);
Assert.Greater(panel.layout.height, 50f);
}
}
}