VanguardReMaster/client/Assets/Vanguard/Tests/Editor/ClientSmokeTests.cs

736 lines
30 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.Collections.Generic;
using System.Linq;
using System.Reflection;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using Vanguard.Api;
using Vanguard.Api.Contracts;
using Vanguard.App;
using Vanguard.Auth;
using Vanguard.Board3D;
using Vanguard.Meta;
using Vanguard.Shared.Protocol;
using Vanguard.State;
using Vanguard.UI;
using BattleView = Vanguard.UI.BattleView;
namespace Vanguard.Tests
{
public sealed class ClientSmokeTests
{
[Test]
public void Client_Views_Build()
{
var config = new AppConfig();
config.Load();
var tokens = new TokenStore();
var api = new ApiClient(config, tokens);
var player = new PlayerState();
var auth = new AuthService(api, tokens, player);
var meta = new MetaService(api, player);
var canvas = UiKit.CreateCanvas("Test Canvas");
var login = new LoginView(canvas.transform, config, auth, () => { });
login.Show("smoke");
var lobby = new MetaView(
canvas.transform, auth, player, () => { },
() => { }, () => { }, () => { }, () => { }, () => { });
var onboarding = new OnboardingView(
canvas.transform, auth, meta, () => { }, () => { });
var decks = new DeckBuilderView(
canvas.transform, meta, auth, () => { }, _ => { }, _ => { });
var queue = new QueueView(
canvas.transform, meta, auth, () => { }, _ => { });
var store = new StoreView(
canvas.transform, meta, auth, player, () => { });
var history = new HistoryView(
canvas.transform, meta, auth, () => { });
var battle = new BattleView(
canvas.transform, meta, () => { });
Assert.IsNotNull(GameObject.Find("LoginScreen"));
Assert.IsTrue(Exists("MetaScreen"));
Assert.IsTrue(Exists("OnboardingScreen"));
Assert.IsTrue(Exists("DeckBuilderScreen"));
Assert.IsTrue(Exists("StoreScreen"));
Assert.IsTrue(Exists("HistoryScreen"));
Assert.IsTrue(Exists("ReplayScreen"));
Assert.IsTrue(Exists("QueueScreen"));
Assert.IsTrue(Exists("BattleScreen"));
Assert.IsNotNull(
Resources.Load<Texture2D>("CardArt/td01_005"),
"TD01 card art is not imported");
var standardBack = Resources.Load<Texture2D>("CardArt/vanguard_back");
Assert.IsNotNull(standardBack, "Standard Vanguard card back is not imported");
Assert.AreEqual(standardBack, CardArt.Back.texture);
var artGo = new GameObject("ArtProbe", typeof(RectTransform), typeof(Image));
var artImage = artGo.GetComponent<Image>();
CardImageService.Apply(artImage, "td01_005", null);
Assert.IsNotNull(artImage.sprite, "CardImageService did not load real card art");
Object.DestroyImmediate(artGo);
// После смены сцены старые views не должны реагировать на PlayerState.
lobby.Dispose();
store.Dispose();
player.Apply(new MeResponse
{
UserName = "after-dispose",
Gold = 10,
Dust = 2,
Tokens = 1,
});
Cleanup();
}
[Test]
public void Localization_Translates_Static_And_Dynamic_Strings()
{
Loc.SetLanguage(UiLanguage.Ru);
Assert.AreEqual("Коллекция", Loc.Translate("Коллекция"));
Loc.SetLanguage(UiLanguage.En);
Assert.AreEqual("Collection", Loc.Translate("Коллекция"));
Assert.AreEqual("Grade: all", Loc.Translate("Ранг: все"));
Assert.AreEqual("Cards in collection: 12", Loc.Translate("Карт в коллекции: 12"));
Assert.AreEqual("Victory!", Loc.Translate("Победа!"));
Assert.AreEqual("Sentinel: yes · Boost: no", Loc.Translate("Sentinel: да · Boost: нет"));
Loc.SetLanguage(UiLanguage.Ru);
var go = new GameObject("LocalizedLabel", typeof(RectTransform), typeof(Text));
var label = go.GetComponent<Text>();
var localized = go.AddComponent<LocalizedText>();
localized.Initialize("Коллекция");
Assert.AreEqual("Коллекция", label.text);
Loc.SetLanguage(UiLanguage.En);
Assert.AreEqual("Collection", label.text);
label.text = "Магазин";
localized.ApplyNow();
Assert.AreEqual("Store", label.text);
Loc.SetLanguage(UiLanguage.Ru);
Assert.AreEqual("Магазин", label.text);
Object.DestroyImmediate(go);
}
[Test]
public void Canvas_Reference_Resolution_Is_1920x1080()
{
var canvas = UiKit.CreateCanvas("Reference Resolution Canvas");
try
{
var scaler = canvas.GetComponent<CanvasScaler>();
Assert.IsNotNull(scaler);
Assert.AreEqual(1920f, scaler.referenceResolution.x);
Assert.AreEqual(1080f, scaler.referenceResolution.y);
}
finally
{
Object.DestroyImmediate(canvas.gameObject);
var eventSystem = Object.FindAnyObjectByType<EventSystem>();
if (eventSystem != null)
Object.DestroyImmediate(eventSystem.gameObject);
}
}
[Test]
public void Battle_Layout_Fits_Reference_Resolution()
{
var config = new AppConfig();
config.Load();
var tokens = new TokenStore();
var api = new ApiClient(config, tokens);
var player = new PlayerState();
var auth = new AuthService(api, tokens, player);
var meta = new MetaService(api, player);
var canvas = UiKit.CreateCanvas("Layout Test Canvas");
try
{
var battle = new BattleView(canvas.transform, meta, () => { });
var screen = Resources.FindObjectsOfTypeAll<GameObject>()
.First(go => go.name == "BattleScreen");
screen.SetActive(true);
var self = ReferenceRect(FindRect("ZoneBlocksSelf"));
var opponent = ReferenceRect(FindRect("ZoneBlocksOpponent"));
var phase = ReferenceRect(FindRect("PhasePanel"));
var stats = ReferenceRect(FindRect("CombatStats"));
AssertWithinScreen(self, "self zones");
AssertWithinScreen(opponent, "opponent zones");
AssertWithinScreen(phase, "phase panel");
AssertWithinScreen(stats, "combat stats");
Assert.IsFalse(self.Overlaps(opponent));
Assert.IsFalse(self.Overlaps(phase));
Assert.IsFalse(opponent.Overlaps(phase));
Assert.IsFalse(opponent.Overlaps(stats));
Assert.IsFalse(phase.Overlaps(stats));
var toggle = Resources.FindObjectsOfTypeAll<Button>()
.First(button => button.name == "ToggleIntentPanel"
&& button.gameObject.scene.IsValid());
toggle.onClick.Invoke();
var movedOpponent = ReferenceRect(FindRect("ZoneBlocksOpponent"));
var movedPhase = ReferenceRect(FindRect("PhasePanel"));
Assert.Greater(movedOpponent.xMin, opponent.xMin);
Assert.Greater(movedPhase.xMin, phase.xMin);
AssertWithinScreen(movedOpponent, "opponent zones after hide");
AssertWithinScreen(movedPhase, "phase panel after hide");
}
finally
{
Object.DestroyImmediate(canvas.gameObject);
Cleanup();
}
}
[Test]
public void Mulligan_Finish_Button_Is_Hidden_After_Exchange()
{
var config = new AppConfig();
config.Load();
var tokens = new TokenStore();
var api = new ApiClient(config, tokens);
var player = new PlayerState();
var auth = new AuthService(api, tokens, player);
var meta = new MetaService(api, player);
var canvas = UiKit.CreateCanvas("Mulligan Test Canvas");
try
{
var battle = new BattleView(canvas.transform, meta, () => { });
var match = new MatchView
{
You = 0,
ActivePlayer = 0,
Phase = GamePhase.Mulligan,
LegalIntents = new List<IntentPayload>
{
new FinishMulliganIntent(),
new ConcedeIntent(),
},
};
var viewField = typeof(BattleView)
.GetField("_view", BindingFlags.Instance | BindingFlags.NonPublic)!;
var usedField = typeof(BattleView)
.GetField("_mulliganUsed", BindingFlags.Instance | BindingFlags.NonPublic)!;
var rebuild = typeof(BattleView)
.GetMethod("RebuildPhaseControls", BindingFlags.Instance | BindingFlags.NonPublic)!;
viewField.SetValue(battle, match);
usedField.SetValue(battle, true);
rebuild.Invoke(battle, new object[] { match });
Assert.IsFalse(
FindRect("PhasePanel").gameObject.activeSelf,
"Phase panel must disappear after the mulligan exchange");
Assert.IsFalse(
Resources.FindObjectsOfTypeAll<GameObject>()
.Any(go => go.name == "Phase_FinishMulliganIntent"),
"Finish mulligan button must not remain after the exchange");
usedField.SetValue(battle, false);
rebuild.Invoke(battle, new object[] { match });
Assert.IsTrue(
FindRect("PhasePanel").gameObject.activeSelf,
"Finish mulligan button must be available before the exchange");
Assert.IsTrue(
Resources.FindObjectsOfTypeAll<GameObject>()
.Any(go => go.name == "Phase_FinishMulliganIntent"));
}
finally
{
Object.DestroyImmediate(canvas.gameObject);
Cleanup();
}
}
[Test]
public void Optional_Ability_Shows_Overlay_Instead_Of_Action_Panel()
{
var config = new AppConfig();
config.Load();
var tokens = new TokenStore();
var api = new ApiClient(config, tokens);
var player = new PlayerState();
var auth = new AuthService(api, tokens, player);
var meta = new MetaService(api, player);
var canvas = UiKit.CreateCanvas("Ability Test Canvas");
try
{
var battle = new BattleView(canvas.transform, meta, () => { });
var match = new MatchView
{
You = 0,
ActivePlayer = 0,
Ability = new AbilityView
{
Player = 0,
InstanceId = 42,
PrintingId = "BT01/003EN",
Optional = true,
},
LegalIntents = new List<IntentPayload>
{
new ResolveAbilityIntent { Accept = true },
new ResolveAbilityIntent { Accept = false },
new ConcedeIntent(),
},
};
typeof(BattleView)
.GetField("_view", BindingFlags.Instance | BindingFlags.NonPublic)!
.SetValue(battle, match);
typeof(BattleView)
.GetMethod("SyncAbilityPrompt", BindingFlags.Instance | BindingFlags.NonPublic)!
.Invoke(battle, null);
var overlay = Resources.FindObjectsOfTypeAll<GameObject>()
.First(go => go.name == "AbilityOverlay");
Assert.IsTrue(overlay.activeSelf);
Assert.IsTrue(Resources.FindObjectsOfTypeAll<GameObject>()
.Any(go => go.name == "Ability_Активировать"));
Assert.IsTrue(Resources.FindObjectsOfTypeAll<GameObject>()
.Any(go => go.name == "Ability_Пропустить"));
typeof(BattleView)
.GetMethod("RebuildIntents", BindingFlags.Instance | BindingFlags.NonPublic)!
.Invoke(battle, new object[] { match });
Assert.IsFalse(Resources.FindObjectsOfTypeAll<GameObject>()
.Any(go => go.name == "Intent_ResolveAbilityIntent"));
typeof(BattleView)
.GetMethod("HideAbilityOverlay", BindingFlags.Instance | BindingFlags.NonPublic)!
.Invoke(battle, null);
Assert.IsFalse(overlay.activeSelf);
}
finally
{
Object.DestroyImmediate(canvas.gameObject);
Cleanup();
}
}
[Test]
public void Rear_Guard_Choice_Is_Shown_Without_Buttons()
{
var config = new AppConfig();
config.Load();
var tokens = new TokenStore();
var api = new ApiClient(config, tokens);
var player = new PlayerState();
var auth = new AuthService(api, tokens, player);
var meta = new MetaService(api, player);
var canvas = UiKit.CreateCanvas("Choice Test Canvas");
try
{
var battle = new BattleView(canvas.transform, meta, () => { });
var match = new MatchView
{
You = 0,
ActivePlayer = 0,
Choice = new ChoiceView
{
Player = 0,
Prompt = "Выберите круг для призыва Barcgal",
Options = new List<ChoiceOptionView>
{
new() { Slot = 0, Label = "Круг 1" },
new() { Slot = 1, Label = "Круг 2" },
new() { Slot = 2, Label = "Круг 3" },
new() { Slot = 3, Label = "Круг 4" },
new() { Slot = 4, Label = "Круг 5" },
},
},
LegalIntents = new List<IntentPayload>(),
};
typeof(BattleView)
.GetField("_view", BindingFlags.Instance | BindingFlags.NonPublic)!
.SetValue(battle, match);
typeof(BattleView)
.GetMethod("RefreshActionPrompt", BindingFlags.Instance | BindingFlags.NonPublic)!
.Invoke(battle, null);
var prompt = Resources.FindObjectsOfTypeAll<GameObject>()
.First(go => go.name == "ActionPrompt");
Assert.IsTrue(prompt.activeSelf);
var title = Resources.FindObjectsOfTypeAll<Text>()
.First(text => text.name == "ActionPromptTitle");
Assert.AreEqual("ВЫБОР КРУГА", title.text);
Assert.IsFalse(Resources.FindObjectsOfTypeAll<GameObject>()
.Any(go => go.name == "ChoiceOverlay"));
Assert.IsFalse(Resources.FindObjectsOfTypeAll<GameObject>()
.Any(go => go.name == "Choice_3"));
}
finally
{
Object.DestroyImmediate(canvas.gameObject);
Cleanup();
}
}
[Test]
public void Card_Choice_Shows_Overlay_With_Card_Tiles()
{
var config = new AppConfig();
config.Load();
var tokens = new TokenStore();
var api = new ApiClient(config, tokens);
var player = new PlayerState();
var auth = new AuthService(api, tokens, player);
var meta = new MetaService(api, player);
var canvas = UiKit.CreateCanvas("Card Choice Test Canvas");
try
{
var battle = new BattleView(canvas.transform, meta, () => { });
var match = new MatchView
{
You = 0,
ActivePlayer = 0,
Choice = new ChoiceView
{
Player = 0,
Prompt = "Найдите Future Knight, Llew или Flogal",
Options = new List<ChoiceOptionView>
{
new() { Slot = 0, Label = "Flogal (BT01/046EN)" },
new() { Slot = 1, Label = "Future Knight, Llew (BT01/045EN)" },
},
},
LegalIntents = new List<IntentPayload>(),
};
typeof(BattleView)
.GetField("_view", BindingFlags.Instance | BindingFlags.NonPublic)!
.SetValue(battle, match);
typeof(BattleView)
.GetMethod("SyncCardChoicePrompt", BindingFlags.Instance | BindingFlags.NonPublic)!
.Invoke(battle, null);
var overlay = Resources.FindObjectsOfTypeAll<GameObject>()
.First(go => go.name == "CardChoiceOverlay");
Assert.IsTrue(overlay.activeSelf);
Assert.IsTrue(Resources.FindObjectsOfTypeAll<GameObject>()
.Any(go => go.name == "CardChoice_1"));
}
finally
{
Object.DestroyImmediate(canvas.gameObject);
Cleanup();
}
}
[Test]
public void Hand_Choice_Uses_Hand_Cards_Without_Intent_Buttons()
{
var config = new AppConfig();
config.Load();
var tokens = new TokenStore();
var api = new ApiClient(config, tokens);
var player = new PlayerState();
var auth = new AuthService(api, tokens, player);
var meta = new MetaService(api, player);
var canvas = UiKit.CreateCanvas("Hand Choice Test Canvas");
try
{
var battle = new BattleView(canvas.transform, meta, () => { });
var match = new MatchView
{
You = 0,
ActivePlayer = 0,
Players = new List<PlayerView>
{
new()
{
Index = 0,
Hand = new List<CardView>
{
new() { InstanceId = 11, PrintingId = "BT01/046EN" },
new() { InstanceId = 12, PrintingId = "BT01/042EN" },
},
},
},
Choice = new ChoiceView
{
Player = 0,
Prompt = "Выберите карту из руки",
Options = new List<ChoiceOptionView>
{
new() { Slot = 0, Label = "Flogal" },
new() { Slot = 1, Label = "Little Sage, Marron" },
},
},
LegalIntents = new List<IntentPayload>(),
};
typeof(BattleView)
.GetField("_view", BindingFlags.Instance | BindingFlags.NonPublic)!
.SetValue(battle, match);
var isHandChoice = typeof(BattleView)
.GetMethod("IsHandChoice", BindingFlags.Instance | BindingFlags.NonPublic)!
.Invoke(battle, null);
Assert.AreEqual(true, isHandChoice);
typeof(BattleView)
.GetMethod("RefreshActionPrompt", BindingFlags.Instance | BindingFlags.NonPublic)!
.Invoke(battle, null);
var title = Resources.FindObjectsOfTypeAll<Text>()
.First(text => text.name == "ActionPromptTitle");
Assert.AreEqual("ВЫБОР КАРТЫ", title.text);
typeof(BattleView)
.GetMethod("RebuildIntents", BindingFlags.Instance | BindingFlags.NonPublic)!
.Invoke(battle, new object[] { match });
Assert.IsFalse(Resources.FindObjectsOfTypeAll<GameObject>()
.Any(go => go.name == "Intent_ChooseIntent"));
}
finally
{
Object.DestroyImmediate(canvas.gameObject);
Cleanup();
}
}
[Test]
public void Action_Prompt_Shows_Current_Required_Action()
{
var config = new AppConfig();
config.Load();
var tokens = new TokenStore();
var api = new ApiClient(config, tokens);
var player = new PlayerState();
var auth = new AuthService(api, tokens, player);
var meta = new MetaService(api, player);
var canvas = UiKit.CreateCanvas("Action Prompt Test Canvas");
try
{
var battle = new BattleView(canvas.transform, meta, () => { });
var match = new MatchView
{
You = 0,
ActivePlayer = 0,
TurnNumber = 2,
Phase = GamePhase.Ride,
LegalIntents = new List<IntentPayload>(),
};
typeof(BattleView)
.GetField("_view", BindingFlags.Instance | BindingFlags.NonPublic)!
.SetValue(battle, match);
var refresh = typeof(BattleView)
.GetMethod("RefreshActionPrompt", BindingFlags.Instance | BindingFlags.NonPublic)!;
refresh.Invoke(battle, null);
var prompt = Resources.FindObjectsOfTypeAll<GameObject>()
.First(go => go.name == "ActionPrompt");
Assert.IsTrue(prompt.activeSelf);
var title = Resources.FindObjectsOfTypeAll<Text>()
.First(text => text.name == "ActionPromptTitle");
Assert.AreEqual("СМЕНА", title.text);
match.Phase = GamePhase.Main;
refresh.Invoke(battle, null);
Assert.IsFalse(prompt.activeSelf);
}
finally
{
Object.DestroyImmediate(canvas.gameObject);
Cleanup();
}
}
private static RectTransform FindRect(string name) =>
Resources.FindObjectsOfTypeAll<GameObject>()
.Where(go => go.scene.IsValid() && go.name == name)
.Select(go => go.GetComponent<RectTransform>())
.First(rect => rect != null);
private static Rect ReferenceRect(RectTransform rect)
{
var size = rect.sizeDelta;
var anchor = new Vector2(
Mathf.Lerp(0f, 1920f, rect.anchorMin.x),
Mathf.Lerp(0f, 1080f, rect.anchorMin.y));
return new Rect(
anchor + rect.anchoredPosition - Vector2.Scale(size, rect.pivot),
size);
}
private static void AssertWithinScreen(Rect rect, string name)
{
Assert.GreaterOrEqual(rect.xMin, 0f, name + " left");
Assert.GreaterOrEqual(rect.yMin, 0f, name + " bottom");
Assert.LessOrEqual(rect.xMax, 1920f, name + " right");
Assert.LessOrEqual(rect.yMax, 1080f, name + " top");
}
[Test]
public void Battle_Renders_3D_Damage_And_Rested_Cards()
{
var config = new AppConfig();
config.Load();
var tokens = new TokenStore();
var api = new ApiClient(config, tokens);
var player = new PlayerState();
var auth = new AuthService(api, tokens, player);
var meta = new MetaService(api, player);
var canvas = UiKit.CreateCanvas("Test Canvas");
var battle = new BattleView(canvas.transform, meta, () => { });
var screen = Resources.FindObjectsOfTypeAll<GameObject>()
.First(go => go.name == "BattleScreen");
screen.SetActive(true);
CardView Card(string id, int instance, bool rested = false) => new CardView
{
PrintingId = id,
InstanceId = instance,
Rested = rested,
FaceUp = true,
Power = 8000,
BasePower = 8000,
Critical = 1,
BaseCritical = 1,
};
var self = new PlayerView
{
Index = 0,
Name = "Me",
Vanguard = Card("BT01/001EN", 1),
RearGuards = new CardView?[5],
Hand = new List<CardView> { Card("BT01/042EN", 2) },
Soul = new List<CardView> { Card("BT01/003EN", 3) },
FaceDownDamage = 2,
FaceUpDamage = new List<CardView> { Card("BT01/056EN", 4) },
DeckCount = 40,
Drop = new List<CardView>(),
};
self.RearGuards[3] = Card("BT01/042EN", 5, rested: true);
var opponent = new PlayerView
{
Index = 1,
Name = "Opponent",
Vanguard = Card("BT01/001EN", 6),
RearGuards = new CardView?[5],
Hand = new List<CardView>(),
Soul = new List<CardView>(),
FaceDownDamage = 1,
FaceUpDamage = new List<CardView>(),
DeckCount = 40,
Drop = new List<CardView>(),
};
var match = new MatchView
{
MatchId = "render-test",
You = 0,
ActivePlayer = 0,
Phase = GamePhase.Main,
Players = new List<PlayerView> { self, opponent },
Battle = new Vanguard.Shared.Protocol.BattleView
{
GuardCards = new List<CardView> { Card("BT01/011EN", 7) },
},
Clock = new ClockView(),
LegalIntents = new List<IntentPayload>(),
};
Resources.FindObjectsOfTypeAll<GameBoard3D>()
.FirstOrDefault()
?.SetVisible(true);
typeof(BattleView)
.GetField("_view", BindingFlags.Instance | BindingFlags.NonPublic)!
.SetValue(battle, match);
typeof(BattleView)
.GetMethod("Render", BindingFlags.Instance | BindingFlags.NonPublic)!
.Invoke(battle, null);
Assert.IsTrue(Exists("GameBoard3D"));
var cards = Resources.FindObjectsOfTypeAll<Card3D>()
.Where(card => card.gameObject.activeInHierarchy)
.ToList();
Assert.GreaterOrEqual(cards.Count, 3);
var rested = cards.FirstOrDefault(card =>
card.Ref.Zone == BoardZone.RearGuard
&& card.Ref.Player == 0
&& card.Ref.Slot == 3);
Assert.IsNotNull(rested, "Rested rear-guard was not rendered in 3D");
Assert.IsTrue(rested.IsRested);
Assert.AreEqual(90f, Mathf.Abs(Mathf.DeltaAngle(rested.TargetYaw, 0f)), 0.1f);
var damage = cards.Where(card =>
card.Ref.Zone == BoardZone.Damage
&& card.Ref.Player == 0).ToList();
Assert.AreEqual(3, damage.Count);
Assert.IsFalse(damage.Single(card => card.Ref.Index == 2).IsFaceUp);
Assert.AreEqual(
-90f,
Mathf.DeltaAngle(0f, damage.Single(card => card.Ref.Index == 0).TargetYaw),
0.1f,
"Damage cards must be turned counter-clockwise");
Assert.IsTrue(cards.Any(card =>
card.Ref.Zone == BoardZone.Guardian
&& card.Ref.InstanceId == 7));
var selfZoneRoot = Resources.FindObjectsOfTypeAll<GameObject>()
.FirstOrDefault(go => go.name == "ZoneBlocksSelf");
var opponentZoneRoot = Resources.FindObjectsOfTypeAll<GameObject>()
.FirstOrDefault(go => go.name == "ZoneBlocksOpponent");
Assert.IsNotNull(selfZoneRoot);
Assert.IsNotNull(opponentZoneRoot);
var selfLabels = selfZoneRoot.GetComponentsInChildren<Text>();
var opponentLabels = opponentZoneRoot.GetComponentsInChildren<Text>();
Assert.IsTrue(selfLabels.Any(text => text.text.Contains("Рука 1")));
Assert.IsTrue(selfLabels.Any(text => text.text.Contains("Душа 1")));
Assert.IsTrue(opponentLabels.Any(text => text.text.Contains("Рука 0")));
Cleanup();
}
private static bool Exists(string name) =>
Resources.FindObjectsOfTypeAll<GameObject>().Any(go => go.name == name);
private static void Cleanup()
{
foreach (var board in Resources.FindObjectsOfTypeAll<GameBoard3D>())
{
if (board != null)
Object.DestroyImmediate(board.gameObject);
}
var canvas = GameObject.Find("Test Canvas");
if (canvas != null)
Object.DestroyImmediate(canvas);
var eventSystem = Object.FindAnyObjectByType<EventSystem>();
if (eventSystem != null)
Object.DestroyImmediate(eventSystem.gameObject);
}
}
}