U2D Playable Unity Match

Vollständige Bot-Partien sichtbar in Unity, gleich der Partie des Kerns
(Vertical Function Test vor dem vollständigen Asset-Import).

- VerticalFunctionTests (PlayMode): Seed 2 von Runde 1 bis zum Fall des
  Blau-Ancient (9 Runden; Türme fallen ab Runde 7, Grottenboss in Runde 8
  gewonnen und in Runde 9 auf der Bot-Lane eingesetzt, bis zu 4 Helden) und
  Seed 9 ab Runde 21 mit der Fixture des Golden-Szenarios high_wave_21 (bis zu
  125 Einheiten, 6 Helden, Fall des Rot-Ancient). Beide Fingerabdrücke gleich
  dem Kernlauf; 14 Bilder der Schlüsselmomente.
- macOS-Player-Build (LunePlayerBuild) und LuneBenchmark: 1080p, VSync aus,
  116 Einheiten: 161-199 fps, Hauptthread 1,3 ms, Simulation unter 0,1 ms;
  Ausreißer beim Present (p95 18-26 ms) analysiert und für U5 vorgemerkt.
- Dokumentation: U2-WORLD (U2D, Abnahme), PORT-STATUS, PORT-QA,
  KNOWN-BEHAVIOR-DIFFERENCES (U2 ändert kein Verhalten), README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Martin Stickelbroeck 2026-10-01 17:31:19 +02:00
parent 3871b3e2e6
commit 323cc3ae00
32 changed files with 1353 additions and 48 deletions

View file

@ -0,0 +1,32 @@
using System.IO;
using UnityEditor;
using UnityEditor.Build.Reporting;
using UnityEngine;
namespace Luneraria.EditorTools
{
/// <summary>
/// Player builds (U2D). Only macOS is possible on this machine; Windows and Linux need the build
/// modules listed in docs/port/U1.5-UNITY.md. Output goes to Builds/ (ignored by git).
/// Unity -batchmode -projectPath . -executeMethod Luneraria.EditorTools.LunePlayerBuild.BuildMac -logFile -
/// </summary>
public static class LunePlayerBuild
{
public static void BuildMac()
{
string path = Path.Combine(Path.GetDirectoryName(Application.dataPath), "Builds/macOS/Luneraria.app");
// CPU and GPU frame timings for the benchmark (FrameTimingManager).
PlayerSettings.enableFrameTimingStats = true;
var options = new BuildPlayerOptions
{
scenes = new[] { LuneWorldBuilder.ScenePath },
locationPathName = path,
target = BuildTarget.StandaloneOSX,
options = BuildOptions.None,
};
BuildReport report = BuildPipeline.BuildPlayer(options);
Debug.Log(string.Format("LUNE_BUILD {0}: {1}, {2:0.0} MB, {3:0.0} s", report.summary.result, path, report.summary.totalSize / 1048576.0, report.summary.totalTime.TotalSeconds));
EditorApplication.Exit(report.summary.result == BuildResult.Succeeded ? 0 : 1);
}
}
}

View file

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 05d92220eeaed4813a610d541ef7bd4f

View file

