using System.Collections; using System.Linq; using BumpCombat.Constants; using BumpCombat.Defense; using BumpCombat.Defense.Comparison; using NUnit.Framework; using UnityEditor; using UnityEngine; using UnityEngine.TestTools; using Object = UnityEngine.Object; namespace BumpCombat.Tests { public sealed class Defense25DComparisonHudLayoutPlayModeTests { private Defense25DComparison world; private Defense25DComparisonSettings settings; private Camera projectionCamera; private const string SettingsPath = "Assets/_Project/Constants/Defense25DComparison/ComparisonSettings.asset"; [UnitySetUp] public IEnumerator SetUp() { settings = Object.Instantiate(AssetDatabase.LoadAssetAtPath(SettingsPath)); Assert.That(settings, Is.Not.Null); settings.Enemy = AssetDatabase.LoadAssetAtPath( "Assets/_Project/Constants/Enemies/Slime.asset"); world = new GameObject("HUD geometry world").AddComponent(); world.Initialize(settings); GameObject cameraObject = new("HUD pad projection camera"); projectionCamera = cameraObject.AddComponent(); projectionCamera.orthographic = true; projectionCamera.orthographicSize = settings.CameraSize; projectionCamera.aspect = (float)Defense25DView.Width / Defense25DView.Height; Rect viewport = Defense25DView.GetViewport(Screen.width, Screen.height); projectionCamera.rect = new Rect(viewport.x / Screen.width, viewport.y / Screen.height, viewport.width / Screen.width, viewport.height / Screen.height); projectionCamera.transform.SetPositionAndRotation( new Vector3(0f, 13f, -11f), Quaternion.Euler(settings.CameraPitch, 0f, 0f)); yield return null; } [UnityTearDown] public IEnumerator TearDown() { if (world != null) Object.Destroy(world.gameObject); if (projectionCamera != null) Object.Destroy(projectionCamera.gameObject); if (settings != null) Object.Destroy(settings); yield return null; } [UnityTest] public IEnumerator PersistentHudControlsLeaveAllSixProjectedPadsClearAndConsumeOnlyTheirDrawnRegions() { Rect viewport = Defense25DView.GetViewport(Screen.width, Screen.height); float scale = viewport.width / Defense25DView.Width; Rect[] controls = world.GetPersistentDefenseUiHitRects(); Assert.That(controls, Is.Not.Empty); for (int pad = 0; pad < 6; pad++) { Vector2 ground = GameplayConstants.Current.Defense.GetBuildPadPosition(pad); Vector3 screen = projectionCamera.WorldToScreenPoint( new Vector3(ground.x, settings.GroundY, ground.y)); Vector2 gui = new((screen.x - viewport.x) / scale, (Screen.height - screen.y - viewport.y) / scale); Rect padFootprint = new(gui.x - 14f, gui.y - 14f, 28f, 28f); Assert.That(controls.Any(control => control.Overlaps(padFootprint)), Is.False, $"Persistent HUD control covers projected pad {pad + 1} at {gui}."); Assert.That(world.IsPointerOverDefenseUI(ScreenPoint(viewport, scale, gui)), Is.False, $"A click on projected pad {pad + 1} is incorrectly captured by the HUD."); } foreach (Rect control in controls) { Vector2 center = control.center; Assert.That(world.IsPointerOverDefenseUI(ScreenPoint(viewport, scale, center)), Is.True, $"Drawn HUD control at {center} is not protected from combat input."); } Rect noDetails = world.GetSelectedPadDetailsRect(); Assert.That(noDetails.width <= 0f || noDetails.height <= 0f, Is.True, "Detailed construction controls should not occupy arena space before a pad is selected."); Assert.That(world.SelectBuildPad(0), Is.True); Rect details = world.GetSelectedPadDetailsRect(); Assert.That(details.width, Is.GreaterThan(0f)); Assert.That(details.height, Is.GreaterThan(0f)); Assert.That(world.IsPointerOverDefenseUI(ScreenPoint(viewport, scale, details.center)), Is.True, "The contextual detail panel's click area must match its drawn panel."); yield return null; } private static Vector2 ScreenPoint(Rect viewport, float scale, Vector2 gui) => new(viewport.x + gui.x * scale, Screen.height - (viewport.y + gui.y * scale)); } }