Fester Simulationstakt und Ansichten, die nur lesen; Nachweis, dass die Partie bei jeder Bildrate dieselbe ist. - LuneClock: Akkumulator, ganze Schritte zu 0,05 s, Alpha zum Interpolieren. - LuneMatchRunner: Host-Ablauf einer Bot-Partie (Planung über die Befehlsschnittstelle, Kampf im festen Takt, Abrechnung, nächste Runde); LuneMatchDriver reicht in Update nur Bildzeit weiter. - LuneTrace: Fingerabdruck der Partie (jeder Schritt, jede Abrechnung) und der reine Kernlauf zum Vergleich. - Ansichten: LuneMatchView, LuneUnitView (interpolierte Position, Blickrichtung, Clips aus Kampfereignissen, Teamring, Lebensbalken), LuneBuildingView mit Einsturz und Ruine aus den autoritativen HP, Turmblitze, LuneCameraRig wie lune_camera.gd, vorläufige Debug-Anzeige (kein HUD; U3 portiert das G3-HUD). - Einheitenbibliothek: Kohlenbeißer aus 131 Teilen zu einem Mesh, Animator- Controller mit den Blender-Clips, Ring- und Balkenmeshes, Markermaterialien. - Tests: EditMode (30/60/120 fps, unbegrenzt, Ruckler, 8x Tempo; Seed 2 und Seed 9 ab Runde 21) und PlayMode in der Szene bei echten Bildraten 30, 60, 120 und unbegrenzt: überall derselbe Fingerabdruck wie der Kern. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
75 lines
3.2 KiB
C#
75 lines
3.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Luneraria.Core;
|
|
using NUnit.Framework;
|
|
using UnityEngine;
|
|
|
|
namespace Luneraria.View.Tests
|
|
{
|
|
/// <summary>
|
|
/// U2C: the host loop gives the same match at every frame rate. The runner is fed frame
|
|
/// times of 30, 60 and 120 FPS, of an unlimited frame rate (tiny, jittering frames), and of a
|
|
/// stuttering machine (long stalls between short frames); every run must end with the state
|
|
/// fingerprint of the plain core run (LuneTrace.RunCore), step for step and round for round.
|
|
/// </summary>
|
|
public sealed class FrameRateIndependenceTests
|
|
{
|
|
static readonly Dictionary<string, Func<int, double>> Rates = new Dictionary<string, Func<int, double>>
|
|
{
|
|
{ "30 fps", frame => 1.0 / 30.0 },
|
|
{ "60 fps", frame => 1.0 / 60.0 },
|
|
{ "120 fps", frame => 1.0 / 120.0 },
|
|
{ "unlimited (0.4-3.9 ms jitter)", frame => 0.0004 + ((frame * 7919) % 97) / 97.0 * 0.0035 },
|
|
{ "stutter (stalls up to 0.4 s)", frame => frame % 23 == 0 ? 0.4 : 1.0 / 144.0 },
|
|
};
|
|
|
|
static void Compare(int seed, int startRound, int extraGold, double speed)
|
|
{
|
|
var options = new MatchOptions { Seed = seed, HumanSlot = -1 };
|
|
LuneTrace core = LuneTrace.RunCore(options, startRound, extraGold, "");
|
|
foreach (KeyValuePair<string, Func<int, double>> rate in Rates)
|
|
{
|
|
var runner = new LuneMatchRunner(options, startRound, extraGold) { Trace = new LuneTrace(), PlanningSeconds = .5, SettledSeconds = .5 };
|
|
int frames = 0;
|
|
while (runner.Current != LuneMatchRunner.Stage.Over && frames < 20000000)
|
|
runner.Advance(rate.Value(frames++) * speed);
|
|
Debug.Log(string.Format("LUNE_FPS seed {0} {1}: {2} frames, {3} steps, hash {4:X16} (core {5:X16})", seed, rate.Key, frames, runner.Trace.Steps, runner.Trace.Hash, core.Hash));
|
|
Assert.AreEqual(core.Steps, runner.Trace.Steps, rate.Key + ": steps");
|
|
CollectionAssert.AreEqual(core.RoundHashes, runner.Trace.RoundHashes, rate.Key + ": round fingerprints");
|
|
Assert.AreEqual(core.Hash, runner.Trace.Hash, rate.Key + ": match fingerprint");
|
|
Assert.AreEqual(core.Rounds, runner.Trace.Rounds, rate.Key + ": rounds");
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void FullBotMatchSeed2IsTheSameAtEveryFrameRate()
|
|
{
|
|
Compare(2, 1, 0, 1.0);
|
|
}
|
|
|
|
[Test]
|
|
public void HighWaveMatchFromRound21IsTheSameAtEveryFrameRate()
|
|
{
|
|
Compare(9, 21, 4000, 1.0);
|
|
}
|
|
|
|
[Test]
|
|
public void MatchSpeedDoesNotChangeTheMatch()
|
|
{
|
|
Compare(2, 1, 0, 8.0);
|
|
}
|
|
|
|
[Test]
|
|
public void ClockRunsWholeStepsOnly()
|
|
{
|
|
var clock = new LuneClock(0.05);
|
|
int steps = 0;
|
|
steps += clock.Advance(0.049, () => true);
|
|
Assert.AreEqual(0, steps);
|
|
Assert.That(clock.Alpha, Is.InRange(0.97, 0.99));
|
|
steps += clock.Advance(0.052, () => true);
|
|
Assert.AreEqual(2, steps);
|
|
Assert.That(clock.Alpha, Is.InRange(0.01, 0.03));
|
|
}
|
|
}
|
|
}
|