@ -1,5 +1,560 @@
%YAML 1.1 %YAML 1.1
%TAG !u! tag:unity3d.com,2011: %TAG !u! tag:unity3d.com,2011:
--- !u!114 &-8123393559496137545
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 66f335fb1ffd8684294ad653bf1c7564, type: 3}
m_Name: ColorAdjustments
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ColorAdjustments
active: 1
postExposure:
m_OverrideState: 1
m_Value: 0
contrast:
m_OverrideState: 1
m_Value: 0
colorFilter:
m_OverrideState: 1
m_Value: {r: 1, g: 1, b: 1, a: 1}
hueShift:
m_OverrideState: 1
m_Value: 0
saturation:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-7655542585027984839
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c5e1dc532bcb41949b58bc4f2abfbb7e, type: 3}
m_Name: LensDistortion
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.LensDistortion
active: 1
intensity:
m_OverrideState: 1
m_Value: 0
xMultiplier:
m_OverrideState: 1
m_Value: 1
yMultiplier:
m_OverrideState: 1
m_Value: 1
center:
m_OverrideState: 1
m_Value: {x: 0.5, y: 0.5}
scale:
m_OverrideState: 1
m_Value: 1
--- !u!114 &-7615108799411823199
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3eb4b772797da9440885e8bd939e9560, type: 3}
m_Name: ColorCurves
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ColorCurves
active: 1
master:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 2
m_Loop: 0
m_ZeroValue: 0
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 1
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
red:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 2
m_Loop: 0
m_ZeroValue: 0
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 1
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
green:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 2
m_Loop: 0
m_ZeroValue: 0
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 1
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
blue:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 2
m_Loop: 0
m_ZeroValue: 0
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 1
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
hueVsHue:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 0
m_Loop: 1
m_ZeroValue: 0.5
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
hueVsSat:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 0
m_Loop: 1
m_ZeroValue: 0.5
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
satVsSat:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 0
m_Loop: 0
m_ZeroValue: 0.5
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
lumVsSat:
m_OverrideState: 1
m_Value:
<length>k__BackingField: 0
m_Loop: 0
m_ZeroValue: 0.5
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
--- !u!114 &-7576830524726348909
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c01700fd266d6914ababb731e09af2eb, type: 3}
m_Name: DepthOfField
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.DepthOfField
active: 1
mode:
m_OverrideState: 1
m_Value: 0
gaussianStart:
m_OverrideState: 1
m_Value: 10
gaussianEnd:
m_OverrideState: 1
m_Value: 30
gaussianMaxRadius:
m_OverrideState: 1
m_Value: 1
highQualitySampling:
m_OverrideState: 1
m_Value: 0
focusDistance:
m_OverrideState: 1
m_Value: 10
aperture:
m_OverrideState: 1
m_Value: 5.6
focalLength:
m_OverrideState: 1
m_Value: 50
bladeCount:
m_OverrideState: 1
m_Value: 5
bladeCurvature:
m_OverrideState: 1
m_Value: 1
bladeRotation:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-6858918158855671789
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fb60a22f311433c4c962b888d1393f88, type: 3}
m_Name: PaniniProjection
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.PaniniProjection
active: 1
distance:
m_OverrideState: 1
m_Value: 0
cropToFit:
m_OverrideState: 1
m_Value: 1
--- !u!114 &-5760856276792176683
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 899c54efeace73346a0a16faa3afe726, type: 3}
m_Name: Vignette
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.Vignette
active: 1
color:
m_OverrideState: 1
m_Value: {r: 0, g: 0, b: 0, a: 1}
center:
m_OverrideState: 1
m_Value: {x: 0.5, y: 0.5}
intensity:
m_OverrideState: 1
m_Value: 0
smoothness:
m_OverrideState: 1
m_Value: 0.2
rounded:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-5495840893406415533
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 29fa0085f50d5e54f8144f766051a691, type: 3}
m_Name: FilmGrain
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.FilmGrain
active: 1
type:
m_OverrideState: 1
m_Value: 0
intensity:
m_OverrideState: 1
m_Value: 0
response:
m_OverrideState: 1
m_Value: 0.8
texture:
m_OverrideState: 1
m_Value: {fileID: 0}
--- !u!114 &-5026977458242833459
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 221518ef91623a7438a71fef23660601, type: 3}
m_Name: WhiteBalance
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.WhiteBalance
active: 1
temperature:
m_OverrideState: 1
m_Value: 0
tint:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-2986203292941192888
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0b2db86121404754db890f4c8dfe81b2, type: 3}
m_Name: Bloom
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.Bloom
active: 1
skipIterations:
m_OverrideState: 1
m_Value: 1
threshold:
m_OverrideState: 1
m_Value: 0.9
intensity:
m_OverrideState: 1
m_Value: 0
scatter:
m_OverrideState: 1
m_Value: 0.7
clamp:
m_OverrideState: 1
m_Value: 65472
tint:
m_OverrideState: 1
m_Value: {r: 1, g: 1, b: 1, a: 1}
highQualityFiltering:
m_OverrideState: 1
m_Value: 0
filter:
m_OverrideState: 1
m_Value: 0
downscale:
m_OverrideState: 1
m_Value: 0
maxIterations:
m_OverrideState: 1
m_Value: 6
dirtTexture:
m_OverrideState: 1
m_Value: {fileID: 0}
dimension: 1
dirtIntensity:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-2319901517658964290
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 97c23e3b12dc18c42a140437e53d3951, type: 3}
m_Name: Tonemapping
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.Tonemapping
active: 1
mode:
m_OverrideState: 1
m_Value: 0
neutralHDRRangeReductionMode:
m_OverrideState: 1
m_Value: 2
acesPreset:
m_OverrideState: 1
m_Value: 3
hueShiftAmount:
m_OverrideState: 1
m_Value: 0
detectPaperWhite:
m_OverrideState: 1
m_Value: 0
paperWhite:
m_OverrideState: 1
m_Value: 300
detectBrightnessLimits:
m_OverrideState: 1
m_Value: 1
minNits:
m_OverrideState: 1
m_Value: 0.005
maxNits:
m_OverrideState: 1
m_Value: 1000
--- !u!114 &-1860047564659748416
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 06437c1ff663d574d9447842ba0a72e4, type: 3}
m_Name: ScreenSpaceLensFlare
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ScreenSpaceLensFlare
active: 1
intensity:
m_OverrideState: 1
m_Value: 0
tintColor:
m_OverrideState: 1
m_Value: {r: 1, g: 1, b: 1, a: 1}
bloomMip:
m_OverrideState: 1
m_Value: 1
firstFlareIntensity:
m_OverrideState: 1
m_Value: 1
secondaryFlareIntensity:
m_OverrideState: 1
m_Value: 1
warpedFlareIntensity:
m_OverrideState: 1
m_Value: 1
warpedFlareScale:
m_OverrideState: 1
m_Value: {x: 1, y: 1}
samples:
m_OverrideState: 1
m_Value: 1
sampleDimmer:
m_OverrideState: 1
m_Value: 0.5
vignetteEffect:
m_OverrideState: 1
m_Value: 1
startingPosition:
m_OverrideState: 1
m_Value: 1.25
scale:
m_OverrideState: 1
m_Value: 1.5
streaksIntensity:
m_OverrideState: 1
m_Value: 0
streaksLength:
m_OverrideState: 1
m_Value: 0.5
streaksOrientation:
m_OverrideState: 1
m_Value: 0
streaksThreshold:
m_OverrideState: 1
m_Value: 0.25
resolution:
m_OverrideState: 1
m_Value: 4
chromaticAbberationIntensity:
m_OverrideState: 1
m_Value: 0.5
--- !u!114 &-1262730450789330644
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 81180773991d8724ab7f2d216912b564, type: 3}
m_Name: ChromaticAberration
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ChromaticAberration
active: 1
intensity:
m_OverrideState: 1
m_Value: 0
--- !u!114 &11400000 --- !u!114 &11400000
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -12,4 +567,232 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d7fd9488000d3734a9e00ee676215985, type: 3} m_Script: {fileID: 11500000, guid: d7fd9488000d3734a9e00ee676215985, type: 3}
m_Name: DefaultVolumeProfile m_Name: DefaultVolumeProfile
m_EditorClassIdentifier: Unity.RenderPipelines.Core.Runtime::UnityEngine.Rendering.VolumeProfile m_EditorClassIdentifier: Unity.RenderPipelines.Core.Runtime::UnityEngine.Rendering.VolumeProfile
components: [] components:
- {fileID: 743231449427135496}
- {fileID: 3500651641176376349}
- {fileID: -7615108799411823199}
- {fileID: -7576830524726348909}
- {fileID: 1681678413439439198}
- {fileID: -5760856276792176683}
- {fileID: 3980703659364065184}
- {fileID: -1860047564659748416}
- {fileID: -7655542585027984839}
- {fileID: 8136988871774656336}
- {fileID: -2986203292941192888}
- {fileID: -8123393559496137545}
- {fileID: -6858918158855671789}
- {fileID: -1262730450789330644}
- {fileID: -2319901517658964290}
- {fileID: -5495840893406415533}
- {fileID: -5026977458242833459}
- {fileID: 329775769526288344}
- {fileID: 6038158131134407384}
--- !u!114 &329775769526288344
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e021b4c809a781e468c2988c016ebbea, type: 3}
m_Name: ColorLookup
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ColorLookup
active: 1
texture:
m_OverrideState: 1
m_Value: {fileID: 0}
dimension: 1
contribution:
m_OverrideState: 1
m_Value: 0
--- !u!114 &743231449427135496
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 558a8e2b6826cf840aae193990ba9f2e, type: 3}
m_Name: ShadowsMidtonesHighlights
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ShadowsMidtonesHighlights
active: 1
shadows:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
midtones:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
highlights:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
shadowsStart:
m_OverrideState: 1
m_Value: 0
shadowsEnd:
m_OverrideState: 1
m_Value: 0.3
highlightsStart:
m_OverrideState: 1
m_Value: 0.55
highlightsEnd:
m_OverrideState: 1
m_Value: 1
--- !u!114 &1681678413439439198
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ccf1aba9553839d41ae37dd52e9ebcce, type: 3}
m_Name: MotionBlur
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.MotionBlur
active: 1
mode:
m_OverrideState: 1
m_Value: 0
quality:
m_OverrideState: 1
m_Value: 0
intensity:
m_OverrideState: 1
m_Value: 0
clamp:
m_OverrideState: 1
m_Value: 0.05
--- !u!114 &3500651641176376349
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: cdfbdbb87d3286943a057f7791b43141, type: 3}
m_Name: ChannelMixer
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ChannelMixer
active: 1
redOutRedIn:
m_OverrideState: 1
m_Value: 100
redOutGreenIn:
m_OverrideState: 1
m_Value: 0
redOutBlueIn:
m_OverrideState: 1
m_Value: 0
greenOutRedIn:
m_OverrideState: 1
m_Value: 0
greenOutGreenIn:
m_OverrideState: 1
m_Value: 100
greenOutBlueIn:
m_OverrideState: 1
m_Value: 0
blueOutRedIn:
m_OverrideState: 1
m_Value: 0
blueOutGreenIn:
m_OverrideState: 1
m_Value: 0
blueOutBlueIn:
m_OverrideState: 1
m_Value: 100
--- !u!114 &3980703659364065184
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5485954d14dfb9a4c8ead8edb0ded5b1, type: 3}
m_Name: LiftGammaGain
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.LiftGammaGain
active: 1
lift:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
gamma:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
gain:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
--- !u!114 &6038158131134407384
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 6bd486065ce11414fa40e631affc4900, type: 3}
m_Name: ProbeVolumesOptions
m_EditorClassIdentifier: Unity.RenderPipelines.Core.Runtime::UnityEngine.Rendering.ProbeVolumesOptions
active: 1
normalBias:
m_OverrideState: 1
m_Value: 0.05
viewBias:
m_OverrideState: 1
m_Value: 0.1
scaleBiasWithMinProbeDistance:
m_OverrideState: 1
m_Value: 0
samplingNoise:
m_OverrideState: 1
m_Value: 0.1
animateSamplingNoise:
m_OverrideState: 1
m_Value: 1
leakReductionMode:
m_OverrideState: 1
m_Value: 2
minValidDotProductValue:
m_OverrideState: 1
m_Value: 0.1
occlusionOnlyReflectionNormalization:
m_OverrideState: 1
m_Value: 1
intensityMultiplier:
m_OverrideState: 1
m_Value: 1
skyOcclusionIntensityMultiplier:
m_OverrideState: 1
m_Value: 1
worldOffset:
m_OverrideState: 1
m_Value: {x: 0, y: 0, z: 0}
--- !u!114 &8136988871774656336
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70afe9e12c7a7ed47911bb608a23a8ff, type: 3}
m_Name: SplitToning
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.SplitToning
active: 1
shadows:
m_OverrideState: 1
m_Value: {r: 0.5, g: 0.5, b: 0.5, a: 1}
highlights:
m_OverrideState: 1
m_Value: {r: 0.5, g: 0.5, b: 0.5, a: 1}
balance:
m_OverrideState: 1
m_Value: 0

View file

@ -100,42 +100,42 @@ MonoBehaviour:
obsoleteHasProbeVolumes: obsoleteHasProbeVolumes:
m_Keys: [] m_Keys: []
m_Values: m_Values:
m_PrefilteringModeMainLightShadows: 1 m_PrefilteringModeMainLightShadows: 3
m_PrefilteringModeAdditionalLight: 4 m_PrefilteringModeAdditionalLight: 3
m_PrefilteringModeAdditionalLightShadows: 1 m_PrefilteringModeAdditionalLightShadows: 0
m_PrefilterXRKeywords: 0 m_PrefilterXRKeywords: 1
m_PrefilteringModeForwardPlus: 1 m_PrefilteringModeForwardPlus: 0
m_PrefilteringModeDeferredRendering: 1 m_PrefilteringModeDeferredRendering: 0
m_PrefilteringModeScreenSpaceOcclusion: 1 m_PrefilteringModeScreenSpaceOcclusion: 0
m_PrefilterDebugKeywords: 0 m_PrefilterDebugKeywords: 1
m_PrefilterWriteRenderingLayers: 0 m_PrefilterWriteRenderingLayers: 1
m_PrefilterHDROutput: 0 m_PrefilterHDROutput: 1
m_PrefilterAlphaOutput: 0 m_PrefilterAlphaOutput: 1
m_PrefilterSSAODepthNormals: 0 m_PrefilterSSAODepthNormals: 1
m_PrefilterSSAOSourceDepthLow: 0 m_PrefilterSSAOSourceDepthLow: 1
m_PrefilterSSAOSourceDepthMedium: 0 m_PrefilterSSAOSourceDepthMedium: 1
m_PrefilterSSAOSourceDepthHigh: 0 m_PrefilterSSAOSourceDepthHigh: 1
m_PrefilterSSAOInterleaved: 0 m_PrefilterSSAOInterleaved: 1
m_PrefilterSSAOBlueNoise: 0 m_PrefilterSSAOBlueNoise: 1
m_PrefilterSSAOSampleCountLow: 0 m_PrefilterSSAOSampleCountLow: 1
m_PrefilterSSAOSampleCountMedium: 0 m_PrefilterSSAOSampleCountMedium: 1
m_PrefilterSSAOSampleCountHigh: 0 m_PrefilterSSAOSampleCountHigh: 1
m_PrefilterDBufferMRT1: 0 m_PrefilterDBufferMRT1: 1
m_PrefilterDBufferMRT2: 0 m_PrefilterDBufferMRT2: 1
m_PrefilterDBufferMRT3: 0 m_PrefilterDBufferMRT3: 1
m_PrefilterSoftShadowsQualityLow: 0 m_PrefilterSoftShadowsQualityLow: 1
m_PrefilterSoftShadowsQualityMedium: 0 m_PrefilterSoftShadowsQualityMedium: 1
m_PrefilterSoftShadowsQualityHigh: 0 m_PrefilterSoftShadowsQualityHigh: 1
m_PrefilterSoftShadows: 0 m_PrefilterSoftShadows: 0
m_PrefilterScreenCoord: 0 m_PrefilterScreenCoord: 1
m_PrefilterScreenSpaceIrradiance: 0 m_PrefilterScreenSpaceIrradiance: 1
m_PrefilterNativeRenderPass: 0 m_PrefilterNativeRenderPass: 1
m_PrefilterUseLegacyLightmaps: 0 m_PrefilterUseLegacyLightmaps: 0
m_PrefilterBicubicLightmapSampling: 0 m_PrefilterBicubicLightmapSampling: 1
m_PrefilterReflectionProbeRotation: 0 m_PrefilterReflectionProbeRotation: 0
m_PrefilterReflectionProbeBlending: 0 m_PrefilterReflectionProbeBlending: 1
m_PrefilterReflectionProbeBoxProjection: 0 m_PrefilterReflectionProbeBoxProjection: 1
m_PrefilterReflectionProbeAtlas: 0 m_PrefilterReflectionProbeAtlas: 1
m_ShaderVariantLogLevel: 0 m_ShaderVariantLogLevel: 0
m_ShadowCascades: 0 m_ShadowCascades: 0
m_Textures: m_Textures:

