champions-of-luneraria/Assets/Luneraria/Tests/UI/PlayMode/HudPlaytest.cs
Martin Stickelbroeck 66867f3332 U3D Abnahme: Prüfung, Feld um das HUD, Bilder, Leistung
Das G3-HUD ist abgenommen; der Meilenstein U3 ist damit fertig.

- Prüfung (Port von verify_g3, Teile A–H plus I): 718 Prüfungen ohne
  Fehler. Teil I prüft die Feld-Darstellung um das HUD (U3-Zugabe aus
  dem Motivvergleich): Aufstellungsflächen, Grotteneingang, ruhender
  Boss, Namensschilder, Eigentümer-Ring, Ancient-Balken.
- Feld um das HUD: Namensschilder für Helden (mit Stufe), Angriffs-
  champions und den eingesetzten Boss („für Blau“), Eigentümer-Ring,
  Ancient-Balken bei Schaden, ruhende Schlundkrone auf dem Podest,
  eigener Grotteneingang, Aufstellungsflächen als eigene Objekte.
- Schriften korrigiert: Die Referenz deklariert Cantarell 560/700–760
  und Cinzel 700, wendet die Gewichte aber nie an (variation_opentype
  mit dem String-Schlüssel „wght“ wird von Godot ignoriert). G3 zeigt
  Regular 400, der Port ebenso — Cinzel-Regular statt Bold, keine
  Kontur zur Aufdickung. Laufweite aus spacing_glyph umgerechnet;
  dunkler Saum als Schatten statt TextCore-Kontur.
- Werkzeuge: HudViewsTests (14 G3-Motive), HudPlaytest (Spieltest über
  die echte Oberfläche per Befehlsdateien), ui_ms_per_frame im
  LuneBenchmark. Layout folgt den echten Panelhöhen, Kamera startet wie
  Godot _ready auf der eigenen Lane.
- Tests: EditMode 23/23, PlayMode 13/13, Kerntests 402/402, Verify
  718/0. Fingerabdrücke der Bot-Partien unverändert (DCE95C96556F3A1A,
  7B8E43AEFC0D60C5).
- Bilder: 14 Motive bei 1920×1080, Auflösungen 1152×720 bis 2560×1440,
  Prüf-Schlüsselbilder, Spieltest-Screenshots.
- Leistung (macOS-Player mit HUD, 116 Einheiten): 169 fps Übersicht,
  218 fps Mid nah; HUD-Skripte 0,090 bzw. 0,078 ms je Bild.
- Dokumentation: U3-UI (U3D, Schriftbefund), PORT-QA (U3), PORT-STATUS
  (U3 fertig, U4 nächster Schritt), KNOWN-BEHAVIOR-DIFFERENCES.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 12:55:06 +02:00

