convert game playing..
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using BumpCombat.Core;
|
||||
using BumpCombat.Player;
|
||||
using BumpCombat.Progression;
|
||||
using BumpCombat.Stage;
|
||||
using BumpCombat.UI;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
|
||||
[InitializeOnLoad]
|
||||
internal static class AgyPlayValidationHarness
|
||||
{
|
||||
// A validation run starts only when run.marker explicitly contains "run".
|
||||
private const string Out = "D:/project/game/output/agy-play-validation-20260920/";
|
||||
private const string RunMarker = Out + "run.marker";
|
||||
|
||||
static AgyPlayValidationHarness()
|
||||
{
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
if (IsRunRequested() && !EditorApplication.isPlayingOrWillChangePlaymode)
|
||||
EditorApplication.EnterPlaymode();
|
||||
};
|
||||
EditorApplication.playModeStateChanged += s =>
|
||||
{
|
||||
if (s == PlayModeStateChange.EnteredPlayMode && IsRunRequested())
|
||||
new GameObject("AgyPlayValidationRunner") { hideFlags = HideFlags.HideAndDontSave }
|
||||
.AddComponent<AgyPlayValidationRunner>();
|
||||
};
|
||||
}
|
||||
|
||||
private static bool IsRunRequested()
|
||||
{
|
||||
return File.Exists(RunMarker)
|
||||
&& string.Equals(File.ReadAllText(RunMarker).Trim(), "run", StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class AgyPlayValidationRunner : MonoBehaviour
|
||||
{
|
||||
private const string Out = "D:/project/game/output/agy-play-validation-20260920/";
|
||||
|
||||
private IEnumerator Start()
|
||||
{
|
||||
yield return new WaitForSecondsRealtime(0.5f);
|
||||
RunManager run = RunManager.Instance;
|
||||
if (run == null) { Fail("RunManager missing"); yield break; }
|
||||
if (run.IsTitleScreen) run.BeginRun(RunMode.Development);
|
||||
yield return ResolveBlockingSelections(run);
|
||||
if (run.IsSelectionOpen || Time.timeScale <= 0f)
|
||||
{
|
||||
Fail($"Startup selection did not resolve. selectionOpen={run.IsSelectionOpen}, timeScale={Time.timeScale:R}");
|
||||
yield break;
|
||||
}
|
||||
|
||||
StageManager stage = StageManager.Instance;
|
||||
PlayerController player = FindAnyObjectByType<PlayerController>();
|
||||
WorldMapPanel map = FindAnyObjectByType<WorldMapPanel>();
|
||||
if (stage == null || stage.CurrentZone == null || player == null || map == null)
|
||||
{ Fail("Stage/player/map initialization failed"); yield break; }
|
||||
|
||||
Capture("01-start");
|
||||
Vector2 before = player.transform.position;
|
||||
yield return Hold(Key.RightArrow, 0.5f);
|
||||
Vector2 after = player.transform.position;
|
||||
float movementDistance = Vector2.Distance(before, after);
|
||||
File.WriteAllText(Out + "movement.txt", $"before={before:R}\nafter={after:R}\ndistance={movementDistance:R}\n");
|
||||
Capture("02-moved");
|
||||
if (movementDistance <= 0.01f)
|
||||
{
|
||||
Fail($"Player did not move. distance={movementDistance:R}");
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return Press(Key.M);
|
||||
File.WriteAllText(Out + "map.txt", $"open={map.IsOpen}\ntimeScale={Time.timeScale:R}\n");
|
||||
Capture("03-world-map-open");
|
||||
if (!map.IsOpen || Time.timeScale > 0f)
|
||||
{
|
||||
Fail($"World map did not open correctly. open={map.IsOpen}, timeScale={Time.timeScale:R}");
|
||||
yield break;
|
||||
}
|
||||
yield return Press(Key.M);
|
||||
if (map.IsOpen || Time.timeScale <= 0f)
|
||||
{
|
||||
Fail($"World map did not close correctly. open={map.IsOpen}, timeScale={Time.timeScale:R}");
|
||||
yield break;
|
||||
}
|
||||
|
||||
int target = -1; StageDirection exit = StageDirection.North;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
int candidate = stage.CurrentZone.NodeData.GetConnection((StageDirection)i);
|
||||
if (candidate >= 0) { target = candidate; exit = (StageDirection)i; break; }
|
||||
}
|
||||
if (target < 0) { Fail("No connected zone"); yield break; }
|
||||
int oldZone = stage.CurrentZone.NodeId;
|
||||
stage.RequestZoneTransition(target, exit.Opposite());
|
||||
yield return null;
|
||||
File.WriteAllText(Out + "transition-active.txt", $"transitioning={stage.IsTransitioning}\nlocked={player.IsTransitionLocked}\n");
|
||||
Capture("04-transition-active");
|
||||
float deadline = Time.realtimeSinceStartup + 4f;
|
||||
while (stage.IsTransitioning && Time.realtimeSinceStartup < deadline) yield return null;
|
||||
ArenaBounds bounds = ArenaBounds.Resolve();
|
||||
File.WriteAllText(Out + "transition.txt",
|
||||
$"oldZone={oldZone}\nnewZone={stage.CurrentZone?.NodeId ?? -1}\ntarget={target}\n" +
|
||||
$"transitioning={stage.IsTransitioning}\nlocked={player.IsTransitionLocked}\n" +
|
||||
$"boundsCenter={bounds?.ZoneCenter.ToString("R")}\nzoneCenter={stage.CurrentZone?.ZoneCenter.ToString("R")}\n");
|
||||
Capture("05-transition-complete");
|
||||
if (stage.IsTransitioning
|
||||
|| player.IsTransitionLocked
|
||||
|| stage.CurrentZone == null
|
||||
|| stage.CurrentZone.NodeId != target)
|
||||
{
|
||||
Fail($"Zone transition failed. oldZone={oldZone}, target={target}, newZone={stage.CurrentZone?.NodeId ?? -1}, transitioning={stage.IsTransitioning}, locked={player.IsTransitionLocked}");
|
||||
yield break;
|
||||
}
|
||||
|
||||
run.EndRunAsClear();
|
||||
yield return null;
|
||||
RunClearPanel clear = FindAnyObjectByType<RunClearPanel>();
|
||||
File.WriteAllText(Out + "clear.txt", $"runCleared={run.IsRunCleared}\ntimeScale={Time.timeScale:R}\npanel={(clear != null)}\n");
|
||||
Capture("06-run-clear");
|
||||
if (!run.IsRunCleared || Time.timeScale > 0f || clear == null)
|
||||
{
|
||||
Fail($"Run clear validation failed. runCleared={run.IsRunCleared}, timeScale={Time.timeScale:R}, panel={(clear != null)}");
|
||||
yield break;
|
||||
}
|
||||
File.WriteAllText(Out + "completed.marker", DateTime.Now.ToString("O"));
|
||||
DeleteRunMarker();
|
||||
EditorApplication.ExitPlaymode();
|
||||
}
|
||||
|
||||
private static IEnumerator ResolveBlockingSelections(RunManager run)
|
||||
{
|
||||
ArtifactRewardController rewards = FindAnyObjectByType<ArtifactRewardController>();
|
||||
RunTutorialController tutorial = FindAnyObjectByType<RunTutorialController>();
|
||||
float deadline = Time.realtimeSinceStartup + 6f;
|
||||
|
||||
while (Time.realtimeSinceStartup < deadline)
|
||||
{
|
||||
bool handled = false;
|
||||
|
||||
if (rewards != null && rewards.DebugIsSelectionVisible)
|
||||
{
|
||||
if (rewards.DebugShownCount <= 0 || !rewards.DebugChooseOption(0))
|
||||
{
|
||||
Fail("Could not resolve startup artifact selection.");
|
||||
yield break;
|
||||
}
|
||||
|
||||
handled = true;
|
||||
}
|
||||
else if (tutorial != null && tutorial.DebugIsVisible)
|
||||
{
|
||||
if (!tutorial.DebugContinue())
|
||||
{
|
||||
Fail("Could not dismiss startup tutorial.");
|
||||
yield break;
|
||||
}
|
||||
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (!handled && !run.IsSelectionOpen && Time.timeScale > 0f)
|
||||
{
|
||||
yield return null;
|
||||
if (!run.IsSelectionOpen && Time.timeScale > 0f)
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerator Hold(Key key, float seconds)
|
||||
{
|
||||
if (Keyboard.current == null) yield break;
|
||||
InputSystem.QueueStateEvent(Keyboard.current, new KeyboardState(key));
|
||||
float end = Time.realtimeSinceStartup + seconds;
|
||||
while (Time.realtimeSinceStartup < end) yield return null;
|
||||
InputSystem.QueueStateEvent(Keyboard.current, new KeyboardState());
|
||||
yield return null;
|
||||
}
|
||||
|
||||
private static IEnumerator Press(Key key)
|
||||
{
|
||||
if (Keyboard.current == null) yield break;
|
||||
InputSystem.QueueStateEvent(Keyboard.current, new KeyboardState(key));
|
||||
yield return null;
|
||||
InputSystem.QueueStateEvent(Keyboard.current, new KeyboardState());
|
||||
yield return null;
|
||||
}
|
||||
|
||||
private static void Capture(string name)
|
||||
{
|
||||
Directory.CreateDirectory(Out);
|
||||
Camera camera = Camera.main;
|
||||
if (camera == null) return;
|
||||
RenderTexture previous = camera.targetTexture;
|
||||
RenderTexture active = RenderTexture.active;
|
||||
var target = new RenderTexture(1280, 720, 24);
|
||||
var texture = new Texture2D(1280, 720, TextureFormat.RGB24, false);
|
||||
try
|
||||
{
|
||||
camera.targetTexture = target; camera.Render(); RenderTexture.active = target;
|
||||
texture.ReadPixels(new Rect(0, 0, 1280, 720), 0, 0); texture.Apply();
|
||||
File.WriteAllBytes(Out + name + ".png", texture.EncodeToPNG());
|
||||
}
|
||||
finally
|
||||
{
|
||||
camera.targetTexture = previous; RenderTexture.active = active;
|
||||
DestroyImmediate(texture); target.Release(); DestroyImmediate(target);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Fail(string message)
|
||||
{
|
||||
Directory.CreateDirectory(Out);
|
||||
File.WriteAllText(Out + "validation-error.txt", message);
|
||||
DeleteRunMarker();
|
||||
EditorApplication.ExitPlaymode();
|
||||
}
|
||||
|
||||
private static void DeleteRunMarker()
|
||||
{
|
||||
string marker = Out + "run.marker";
|
||||
if (File.Exists(marker))
|
||||
{
|
||||
File.Delete(marker);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ec05c0337f620543ba3d30114ee07fe
|
||||
@@ -1271,7 +1271,7 @@ namespace BumpCombat.Editor
|
||||
body.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
|
||||
|
||||
CircleCollider2D collider = player.AddComponent<CircleCollider2D>();
|
||||
collider.offset = new Vector2(0f, -0.28f);
|
||||
collider.offset = new Vector2(0f, -0.06f);
|
||||
collider.radius = 0.22f;
|
||||
|
||||
player.AddComponent<PlayerStats>();
|
||||
@@ -1394,10 +1394,7 @@ namespace BumpCombat.Editor
|
||||
radius,
|
||||
experience,
|
||||
animationLeadTime);
|
||||
definition.ConfigureRole(
|
||||
kind == EnemyKind.Slime || kind == EnemyKind.Skeleton
|
||||
? EnemyRole.Crowd
|
||||
: EnemyRole.Normal);
|
||||
definition.ConfigureRole(EnemyRole.Normal);
|
||||
AssetDatabase.CreateAsset(definition, path);
|
||||
return definition;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user