View file

@ -68,7 +68,21 @@ MonoBehaviour:
- rid: 5026820519263993890 - rid: 5026820519263993890
- rid: 5026820519263993891 - rid: 5026820519263993891
m_RuntimeSettings: m_RuntimeSettings:
m_List: [] m_List:
- rid: 5026820519263993859
- rid: 5026820519263993860
- rid: 5026820519263993863
- rid: 5026820519263993864
- rid: 5026820519263993869
- rid: 5026820519263993870
- rid: 5026820519263993871
- rid: 5026820519263993872
- rid: 5026820519263993876
- rid: 5026820519263993880
- rid: 5026820519263993881
- rid: 5026820519263993882
- rid: 5026820519263993887
- rid: 5026820519263993891
m_AssetVersion: 10 m_AssetVersion: 10
m_ObsoleteDefaultVolumeProfile: {fileID: 0} m_ObsoleteDefaultVolumeProfile: {fileID: 0}
m_RenderingLayerNames: m_RenderingLayerNames:

View file

@ -0,0 +1,278 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Luneraria.Core;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
namespace Luneraria.View.Tests
{
/// <summary>
/// U2D vertical function test: complete bot matches visible in the Unity scene, before the full
/// asset import. Match 1 (seed 2) runs from round 1 to the fall of an Ancient: lanes, towers
/// fall, heroes fight, the grotto boss is won and deployed. Match 2 (seed 9) starts at round 21
/// with the fixture of the golden scenario high_wave_21 (waves above 20). Both must end with the
/// fingerprint of the plain core run. A camera director photographs the key moments
/// (docs/port/screenshots/u2d/); results and frame times go to docs/port/logs/u2d/.
/// </summary>
public sealed class VerticalFunctionTests
{
const float Speed = 6f;
sealed class Shot
{
public string Label;
public Func<LuneMatchRunner, LuneMatchView, bool> Trigger;
public Action<LuneMatchRunner, LuneMatchView, LuneCameraRig> Aim;
public float Delay = .35f;
public bool Done;
}
sealed class Stats
{
public int Frames;
public double Seconds;
public readonly List<double> FrameMs = new List<double>();
public double AdvanceMs;
public double SyncMs;
public int Units;
}
[UnityTest]
[Timeout(3600000)]
public IEnumerator BotMatchesRunVisiblyAndMatchTheCore()
{
var report = new StringBuilder("{\n \"runs\": [\n");
var failures = new List<string>();
yield return Play(2, 1, 0, "match-seed2", SeedTwoShots(), report, failures, false);
report.Append(",\n");
yield return Play(9, 21, 4000, "match-seed9-round21", WaveShots(), report, failures, true);
report.Append("\n ]\n}\n");
string logs = Path.Combine(PlayModeSupport.Root, "docs/port/logs/u2d");
Directory.CreateDirectory(logs);
File.WriteAllText(Path.Combine(logs, "vertical-function-test.json"), report.ToString());
Assert.IsEmpty(failures, string.Join("\n", failures));
}
static IEnumerator Play(int seed, int startRound, int extraGold, string label, List<Shot> shots, StringBuilder report, List<string> failures, bool measure)
{
var options = new MatchOptions { Seed = seed, HumanSlot = -1 };
LuneTrace core = LuneTrace.RunCore(options, startRound, extraGold, "");
QualitySettings.vSyncCount = 0;
Application.targetFrameRate = 60;
yield return PlayModeSupport.Load();
var driver = UnityEngine.Object.FindAnyObjectByType<LuneMatchDriver>();
var view = UnityEngine.Object.FindAnyObjectByType<LuneMatchView>();
var rig = UnityEngine.Object.FindAnyObjectByType<LuneCameraRig>();
Camera camera = Camera.main;
rig.Interactive = false;
driver.Seed = seed;
driver.StartRound = startRound;
driver.ExtraGold = extraGold;
driver.Speed = Speed;
driver.Restart();
LuneMatchRunner runner = driver.Runner;
rig.Overview(true);
var rounds = new List<string>();
var events = new List<string>();
int maxUnits = 0;
int maxHeroes = 0;
runner.Settled += settlement =>
{
rounds.Add(string.Format("{{\"round\": {0}, \"combat_seconds\": {1}, \"lost\": [{2}], \"boss\": \"{3}\", \"end\": \"{4}\"}}", settlement.Round,
PlayModeSupport.Number(settlement.CombatTime), string.Join(", ", settlement.BuildingsLost.Select(b => "\"" + b + "\"")), settlement.Boss, settlement.EndReason));
foreach (string building in settlement.BuildingsLost)
events.Add(string.Format("\"round {0}: {1} destroyed\"", settlement.Round, building));
if (settlement.Boss != "none")
events.Add(string.Format("\"round {0}: grotto boss {1}\"", settlement.Round, settlement.Boss));
};
var captures = new List<string>();
Shot active = null;
float activeSince = 0f;
var stats = new Stats();
bool measuring = false;
float realStart = Time.realtimeSinceStartup;
float overSince = -1f;
while (Time.realtimeSinceStartup - realStart < 900f)
{
yield return null;
LuneMatch match = runner.Match;
if (match.Combat != null && runner.Current == LuneMatchRunner.Stage.Combat)
{
maxUnits = Math.Max(maxUnits, match.Combat.Units.Count(u => u.IsMobile() && u.Alive));
maxHeroes = Math.Max(maxHeroes, match.Combat.Units.Count(u => u.Kind == "hero" && u.Alive));
}
// Frame times at normal game speed with the most units on the map (match 2, round 21).
if (measure && runner.Current == LuneMatchRunner.Stage.Combat && match.RoundNumber == startRound)
{
double elapsed = match.Combat.Elapsed;
if (!measuring && elapsed >= 10.0 && stats.Frames == 0)
{
measuring = true;
driver.Speed = 1f;
Application.targetFrameRate = -1;
rig.LookAt(new Vec2(0, 0), 64f, true);
}
if (measuring)
{
stats.Frames++;
stats.Seconds += Time.unscaledDeltaTime;
stats.FrameMs.Add(Time.unscaledDeltaTime * 1000.0);
stats.AdvanceMs += driver.LastAdvanceMs;
stats.SyncMs += view.LastSyncMs;
stats.Units = Math.Max(stats.Units, match.Combat.Units.Count(u => u.IsMobile() && u.Alive));
if (stats.Seconds >= 20.0)
{
measuring = false;
driver.Speed = Speed;
Application.targetFrameRate = 60;
}
}
}
if (measuring)
continue;
if (active == null)
{
active = shots.FirstOrDefault(s => !s.Done && s.Trigger(runner, view));
if (active != null)
{
rig.SetYaw(0f, true);
active.Aim(runner, view, rig);
activeSince = Time.realtimeSinceStartup;
}
}
else if (Time.realtimeSinceStartup - activeSince >= active.Delay)
{
// Render directly: WaitForEndOfFrame never resumes in batch mode.
string file = Path.Combine(PlayModeSupport.Root, "docs/port/screenshots/u2d", active.Label + ".png");
LuneCapture.Render(camera, file);
captures.Add("\"" + active.Label + "\"");
Debug.Log("LUNE_U2D shot " + active.Label + " round " + runner.Match.RoundNumber + " " + runner.Current);
active.Done = true;
active = null;
}
if (runner.Current == LuneMatchRunner.Stage.Over)
{
if (overSince < 0f)
overSince = Time.realtimeSinceStartup;
bool pending = shots.Any(s => !s.Done && s.Trigger(runner, view)) || active != null;
if (!pending && Time.realtimeSinceStartup - overSince > 3.5f)
break;
if (Time.realtimeSinceStartup - overSince > 12f)
break;
}
}
LuneTrace trace = runner.Trace;
bool same = runner.Current == LuneMatchRunner.Stage.Over && trace.Hash == core.Hash && trace.Steps == core.Steps;
if (!same)
failures.Add(label + ": fingerprint " + trace.Hash.ToString("X16") + " / core " + core.Hash.ToString("X16"));
foreach (Shot shot in shots.Where(s => !s.Done))
failures.Add(label + ": no shot " + shot.Label);
double[] frameMs = stats.FrameMs.OrderBy(v => v).ToArray();
string performance = stats.Frames == 0 ? "null" : string.Format("{{\"window\": \"20 s real time at 1x speed, unlimited frame rate, round {0}\", \"frames\": {1}, \"fps\": {2}, \"frame_ms_avg\": {3}, \"frame_ms_p95\": {4}, \"simulation_ms_per_frame\": {5}, \"views_ms_per_frame\": {6}, \"units_alive_max\": {7}}}",
startRound, stats.Frames, PlayModeSupport.Number(stats.Frames / stats.Seconds), PlayModeSupport.Number(stats.Seconds * 1000.0 / stats.Frames),
PlayModeSupport.Number(frameMs[(int)(frameMs.Length * .95)]), PlayModeSupport.Number(stats.AdvanceMs / stats.Frames), PlayModeSupport.Number(stats.SyncMs / stats.Frames), stats.Units);
report.AppendFormat(" {{\"label\": \"{0}\", \"seed\": {1}, \"start_round\": {2}, \"extra_gold\": {3}, \"speed\": {4}, \"winner\": {5}, \"end_note\": \"{6}\", \"steps\": {7}, \"hash\": \"{8:X16}\", \"core_hash\": \"{9:X16}\", \"same_as_core\": {10},\n \"units_alive_max\": {11}, \"heroes_alive_max\": {12}, \"boss_log\": [{13}],\n \"rounds\": [{14}],\n \"events\": [{15}],\n \"shots\": [{16}],\n \"performance\": {17}}}",
label, seed, startRound, extraGold, Speed, runner.Match.Winner, runner.Match.EndNote, trace.Steps, trace.Hash, core.Hash, same ? "true" : "false",
maxUnits, maxHeroes, string.Join(", ", runner.Match.BossLog.Select(e => string.Format("\"round {0}: {1} team {2} {3}\"", e.Round, e.Event, e.Team, e.Lane))),
string.Join(", ", rounds), string.Join(", ", events), string.Join(", ", captures), performance);
Application.targetFrameRate = -1;
}
static bool InCombat(LuneMatchRunner runner, int round, double from)
{
return runner.Current == LuneMatchRunner.Stage.Combat && runner.Match.RoundNumber == round && runner.Match.Combat.Elapsed >= from;
}
static LuneUnit Find(LuneMatchRunner runner, Func<LuneUnit, bool> predicate)
{
return runner.Match.Combat == null ? null : runner.Match.Combat.Units.FirstOrDefault(predicate);
}
static Shot Building(string label, Func<LuneMatchView, LuneBuildingView> pick, LuneBuildingView.Look look, float zoom, float delay)
{
LuneBuildingView chosen = null;
return new Shot
{
Label = label,
Delay = delay,
Trigger = (runner, view) =>
{
chosen = pick(view);
return chosen != null && chosen.Current == look;
},
Aim = (runner, view, rig) =>
{
// Towers are watched from their lane (open ground), not through the forest.
Vec2 at = LuneSpace.Map(chosen.transform.position);
BuildingDef definition = runner.Match.Map.BuildingDef(chosen.BuildingId);
float yaw = 0f;
if (!chosen.IsAncient && definition.Tier != "base")
yaw = LuneCameraRig.YawTowards(at, runner.Match.Map.LanePoint(definition.Team, definition.Lane, definition.Progress));
rig.SetYaw(yaw, true);
rig.LookAt(at, zoom, true);
},
};
}
static List<Shot> SeedTwoShots()
{
string fallen = null;
return new List<Shot>
{
new Shot { Label = "01-runde1-aufmarsch", Trigger = (r, v) => InCombat(r, 1, 9.0), Aim = (r, v, rig) => rig.LookAt(new Vec2(0, 0), 64f, true) },
new Shot { Label = "02-runde1-mid-kampf", Trigger = (r, v) => InCombat(r, 1, 24.0), Aim = (r, v, rig) =>
{
LuneUnit fighter = Find(r, u => u.IsMobile() && u.Alive && u.Lane == "mid" && u.Target != null && u.InRange);
rig.LookAt(fighter != null ? fighter.Position : new Vec2(0, 0), 20f, true);
} },
Building("03-turm-faellt", v => AnyBuilding(v, b => !b.IsAncient && b.Current == LuneBuildingView.Look.Destroying, ref fallen), LuneBuildingView.Look.Destroying, 22f, .7f),
Building("04-turm-ruine", v => fallen != null ? v.Building(fallen) : null, LuneBuildingView.Look.Ruin, 22f, .4f),
new Shot { Label = "05-grotte-bosskampf", Delay = .5f, Trigger = (r, v) => Find(r, u => u.Kind == "boss" && u.Zone == "grotto" && u.Alive && !u.Dormant) != null,
Aim = (r, v, rig) => rig.LookAt(LuneMap.Grotto, 20f, true) },
new Shot { Label = "06-held-kaempft", Delay = .3f, Trigger = (r, v) => Find(r, u => u.Kind == "hero" && u.Alive && u.Zone == "lane" && u.Target != null && u.InRange) != null,
Aim = (r, v, rig) => rig.LookAt(Find(r, u => u.Kind == "hero" && u.Alive && u.Zone == "lane" && u.Target != null && u.InRange).Position, 16f, true) },
new Shot { Label = "07-boss-auf-der-lane", Delay = .3f, Trigger = (r, v) => r.Current == LuneMatchRunner.Stage.Combat && r.Match.Combat.Elapsed > 12.0 && Find(r, u => u.Kind == "boss" && u.Zone == "lane" && u.Alive) != null,
Aim = (r, v, rig) => rig.LookAt(Find(r, u => u.Kind == "boss" && u.Zone == "lane" && u.Alive).Position, 22f, true) },
Building("08-ancient-faellt", v => v.Building("blau_ancient"), LuneBuildingView.Look.Destroying, 30f, .8f),
Building("09-ancient-ruine", v => v.Building("blau_ancient"), LuneBuildingView.Look.Ruin, 30f, .5f),
new Shot { Label = "10-ende-uebersicht", Delay = .5f, Trigger = (r, v) => r.Current == LuneMatchRunner.Stage.Over && v.Building("blau_ancient").Current == LuneBuildingView.Look.Ruin,
Aim = (r, v, rig) => rig.Overview(true) },
};
}
/// <summary>The first building that satisfies the condition; remembers its id.</summary>
static LuneBuildingView AnyBuilding(LuneMatchView view, Func<LuneBuildingView, bool> condition, ref string remembered)
{
foreach (LuneBuildingView building in UnityEngine.Object.FindObjectsByType<LuneBuildingView>(FindObjectsSortMode.None).OrderBy(b => b.BuildingId, StringComparer.Ordinal))
{
if (condition(building))
{
remembered = building.BuildingId;
return building;
}
}
return null;
}
static List<Shot> WaveShots()
{
return new List<Shot>
{
new Shot { Label = "11-welle21-uebersicht", Trigger = (r, v) => InCombat(r, 21, 6.0), Aim = (r, v, rig) => rig.LookAt(new Vec2(0, 0), 70f, true) },
new Shot { Label = "12-welle21-kampf", Trigger = (r, v) => InCombat(r, 21, 36.0), Aim = (r, v, rig) =>
{
LuneUnit fighter = Find(r, u => u.IsMobile() && u.Alive && u.Target != null && u.InRange && u.Zone == "lane");
rig.LookAt(fighter != null ? fighter.Position : new Vec2(0, 0), 24f, true);
} },
Building("13-ancient-rot-faellt", v => v.Building("rot_ancient"), LuneBuildingView.Look.Destroying, 30f, .8f),
new Shot { Label = "14-welle-ende", Delay = .6f, Trigger = (r, v) => r.Current == LuneMatchRunner.Stage.Over && v.Building("rot_ancient").Current == LuneBuildingView.Look.Ruin,
Aim = (r, v, rig) => rig.LookAt(r.Match.Map.CorePosition(1), 34f, true) },
};
}
}
}