170 lines
7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using Luneraria.Core;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
namespace Luneraria.UI.Tests
{
/// <summary>
/// Interactive playtest of the real game (port of tools/luneraria/g3_playtest.gd). It never
/// touches the match state: every action is an input a player gives, at pixel coordinates of
/// the 1920×1080 picture (origin top left). A click reaches the HUD panel as pointer events
/// and the field as LuneGame.ClickField, which ignores points on the HUD, because in the
/// player both see the same mouse; game keys go through LuneGame.Key, the camera keys R, Q,
/// E and O through the rig as in its own key handling. Pointer moves give hover (tooltips).
/// A tester writes command batches and gets a screenshot back after each batch:
/// /tmp/lune_play/cmd-N.txt lines: click X Y · move X Y · key NAME · wait SECONDS · shot NAME · quit
/// /tmp/lune_play/out-N.png the screen after the batch; /tmp/lune_play/ok-N holds round and phase
/// The log of all commands with round and phase goes to docs/port/logs/u3/playtest-log.txt.
/// There is no main menu in the port yet, so the playtest starts in the match of the G1 scene
/// (seed of the scene, 2; LUNE_PLAYTEST_SEED overrides). It ends with quit or after 30 idle minutes.
/// Run: -testFilter "Luneraria.UI.Tests.HudPlaytest".
/// </summary>
[Explicit("Spieltest: wartet auf Befehlsdateien")]
public sealed class HudPlaytest
{
const string Dir = "/tmp/lune_play/";
const string LogFile = "docs/port/logs/u3/playtest-log.txt";
HudSession _s;
readonly List<string> _log = new List<string>();
float _started;
bool _quit;
[UnityTest, Timeout(int.MaxValue)]
public IEnumerator Play()
{
Directory.CreateDirectory(Dir);
foreach (string file in Directory.GetFiles(Dir))
File.Delete(file);
int seed = int.TryParse(Environment.GetEnvironmentVariable("LUNE_PLAYTEST_SEED"), out int value) ? value : 2;
_s = new HudSession();
yield return _s.Open(1920, 1080, seed: seed);
_started = Time.realtimeSinceStartup;
yield return new WaitForSecondsRealtime(.5f);
_s.Capture(Dir + "out-0.png");
File.WriteAllText(Dir + "ok-0", Status());
int next = 1;
float idle = 0f;
while (!_quit && idle < 1800f)
{
string path = Dir + "cmd-" + next + ".txt";
if (!File.Exists(path))
{
idle += Time.unscaledDeltaTime;
yield return null;
continue;
}
idle = 0f;
foreach (string raw in File.ReadAllLines(path))
{
string line = raw.Trim();
if (line.Length == 0 || line.StartsWith("#", StringComparison.Ordinal))
continue;
Write(next, line);
yield return Command(line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries));
if (_quit)
break;
}
yield return new WaitForSecondsRealtime(.35f);
_s.Capture(Dir + "out-" + next + ".png");
File.WriteAllText(Dir + "ok-" + next, Status());
next++;
}
_s.Close();
Assert.IsTrue(_quit, "the playtest ended with quit");
}
string Status()
{
LuneMatch match = _s.Match;
string phase = match.Phase == Phase.Planning ? "PLANUNG" : match.Phase == Phase.Combat ? "KAMPF" : "ENDE";
return "round=" + match.RoundNumber + " phase=" + phase;
}
void Write(int index, string line)
{
_log.Add(string.Format(CultureInfo.InvariantCulture, "{0,7:0.0} s #{1} [{2}] {3}", Time.realtimeSinceStartup - _started, index, Status(), line));
string file = Path.Combine(HudSession.Root, LogFile);
Directory.CreateDirectory(Path.GetDirectoryName(file));
File.WriteAllText(file, string.Join("\n", _log) + "\n");
}
IEnumerator Command(string[] parts)
{
switch (parts[0])
{
case "click":
{
Vector2 pixel = Pixel(parts);
Vector2 at = _s.Hud.PixelToPanel(pixel);
_s.MoveTo(at);
yield return null;
_s.Pointer(at, EventType.MouseDown);
// The field sees the same press (LuneGame.HandleClicks); it ignores points on the HUD.
_s.Game.ClickField(new Vector2(pixel.x, _s.Height - pixel.y));
yield return null;
_s.Pointer(at, EventType.MouseUp);
yield return null;
break;
}
case "move":
_s.MoveTo(_s.Hud.PixelToPanel(Pixel(parts)));
yield return null;
break;
case "key":
Key(parts[1].ToUpperInvariant());
yield return null;
break;
case "wait":
yield return new WaitForSecondsRealtime(float.Parse(parts[1], CultureInfo.InvariantCulture));
break;
case "shot":
yield return null;
_s.Capture(Dir + parts[1] + ".png");
break;
case "quit":
_quit = true;
break;
default:
Debug.LogWarning("LUNE_PLAYTEST unknown command: " + string.Join(" ", parts));
break;
}
}
static Vector2 Pixel(string[] parts)
{
return new Vector2(float.Parse(parts[1], CultureInfo.InvariantCulture), float.Parse(parts[2], CultureInfo.InvariantCulture));
}
void Key(string name)
{
// Camera keys of the rig (LuneCameraRig.HandleInput), blocked while the options are open.
if (name == "R" || name == "Q" || name == "E" || name == "O")
{
if (_s.Hud.Settings.Open)
return;
if (name == "R")
_s.Rig.ResetRequested?.Invoke();
else if (name == "O")
_s.Rig.Overview();
else
_s.Rig.RotateStep(name == "Q" ? -1 : 1);
return;
}
KeyCode code;
if (name.Length == 1 && char.IsDigit(name[0]))
code = KeyCode.Alpha0 + (name[0] - '0');
else if (name == "ESC")
code = KeyCode.Escape;
else
code = (KeyCode)Enum.Parse(typeof(KeyCode), name, true);
_s.Game.Key(code);
}
}
}