View file

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 26a9b53c1b823413f92f2dc0a48c6412

View file

@ -154,7 +154,7 @@ PlayerSettings:
vrSettings: vrSettings:
enable360StereoCapture: 0 enable360StereoCapture: 0
isWsaHolographicRemotingEnabled: 0 isWsaHolographicRemotingEnabled: 0
enableFrameTimingStats: 0 enableFrameTimingStats: 1
enableOpenGLProfilerGPURecorders: 1 enableOpenGLProfilerGPURecorders: 1
allowHDRDisplaySupport: 0 allowHDRDisplaySupport: 0
useHDRDisplay: 0 useHDRDisplay: 0
@ -296,7 +296,10 @@ PlayerSettings:
androidSymbolsSizeThreshold: 800 androidSymbolsSizeThreshold: 800
m_BuildTargetIcons: [] m_BuildTargetIcons: []
m_BuildTargetPlatformIcons: [] m_BuildTargetPlatformIcons: []
m_BuildTargetBatching: [] m_BuildTargetBatching:
- m_BuildTarget: Standalone
m_StaticBatching: 1
m_DynamicBatching: 0
m_BuildTargetShaderSettings: [] m_BuildTargetShaderSettings: []
m_BuildTargetGraphicsJobs: [] m_BuildTargetGraphicsJobs: []
m_BuildTargetGraphicsJobMode: [] m_BuildTargetGraphicsJobMode: []

View file

@ -9,6 +9,9 @@ Drei-Lane-Autobattler 3 gegen 3 (Mischung aus Dota 2 und Legion TD 2), portiert
## Aufbau ## Aufbau
- `Assets/Luneraria/Core/`: Regelkern ohne Unity-Abhängigkeit (`Luneraria.Core`). - `Assets/Luneraria/Core/`: Regelkern ohne Unity-Abhängigkeit (`Luneraria.Core`).
- `Assets/Luneraria/View/`: Host-Schleife (fester Takt), Ansichten, Kamera (`Luneraria.View`); `Scenes/G1_Map.unity` ist die spielbare Karte.
- `Assets/Luneraria/Art/`: FBX aus der Blender-Pipeline (`tools/blender/`); `World/Bake/`: gebackene G1-Welt aus dem Godot-Generator.
- `Assets/Luneraria/Editor/`: Importregeln, Asset-Prüfung, Welt- und Szenenbau, Player-Build.
- `Assets/Luneraria/Tests/Core/`: Prüfsuiten mit Golden-Referenzen aus Godot, Diagnose (Float-Probe, Schritt-Trace, Benchmark); laufen im Unity Test Runner und mit dem .NET SDK. - `Assets/Luneraria/Tests/Core/`: Prüfsuiten mit Golden-Referenzen aus Godot, Diagnose (Float-Probe, Schritt-Trace, Benchmark); laufen im Unity Test Runner und mit dem .NET SDK.
- `tools/`: .NET-Projekte (Ausgabe nach `tools/out/`), Godot-Referenzwerkzeuge, Portwerkzeuge. - `tools/`: .NET-Projekte (Ausgabe nach `tools/out/`), Godot-Referenzwerkzeuge, Portwerkzeuge.
- `docs/port/`: Stand, Entscheidungen und QA je Meilenstein ([PORT-STATUS.md](docs/port/PORT-STATUS.md)). - `docs/port/`: Stand, Entscheidungen und QA je Meilenstein ([PORT-STATUS.md](docs/port/PORT-STATUS.md)).
@ -27,6 +30,14 @@ Dieselben Tests im Unity Test Runner:
/Applications/Unity/Hub/Editor/6000.3.20f1/Unity.app/Contents/MacOS/Unity -batchmode -projectPath "$PWD" -runTests -testPlatform EditMode -testResults "$PWD/tools/out/editmode-results.xml" -logFile - /Applications/Unity/Hub/Editor/6000.3.20f1/Unity.app/Contents/MacOS/Unity -batchmode -projectPath "$PWD" -runTests -testPlatform EditMode -testResults "$PWD/tools/out/editmode-results.xml" -logFile -
``` ```
Szene neu bauen (nach geänderter Welt, Materialien oder Assets):
```bash
/Applications/Unity/Hub/Editor/6000.3.20f1/Unity.app/Contents/MacOS/Unity -batchmode -projectPath "$PWD" -executeMethod Luneraria.EditorTools.LuneWorldBuilder.BuildScene -logFile -
```
Spielen: `Assets/Luneraria/Scenes/G1_Map.unity` im Editor öffnen und Play drücken (Bot-Partie, Kamera mit WASD, Mausrad, Q/E, R, O).
Leistung (.NET bzw. Editor über `-executeMethod Luneraria.Core.Tests.CoreBatch.Benchmark`): Leistung (.NET bzw. Editor über `-executeMethod Luneraria.Core.Tests.CoreBatch.Benchmark`):
```bash ```bash

View file

@ -32,6 +32,10 @@ Diese Fehler der Referenz sind im Port absichtlich nachgebildet. Die Golden-Pari
Details: [U1.5-UNITY.md](U1.5-UNITY.md#float-befund). Details: [U1.5-UNITY.md](U1.5-UNITY.md#float-befund).
## Unity-Szene (U2)
Die Szene ändert kein Verhalten. Der Host treibt den Kern im festen Takt; die Ansichten lesen nur. Die vollständige Bot-Partie (Seed 2) und die Partie ab Runde 21 (Seed 9) liefern in der Szene denselben Fingerabdruck wie der reine Kernlauf. Das gilt auch bei 30, 60 und 120 fps, unbegrenzter Bildrate, Rucklern und erhöhtem Spieltempo ([U2-WORLD.md](U2-WORLD.md#u2c--simulation-und-darstellung)).
## Darstellungswerte ## Darstellungswerte
| Wert | Warum kein Regelunterschied | | Wert | Warum kein Regelunterschied |

View file

@ -30,6 +30,18 @@ Hinweis: Die Projektdateien des .NET-Harness (`tools/dotnet/*/*.csproj`) fehlten
| Offen | IL2CPP- und Player-Läufe unter Windows 11 und Fedora 44 (Build-Module fehlen, Installation nach Freigabe) | | Offen | IL2CPP- und Player-Läufe unter Windows 11 und Fedora 44 (Build-Module fehlen, Installation nach Freigabe) |
| Commit | Zweig `luneraria/u1.5` | | Commit | Zweig `luneraria/u1.5` |
## U2 bis U5 ## U2 · Unity-Spielwelt
U2 ist der nächste Schritt; U3 bis U5 sind noch nicht begonnen. | Punkt | Ergebnis |
| --- | --- |
| Tests | Blender-Pipeline 176/176 und Unity-Verifier 128/128 (9 Assets). Test Runner EditMode 11/11: Kern 402 Prüfungen, 2 Integrationstests, 4 Bildratentests. PlayMode 2/2: Szene bei echten Bildraten, Vertical Function Test |
| Parität | Bot-Partie Seed 2 (9 Runden) und Seed 9 ab Runde 21 in der sichtbaren Szene mit demselben Fingerabdruck wie der reine Kern; ebenso bei 30, 60, 120 fps, unbegrenzt, mit Rucklern und bei 8× Tempo |
| Bild | `screenshots/u2a/` (Assets), `u2b/` (Karte mit Godot-Kameraeinstellungen; Vergleich mit `g2/nachher`), `u2d/` (14 Schlüsselmomente der Partien) |
| Leistung | macOS-Player (M5, 1080p, VSync aus) mit 116 Einheiten: 161–199 fps; Hauptthread 1,3 ms, Render-Thread 1,3 ms, Simulation unter 0,1 ms. Ausreißer beim Present (p95 18–26 ms) offen für U5 |
| Bekannte Unterschiede | keine Verhaltensunterschiede. Darstellung vereinfacht: Platzhaltermodelle für Einheiten, Materialien, Effekte, Debug-Anzeige statt HUD ([U2-WORLD.md](U2-WORLD.md)) |
| Offen | Batch-Import der übrigen Assets; Windows-/Linux-Player (Build-Module); Leistungsspitzen |
| Commits | `luneraria/u2`: U2A `49cb4af`, U2B `8a72cb1`, U2C, U2D |
## U3 bis U5
U3 (G3-HUD portieren) ist der nächste Schritt; U4 und U5 sind noch nicht begonnen.

View file

@ -1,6 +1,6 @@
# Port-Status · Champions of Luneraria in Unity # Port-Status · Champions of Luneraria in Unity
Stand: 30. September 2026. Stand: 1. Oktober 2026.
Referenz ist der Godot-Stand `luneraria/g3` (`1b540f2`) in `../saumwacht-godot`; er wird nicht mehr verändert. Engine: Unity 6.3 LTS (6000.3.20f1), Renderer URP. HDRP ist ausgeschlossen, bis ein Vergleich mit einer echten Szene etwas anderes nahelegt. Referenz ist der Godot-Stand `luneraria/g3` (`1b540f2`) in `../saumwacht-godot`; er wird nicht mehr verändert. Engine: Unity 6.3 LTS (6000.3.20f1), Renderer URP. HDRP ist ausgeschlossen, bis ein Vergleich mit einer echten Szene etwas anderes nahelegt.
@ -10,8 +10,8 @@ Zielplattformen sind Fedora 44 und Windows 11; macOS ist die Entwicklungsplattfo
| --- | --- | --- | | --- | --- | --- |
| U1 · C#-Simulation | **fertig** (Referenzmeilenstein, `luneraria/u1` = `4d6ed17`): 402/402 Prüfungen, Golden-Parität mit Godot | [U1-RULES.md](U1-RULES.md) | | U1 · C#-Simulation | **fertig** (Referenzmeilenstein, `luneraria/u1` = `4d6ed17`): 402/402 Prüfungen, Golden-Parität mit Godot | [U1-RULES.md](U1-RULES.md) |
| U1.5 · Unity-Integrationsprüfung | **fertig**: Kern im Unity-Editor 402/402 (Release und Debug), Golden bitgleich nach expliziter float32-Rundung in `Vec2`, Editor-Benchmark | [U1.5-UNITY.md](U1.5-UNITY.md) | | U1.5 · Unity-Integrationsprüfung | **fertig**: Kern im Unity-Editor 402/402 (Release und Debug), Golden bitgleich nach expliziter float32-Rundung in `Vec2`, Editor-Benchmark | [U1.5-UNITY.md](U1.5-UNITY.md) |
| U2 · Unity-Spielwelt | nächster Schritt | [U2-WORLD.md](U2-WORLD.md) | | U2 · Unity-Spielwelt | **fertig**: U2A Blender→FBX-Pipeline (9 Assets geprüft), U2B G1-Karte mit Materialsystem, U2C fester Takt und Ansichten (bildratenunabhängig nachgewiesen), U2D vollständige Bot-Partien sichtbar und gleich dem Kern | [U2-WORLD.md](U2-WORLD.md) |
| U3 · G3-HUD portieren | nicht begonnen | [U3-UI.md](U3-UI.md) | | U3 · G3-HUD portieren | nächster Schritt | [U3-UI.md](U3-UI.md) |
| U4 · Netzwerk | nicht begonnen; M4-Umfang ist erfasst | [U4-NETWORK.md](U4-NETWORK.md) | | U4 · Netzwerk | nicht begonnen; M4-Umfang ist erfasst | [U4-NETWORK.md](U4-NETWORK.md) |
| U5 · Visuelle Neuausrichtung (Vertical Slice Mid-Lane) | nicht begonnen | [U5-VISUAL.md](U5-VISUAL.md) | | U5 · Visuelle Neuausrichtung (Vertical Slice Mid-Lane) | nicht begonnen | [U5-VISUAL.md](U5-VISUAL.md) |
@ -22,7 +22,8 @@ Bekannte Verhaltensunterschiede, auch die bewusst übernommenen Referenzfehler:
## Offene Voraussetzungen ## Offene Voraussetzungen
- **Build-Module für die Zielplattformen fehlen:** Windows Build Support (Mono) und Linux Build Support (Mono), optional Linux IL2CPP und Linux Dedicated Server. Installation nur nach Freigabe (Details in U1.5-UNITY.md). - **Build-Module für die Zielplattformen fehlen:** Windows Build Support (Mono) und Linux Build Support (Mono), optional Linux IL2CPP und Linux Dedicated Server. Installation nur nach Freigabe (Details in U1.5-UNITY.md).
- Kerntests und Player auf Windows 11 und Fedora 44 stehen deshalb aus. - Kerntests und Player auf Windows 11 und Fedora 44 stehen deshalb aus; gemessen ist bisher der macOS-Player.
- **Batch-Import der übrigen Assets** (58 Phase-4-Modelle, 5 Figuren, 7 Völker) ist nach dem Vertical Function Test jetzt an der Reihe.
## Entscheidungen ## Entscheidungen
@ -32,13 +33,18 @@ Bekannte Verhaltensunterschiede, auch die bewusst übernommenen Referenzfehler:
- **Tabellen** einmal aus Godot erzeugt (`tools/port/gen_content.py`); danach ist `ContentData.cs` die gepflegte Quelle. - **Tabellen** einmal aus Godot erzeugt (`tools/port/gen_content.py`); danach ist `ContentData.cs` die gepflegte Quelle.
- **Referenzfehler R1** im Bot-Gedächtnis ist bewusst übernommen (Parität) und für eine spätere Bot-Phase vorgemerkt. - **Referenzfehler R1** im Bot-Gedächtnis ist bewusst übernommen (Parität) und für eine spätere Bot-Phase vorgemerkt.
- **Geplante Helden** (Lucy, Ghaliga, Siegfried, Feena) stehen im Datenmodell, sind aber inaktiv, bis eine Heldenphase ihre Regeln umsetzt. - **Geplante Helden** (Lucy, Ghaliga, Siegfried, Feena) stehen im Datenmodell, sind aber inaktiv, bis eine Heldenphase ihre Regeln umsetzt.
- **Assets:** Blender-Quellen und Generatoren sind die Quelle. Ein zentraler, reproduzierbarer Blender-Export erzeugt FBX für Unity; GLB nur, wo keine bessere Quelle existiert (U2). - **Assets:** Blender-Quellen und Generatoren sind die Quelle. Ein zentraler, reproduzierbarer Blender-Export erzeugt FBX für Unity; GLB nur, wo keine bessere Quelle existiert (U2A).
- **Prozedurale Welt gebacken (U2B):** Gelände und Vegetation der G1-Karte hängen an Godots Rauschen und Zufallsgenerator; ein Godot-Werkzeug backt die Welt aus dem Referenzgenerator, Unity baut daraus die Szene. U5 bekommt eine neue Weltquelle.
- **Koordinaten:** Unity = (−x, Höhe, y) der Kartenpunkte; FBX-Pipeline, Welt und Kamera nutzen dieselbe Umrechnung, das Bild entspricht Godots.
- **Host-Schleife (U2C):** fester Takt mit Akkumulator; die Bildrate bestimmt nur die Zahl der Schritte je Bild. Nachweis über Fingerabdrücke der Partie.
## Arbeitsweise ## Arbeitsweise
- Ein Zweig je Meilenstein: `luneraria/u1`, `luneraria/u1.5`, `luneraria/u2`, … Nach jedem Meilenstein kommen Tests, Bild, Leistung, bekannte Unterschiede und ein Commit. - Ein Zweig je Meilenstein: `luneraria/u1`, `luneraria/u1.5`, `luneraria/u2`, … Nach jedem Meilenstein kommen Tests, Bild, Leistung, bekannte Unterschiede und ein Commit.
- Werkzeuge: - Werkzeuge:
- `tools/run_core_tests.sh` (Kern mit .NET). - `tools/run_core_tests.sh` (Kern mit .NET).
- Unity Test Runner (EditMode) und `CoreBatch` im Batchmodus (Kern im Editor). - Unity Test Runner: EditMode (Kern, Integration, Bildraten-Logik) und PlayMode (Szene bei echten Bildraten, Vertical Function Test).
- `CoreBatch`, `LuneAssetVerifier`, `LuneWorldBuilder`, `LuneWorldCapture`, `LunePlayerBuild` im Batchmodus.
- `tools/blender/export_fbx.sh` (Assets), `tools/godot-golden/world_export.gd` (Weltbake).
- `tools/godot-golden/run_godot.sh` (Godot-Referenz). - `tools/godot-golden/run_godot.sh` (Godot-Referenz).
- `tools/dotnet/Luneraria.Core.Bench` und `CoreBatch.Benchmark` (Leistung). - `tools/dotnet/Luneraria.Core.Bench` und `CoreBatch.Benchmark` (Leistung).

View file

@ -7,7 +7,7 @@ Stand: 1. Oktober 2026.
| U2A | Blender→FBX-Pipeline, sechs repräsentative Assets vollständig geprüft | **fertig** | | U2A | Blender→FBX-Pipeline, sechs repräsentative Assets vollständig geprüft | **fertig** |
| U2B | Weltgeometrie (G1-Karte, Türme, Ancients, Grotte, Aufstellungsbereiche), Materialsystem | **fertig** | | U2B | Weltgeometrie (G1-Karte, Türme, Ancients, Grotte, Aufstellungsbereiche), Materialsystem | **fertig** |
| U2C | Simulation/Darstellung: fester Takt, Interpolation, Bildraten-Unabhängigkeit | **fertig** | | U2C | Simulation/Darstellung: fester Takt, Interpolation, Bildraten-Unabhängigkeit | **fertig** |
| U2D | spielbare Unity-Partie, Vertical Function Test | offen | | U2D | spielbare Unity-Partie, Vertical Function Test | **fertig** |
Voraussetzung U1.5 ist erfüllt ([U1.5-UNITY.md](U1.5-UNITY.md)). Voraussetzung U1.5 ist erfüllt ([U1.5-UNITY.md](U1.5-UNITY.md)).
@ -268,9 +268,101 @@ Im Batchmodus zeichnet Unity kein Bild auf einen Bildschirm; daher die sehr hohe
- **Weitere Vereinfachungen:** Turmblitze sind kurze Linien. Die Einsturzsequenz ist vereinfacht (Kippen und Absinken). Der schlafende Boss zwischen den Kämpfen und die Planungsflächen fehlen noch. - **Weitere Vereinfachungen:** Turmblitze sind kurze Linien. Die Einsturzsequenz ist vereinfacht (Kippen und Absinken). Der schlafende Boss zwischen den Kämpfen und die Planungsflächen fehlen noch.
- **Kamera:** wie `lune_camera.gd` (WASD, Mausrad, Q/E, R, O, mittlere Maustaste), vorerst mit dem klassischen Input Manager. - **Kamera:** wie `lune_camera.gd` (WASD, Mausrad, Q/E, R, O, mittlere Maustaste), vorerst mit dem klassischen Input Manager.
## U2D ## U2D · Spielbare Unity-Partie
Folgt in diesem Dokument. ### Szene und Steuerung
`Assets/Luneraria/Scenes/G1_Map.unity`, Play im Editor oder der macOS-Player: Eine Bot-Partie läuft (Standard: Seed 2) mit 3 Lanes, 2 Basen, 16 Türmen, 2 Ancients, Grotte und Aufstellungsbereichen auf dem echten C#-Kern.
| Teil | Funktion |
| --- | --- |
| Kamera | wie Godot: WASD/Pfeile, Mausrad, Q/E, R, O, mittlere Maustaste |
| `LuneDebugOverlay` | Runde, Phase, Türme, Ancients, Einheiten, Helden, Boss, Sieger. Das ist eine vorläufige Anzeige, kein HUD; das G3-HUD wird in U3 portiert, nicht neu erfunden |
| `LuneMatchDriver` | Seed, Startrunde, Fixture-Gold und Spieltempo im Inspector |
Menschliche Befehle (Kaufen, Aufstellen, Bereit) kommen mit dem HUD (U3) und dem Netzwerk (U4); bis dahin planen alle sechs Plätze als Bots über dieselbe Befehlsschnittstelle.
### Vertical Function Test
`VerticalFunctionTests` (PlayMode, Test Runner) spielt beide Partien sichtbar in der Szene. Das Spieltempo ist 6×, die Zielbildrate 60. Ein Kameraregisseur fotografiert die Schlüsselmomente; jede Partie muss den Fingerabdruck des reinen Kernlaufs ergeben.
| Partie | Verlauf | Ergebnis |
| --- | --- | --- |
| Seed 2, ab Runde 1 | siehe unten | Rot gewinnt („Ancient von Blau zerstört.“); 12.690 Schritte, Fingerabdruck `DCE95C96556F3A1A` = Kern |
| Seed 9, ab Runde 21 (Fixture wie das Golden-Szenario `high_wave_21`: Runde 21, je 4.000 Gold) | Wellen über 20 mit bis zu 125 Einheiten und 6 Helden; Runde 21: 3 Rot-Türme fallen; Runde 22: Rot-Turm, beide Rot-Basistürme, ein Blau-Turm und der Rot-Ancient fallen | Blau gewinnt („Ancient von Rot zerstört.“); 4.195 Schritte, `7B8E43AEFC0D60C5` = Kern |
Verlauf der Partie mit Seed 2:
- 9 Runden, bis zu 84 Einheiten.
- Runde 7: erster Turm fällt (`blau_bot_outer`).
- Runde 8: Rot gewinnt den Grottenboss; `rot_top_outer` und `rot_top_inner` fallen.
- Runde 9: Der Boss zieht für Rot über die Bot-Lane. Bis zu 4 Helden kämpfen. Blaus Bot-Innenturm, beide Basistürme und der Ancient fallen.
Kein Bot-Spiel erreicht von Runde 1 aus die Welle 20: Ein Durchlauf über 200 Partien (5 Profile × 40 Seeds) endete immer nach 8 bis 12 Runden. Deshalb zeigt die zweite Partie die hohen Wellen mit derselben öffentlichen Fixture wie die Golden-Referenz.
Bilder (`docs/port/screenshots/u2d/`):
| Bild | zeigt |
| --- | --- |
| `01-runde1-aufmarsch`, `02-runde1-mid-kampf` | Aufmarsch auf drei Lanes, erster Kampf in der Mitte mit Turmblitz |
| `03-turm-faellt`, `04-turm-ruine` | Einsturz eines Turms und seine Ruine (Kamera von der Lane aus) |
| `05-grotte-bosskampf` | Schlundkrone kämpft in der Grotte |
| `06-held-kaempft` | Helden im Kampf |
| `07-boss-auf-der-lane` | eingesetzter Boss mit Rots Armee auf der Deltabrücke |
| `08-ancient-faellt`, `09-ancient-ruine`, `10-ende-uebersicht` | Fall des Blau-Ancient, Ruine, Endstand |
| `11-welle21-uebersicht`, `12-welle21-kampf`, `13-ancient-rot-faellt`, `14-welle-ende` | Welle 21: Übersicht, Massenkampf, Fall des Rot-Ancient |
Protokoll: `logs/u2d/vertical-function-test.json`, mit Runden, Ereignissen, Bosslog und Bildliste.
### Leistung
**Player** (macOS, Mono, Release; Apple M5, Metal; 1920 × 1080 im Fenster, VSync aus): Seed 9 in Runde 21, Spieltempo 1×, gemessen ab 10 s Kampf mit `LuneBenchmark`.
| Ansicht | Einheiten | fps (Mittel) | Bildzeit Median | p95 | Hauptthread (Mittel/p95) | Render-Thread | Warten auf Present (Mittel/p95) |
| --- | ---: | ---: | ---: | ---: | --- | --- | --- |
| ganze Karte (Zoom 64) | 116 | 161–199 | 2,3–2,9 ms | 18–26 ms | 1,3 / 2,0 ms | 1,3 ms | 4,9 / 24 ms |
| Mid-Lane nah (Zoom 22) | 63 | 274–354 | 1,9–2,5 ms | 9–13 ms | 0,9 / 1,3 ms | 0,9 ms | 2,7 / 13 ms |
Die Spannen stammen aus vier Läufen.
Was die Zahlen zeigen:
- Je Bild kosten die Simulation 0,02–0,07 ms und die Ansichten 0,12–0,14 ms.
- Die Ausreißer sind Wartezeit beim Present. Nur 4–27 der langsamen Bilder fielen mit einem Simulationsschritt zusammen.
- Garbage Collection war nicht die Ursache. Die Debug-Anzeige erzeugte zwar Müll in jedem Bild; das ist behoben (78 statt 111 Sammlungen in 20 s), änderte die Ausreißer aber nicht.
- Metal liefert in Unity keine GPU-Zeitstempel. Ob GPU oder Fenster-Compositor wartet, bleibt deshalb offen. Kandidaten:
- die Platzhaltermodelle (80.000 Dreiecke je Einheit, über 9 Millionen Dreiecke in der Übersicht),
- vier Schattenkaskaden bis 190 m,
- der Fenstermodus.
Das gehört zu U5 (LOD, Dreiecksbudget, Vollbild).
Rohdaten: `logs/u2d/player-benchmark-macos.json`. Windows- und Linux-Player brauchen die fehlenden Build-Module ([U1.5-UNITY.md](U1.5-UNITY.md#fehlende-build-module)).
```bash
/Applications/Unity/Hub/Editor/6000.3.20f1/Unity.app/Contents/MacOS/Unity -batchmode -projectPath "$PWD" -executeMethod Luneraria.EditorTools.LunePlayerBuild.BuildMac -logFile -
```
```bash
Builds/macOS/Luneraria.app/Contents/MacOS/luneraria-unity -screen-width 1920 -screen-height 1080 -screen-fullscreen 0 -luneBenchmark "$PWD/tools/out/player-benchmark.json"
```
### Abnahme U2
| Vorgabe | Stand |
| --- | --- |
| Reproduzierbarer Blender→FBX-Export mit Prüfung von Einheiten, Maßstab, Achsen, Vorderseite, Root Bone, Bone-Namen, Mesh-Namen, Materialslots, Normalen, Tangenten, Clips; keine Handarbeit | erfüllt (U2A) |
| Sechs repräsentative Assets vollständig in Unity geprüft, vor dem Batchlauf | erfüllt (U2A); Batchlauf erst nach diesem Test, also jetzt freigegeben |
| Erste spielbare Szene mit echter Karte (3 Lanes, 2 Basen, 16 Türme, 2 Ancients, Grotte, Aufstellungsbereiche), an den echten Kern gebunden | erfüllt (U2B, U2D) |
| Szene besitzt keine Regeln; Zustand → Darstellung, nie umgekehrt | erfüllt (U2C) |
| Fester Takt, `Update()` entscheidet nie über den Kampf, Darstellung interpoliert | erfüllt (U2C) |
| Gleiches Ergebnis bei 30, 60, 120 fps und unbegrenzt | erfüllt, logisch und in der echten Szene (U2C) |
| Vertical Function Test: vollständige Bot-Partie sichtbar, 3 Lanes, Wellen > 20, Türme fallen, Helden kämpfen, Boss nutzbar, Ancient fällt, Partie endet, gleich der Kernpartie | erfüllt; Wellen > 20 über die Golden-Fixture (siehe oben) |
### Bekannte Grenzen
- **Platzhaltermodelle:** Alle Einheiten zeigen bis zum Batch-Import die repräsentativen Modelle (siehe U2C).
- **Debug-Anzeige statt HUD:** Das G3-HUD folgt in U3. Es gibt noch keine menschlichen Befehle und kein Planungsbild für einen Spieler.
- **Materialien vereinfacht** (siehe U2B); keine Partikeleffekte; Turmblitze sind einfache Linien.
- **Leistungsspitzen beim Present** offen (siehe oben).
## Abnahme (gesamt) ## Abnahme (gesamt)

View file

@ -0,0 +1,12 @@
{
"build": "OSXPlayer player, 6000.3.20f1, release",
"device": "Mac17,2; Apple M5; Apple M5",
"graphics_api": "Metal",
"resolution": "1920x1080",
"vsync": 0,
"match": "seed 9 from round 21 (fixture high_wave_21), speed 1x, measured after 10 s of combat",
"windows": [
{"view": "overview (zoom 64)", "frames": 3977, "fps": 198.8, "frame_ms_avg": 5.03, "frame_ms_p95": 17.81, "frame_ms_p99": 24.41, "simulation_ms_per_frame": 0.065, "views_ms_per_frame": 0.127, "units_alive_max": 116},
{"view": "mid lane close (zoom 22)", "frames": 3540, "fps": 354.0, "frame_ms_avg": 2.83, "frame_ms_p95": 8.87, "frame_ms_p99": 20.16, "simulation_ms_per_frame": 0.021, "views_ms_per_frame": 0.114, "units_alive_max": 63}
]
}

View file

@ -0,0 +1,12 @@
{
"build": "OSXPlayer player, 6000.3.20f1, release",
"device": "Mac17,2; Apple M5; Apple M5",
"graphics_api": "Metal",
"resolution": "1920x1080",
"vsync": 0,
"match": "seed 9 from round 21 (fixture high_wave_21), speed 1x, measured after 10 s of combat",
"windows": [
{"view": "overview (zoom 64)", "frames": 3586, "fps": 179.3, "frame_ms_avg": 5.58, "frame_ms_median": 2.40, "frame_ms_p95": 18.69, "frame_ms_p99": 27.09, "frame_ms_max": 40.68, "frames_over_16_7_ms": 277, "of_them_with_simulation_step": 23, "gc_collections": 111, "simulation_ms_per_frame": 0.068, "views_ms_per_frame": 0.137, "units_alive_max": 116},
{"view": "mid lane close (zoom 22)", "frames": 3212, "fps": 320.8, "frame_ms_avg": 3.12, "frame_ms_median": 1.99, "frame_ms_p95": 10.92, "frame_ms_p99": 19.79, "frame_ms_max": 25.43, "frames_over_16_7_ms": 84, "of_them_with_simulation_step": 5, "gc_collections": 56, "simulation_ms_per_frame": 0.022, "views_ms_per_frame": 0.124, "units_alive_max": 63}
]
}

View file

@ -0,0 +1,12 @@
{
"build": "OSXPlayer player, 6000.3.20f1, release",
"device": "Mac17,2; Apple M5; Apple M5",
"graphics_api": "Metal",
"resolution": "1920x1080",
"vsync": 0,
"match": "seed 9 from round 21 (fixture high_wave_21), speed 1x, measured after 10 s of combat",
"windows": [
{"view": "overview (zoom 64)", "frames": 3612, "fps": 180.6, "frame_ms_avg": 5.54, "frame_ms_median": 2.30, "frame_ms_p95": 19.70, "frame_ms_p99": 25.49, "frame_ms_max": 36.30, "frames_over_16_7_ms": 286, "of_them_with_simulation_step": 27, "gc_collections": 78, "simulation_ms_per_frame": 0.070, "views_ms_per_frame": 0.139, "units_alive_max": 116},
{"view": "mid lane close (zoom 22)", "frames": 3144, "fps": 314.4, "frame_ms_avg": 3.18, "frame_ms_median": 1.90, "frame_ms_p95": 13.25, "frame_ms_p99": 22.21, "frame_ms_max": 25.20, "frames_over_16_7_ms": 102, "of_them_with_simulation_step": 11, "gc_collections": 26, "simulation_ms_per_frame": 0.024, "views_ms_per_frame": 0.137, "units_alive_max": 63}
]
}

View file

@ -0,0 +1,14 @@
{
"build": "OSXPlayer player, 6000.3.20f1, release",
"device": "Mac17,2; Apple M5; Apple M5",
"graphics_api": "Metal",
"resolution": "1920x1080",
"vsync": 0,
"match": "seed 9 from round 21 (fixture high_wave_21), speed 1x, measured after 10 s of combat",
"windows": [
{"view": "overview (zoom 64)", "frames": 3215, "fps": 160.7, "frame_ms_avg": 6.22, "frame_ms_median": 2.89, "frame_ms_p95": 25.54, "frame_ms_p99": 43.79, "frame_ms_max": 73.93, "frames_over_16_7_ms": 304, "of_them_with_simulation_step": 24, "gc_collections": 77, "simulation_ms_per_frame": 0.072, "views_ms_per_frame": 0.125, "units_alive_max": 116},
// timings overview (zoom 64): main thread avg 1.33 / median 1.22 / p95 2.00 ms, render thread avg 1.26 / median 1.20 / p95 1.61 ms, gpu avg 0.01 / median 0.00 / p95 0.00 ms, present wait avg 4.89 / median 1.56 / p95 23.96 ms
{"view": "mid lane close (zoom 22)", "frames": 2744, "fps": 274.4, "frame_ms_avg": 3.64, "frame_ms_median": 2.46, "frame_ms_p95": 13.47, "frame_ms_p99": 25.11, "frame_ms_max": 41.03, "frames_over_16_7_ms": 96, "of_them_with_simulation_step": 4, "gc_collections": 26, "simulation_ms_per_frame": 0.027, "views_ms_per_frame": 0.119, "units_alive_max": 63}
// timings mid lane close (zoom 22): main thread avg 0.93 / median 0.89 / p95 1.26 ms, render thread avg 0.92 / median 0.90 / p95 1.13 ms, gpu avg 0.00 / median 0.00 / p95 0.00 ms, present wait avg 2.72 / median 1.55 / p95 12.57 ms
]
}

View file

@ -0,0 +1,16 @@
{
"runs": [
{"label": "match-seed2", "seed": 2, "start_round": 1, "extra_gold": 0, "speed": 6, "winner": 1, "end_note": "Ancient von Blau zerstört.", "steps": 12690, "hash": "DCE95C96556F3A1A", "core_hash": "DCE95C96556F3A1A", "same_as_core": true,
"units_alive_max": 84, "heroes_alive_max": 4, "boss_log": ["round 8: won team 1 ", "round 9: deployed team 1 bot"],
"rounds": [{"round": 1, "combat_seconds": 44.9, "lost": [], "boss": "none", "end": "cleared"}, {"round": 2, "combat_seconds": 57.1, "lost": [], "boss": "none", "end": "cleared"}, {"round": 3, "combat_seconds": 54.1, "lost": [], "boss": "none", "end": "cleared"}, {"round": 4, "combat_seconds": 62.9, "lost": [], "boss": "none", "end": "cleared"}, {"round": 5, "combat_seconds": 60.7, "lost": [], "boss": "none", "end": "cleared"}, {"round": 6, "combat_seconds": 62.1, "lost": [], "boss": "none", "end": "cleared"}, {"round": 7, "combat_seconds": 66.5, "lost": ["blau_bot_outer"], "boss": "none", "end": "cleared"}, {"round": 8, "combat_seconds": 89.65, "lost": ["rot_top_outer", "rot_top_inner"], "boss": "won", "end": "cleared"}, {"round": 9, "combat_seconds": 136.55, "lost": ["blau_bot_inner", "blau_base_2", "blau_base_1", "blau_ancient"], "boss": "deployed", "end": "core"}],
"events": ["round 7: blau_bot_outer destroyed", "round 8: rot_top_outer destroyed", "round 8: rot_top_inner destroyed", "round 8: grotto boss won", "round 9: blau_bot_inner destroyed", "round 9: blau_base_2 destroyed", "round 9: blau_base_1 destroyed", "round 9: blau_ancient destroyed", "round 9: grotto boss deployed"],
"shots": ["01-runde1-aufmarsch", "02-runde1-mid-kampf", "03-turm-faellt", "04-turm-ruine", "05-grotte-bosskampf", "06-held-kaempft", "07-boss-auf-der-lane", "08-ancient-faellt", "09-ancient-ruine", "10-ende-uebersicht"],
"performance": null},
{"label": "match-seed9-round21", "seed": 9, "start_round": 21, "extra_gold": 4000, "speed": 6, "winner": 0, "end_note": "Ancient von Rot zerstört.", "steps": 4195, "hash": "7B8E43AEFC0D60C5", "core_hash": "7B8E43AEFC0D60C5", "same_as_core": true,
"units_alive_max": 125, "heroes_alive_max": 6, "boss_log": [],
"rounds": [{"round": 21, "combat_seconds": 121.8, "lost": ["rot_mid_outer", "rot_top_outer", "rot_mid_inner"], "boss": "none", "end": "cleared"}, {"round": 22, "combat_seconds": 87.95, "lost": ["rot_top_inner", "rot_base_1", "blau_bot_outer", "rot_base_2", "rot_ancient"], "boss": "none", "end": "core"}],
"events": ["round 21: rot_mid_outer destroyed", "round 21: rot_top_outer destroyed", "round 21: rot_mid_inner destroyed", "round 22: rot_top_inner destroyed", "round 22: rot_base_1 destroyed", "round 22: blau_bot_outer destroyed", "round 22: rot_base_2 destroyed", "round 22: rot_ancient destroyed"],
"shots": ["11-welle21-uebersicht", "12-welle21-kampf", "13-ancient-rot-faellt", "14-welle-ende"],
"performance": {"window": "20 s real time at 1x speed, unlimited frame rate, round 21", "frames": 64198, "fps": 3209.869, "frame_ms_avg": 0.312, "frame_ms_p95": 0.316, "simulation_ms_per_frame": 0.004, "views_ms_per_frame": 0.116, "units_alive_max": 116}}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB