180 lines
8.6 KiB
C#
180 lines
8.6 KiB
C#
|
|
using System.Collections;
|
||
|
|
using NUnit.Framework;
|
||
|
|
using TKS.OutGame;
|
||
|
|
using UnityEngine;
|
||
|
|
using UnityEngine.TestTools;
|
||
|
|
using UnityEngine.UI;
|
||
|
|
|
||
|
|
namespace TKS.Tests
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// 타이틀 화면(PD 2026-11-13 「장수가 모이는 연출로 시작 · 대기 움직임 · 꾸미기 가벼운 애니 · "Press Any Key" 깜박임 · 터치 → 다음」).
|
||
|
|
/// ① 표대로 장수 6 · 프레임(쉼 + 대기 자세) · 로고 · 글자 ② 등장 = 화면 밖에서 제자리로 · 끝나면 글자 깜박임 ③ 누름 = 등장 중이면 건너뜀 · 글자가 뜬 뒤면 페이드 → 시작 함수 · 타이틀 닫힘
|
||
|
|
/// ④ 대기 프레임이 바뀜 ⑤ 층(P41) = 루트 Static · 연출·글자 Frame · 글자 층에 레이캐스터 없음 ⑥ 시작 흐름 훅 = 메모리 세이브(테스트·캡처)면 건너뜀(기존 테스트 = 바로 로비).
|
||
|
|
/// </summary>
|
||
|
|
public class TitleScreenTests
|
||
|
|
{
|
||
|
|
Camera cam;
|
||
|
|
|
||
|
|
[SetUp]
|
||
|
|
public void SetUp()
|
||
|
|
{
|
||
|
|
cam = new GameObject("TestCam").AddComponent<Camera>();
|
||
|
|
cam.orthographic = true;
|
||
|
|
TitleScreen.ForceShow = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
[TearDown]
|
||
|
|
public void TearDown()
|
||
|
|
{
|
||
|
|
TitleScreen.ForceShow = false;
|
||
|
|
if (TitleScreen.Current != null) Object.Destroy(TitleScreen.Current.gameObject);
|
||
|
|
foreach (var f in Object.FindObjectsByType<ScreenFader>(FindObjectsSortMode.None)) Object.Destroy(f.gameObject);
|
||
|
|
if (cam != null) Object.Destroy(cam.gameObject);
|
||
|
|
}
|
||
|
|
|
||
|
|
[UnityTest]
|
||
|
|
public IEnumerator Build_FollowsTable()
|
||
|
|
{
|
||
|
|
var rows = LoadingTable.Load(TitleScreen.TablePath);
|
||
|
|
int heroRows = rows.FindAll(r => r.kind == "hero").Count;
|
||
|
|
var ts = TitleScreen.Show(cam, null);
|
||
|
|
yield return null;
|
||
|
|
Assert.AreEqual(heroRows, ts.Heroes.Count, "표 hero 줄 수만큼 장수");
|
||
|
|
Assert.GreaterOrEqual(ts.Heroes.Count, 6);
|
||
|
|
foreach (var h in ts.Heroes) Assert.GreaterOrEqual(h.frames.Length, 4, h.id + " = 쉼 + 대기 자세 3 이상");
|
||
|
|
Assert.IsNotNull(ts.Logo != null ? ts.Logo.sprite : null, "로고 그림");
|
||
|
|
Assert.IsNotNull(ts.PressText, "Press Any Key 글자");
|
||
|
|
Assert.AreEqual(rows.Find(r => r.kind == "press").text, ts.PressText.text);
|
||
|
|
Assert.IsNotNull(ts.Ambient, "꾸미기 연출 층(LobbyAmbient)");
|
||
|
|
}
|
||
|
|
|
||
|
|
[UnityTest]
|
||
|
|
public IEnumerator Intro_GathersThenPressBlinks()
|
||
|
|
{
|
||
|
|
var ts = TitleScreen.Show(cam, null);
|
||
|
|
ts.SetTime(0f);
|
||
|
|
yield return null;
|
||
|
|
var first = ts.Heroes[0];
|
||
|
|
Assert.Less(first.img.color.a, 0.01f, "시작 전 장수는 안 보임");
|
||
|
|
ts.SetTime(first.row.phase + first.row.fps * 0.3f);
|
||
|
|
var target = TitleScreen.Target(first.row);
|
||
|
|
Assert.Greater(Vector2.Distance(first.rt.anchoredPosition, target), 1f, "날아오는 중 = 제자리 아님");
|
||
|
|
Assert.IsFalse(ts.PressReady, "등장 중에는 글자 안 뜸");
|
||
|
|
ts.SkipIntro();
|
||
|
|
foreach (var h in ts.Heroes)
|
||
|
|
{
|
||
|
|
Assert.Less(Vector2.Distance(h.rt.anchoredPosition, TitleScreen.Target(h.row)), 0.01f, h.id + " 제자리");
|
||
|
|
Assert.AreEqual(0f, Mathf.Repeat(h.rt.anchoredPosition.x - h.row.w * 0.5f, 1f), 0.001f, h.id + " 왼쪽 모서리 정수 픽셀(QA r1 M6)");
|
||
|
|
Assert.IsTrue(h.landed, h.id + " 착지");
|
||
|
|
}
|
||
|
|
Assert.IsTrue(ts.IntroDone);
|
||
|
|
float until = Time.unscaledTime + 2f;
|
||
|
|
while (!ts.PressReady && Time.unscaledTime < until) yield return null;
|
||
|
|
Assert.IsTrue(ts.PressReady, "등장 끝 뒤 글자가 뜸");
|
||
|
|
float minA = 1f, maxA = 0f;
|
||
|
|
for (float w = Time.unscaledTime + 1.6f; Time.unscaledTime < w;)
|
||
|
|
{
|
||
|
|
yield return null;
|
||
|
|
minA = Mathf.Min(minA, ts.PressText.color.a);
|
||
|
|
maxA = Mathf.Max(maxA, ts.PressText.color.a);
|
||
|
|
}
|
||
|
|
Assert.Greater(maxA - minA, 0.3f, "글자 깜박임(알파가 오르내림)");
|
||
|
|
}
|
||
|
|
|
||
|
|
[UnityTest]
|
||
|
|
public IEnumerator Press_SkipsIntroThenStarts()
|
||
|
|
{
|
||
|
|
bool started = false;
|
||
|
|
var ts = TitleScreen.Show(cam, () => started = true);
|
||
|
|
yield return null;
|
||
|
|
ts.Press();
|
||
|
|
Assert.IsTrue(ts.IntroDone, "등장 중 누름 = 끝 모습으로");
|
||
|
|
Assert.IsFalse(ts.Leaving, "첫 누름은 건너뛰기만");
|
||
|
|
float until = Time.unscaledTime + 2f;
|
||
|
|
while (!ts.PressReady && Time.unscaledTime < until) yield return null;
|
||
|
|
ts.Press();
|
||
|
|
Assert.IsTrue(ts.Leaving);
|
||
|
|
until = Time.unscaledTime + 3f;
|
||
|
|
while (!started && Time.unscaledTime < until) yield return null;
|
||
|
|
Assert.IsTrue(started, "페이드 뒤 시작 함수(로비)");
|
||
|
|
yield return null;
|
||
|
|
Assert.IsNull(TitleScreen.Current, "타이틀 닫힘");
|
||
|
|
}
|
||
|
|
|
||
|
|
[UnityTest]
|
||
|
|
public IEnumerator Idle_FramesChange()
|
||
|
|
{
|
||
|
|
var ts = TitleScreen.Show(cam, null);
|
||
|
|
ts.SkipIntro();
|
||
|
|
yield return null;
|
||
|
|
var seen = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.HashSet<Sprite>>();
|
||
|
|
foreach (var h in ts.Heroes) seen[h.id] = new System.Collections.Generic.HashSet<Sprite>();
|
||
|
|
for (float w = Time.unscaledTime + 1.8f; Time.unscaledTime < w;)
|
||
|
|
{
|
||
|
|
yield return null;
|
||
|
|
foreach (var h in ts.Heroes) seen[h.id].Add(h.img.sprite);
|
||
|
|
}
|
||
|
|
foreach (var h in ts.Heroes) Assert.GreaterOrEqual(seen[h.id].Count, 3, h.id + " 대기 자세가 바뀜");
|
||
|
|
}
|
||
|
|
|
||
|
|
[UnityTest]
|
||
|
|
public IEnumerator Layers_P41()
|
||
|
|
{
|
||
|
|
var ts = TitleScreen.Show(cam, null);
|
||
|
|
yield return null;
|
||
|
|
Assert.AreEqual(UiRate.Static, ts.RootCanvas.GetComponent<UiLayerInfo>().rate, "루트 = 배경만(Static)");
|
||
|
|
Assert.AreEqual(UiRate.Frame, ts.Ui.GetComponent<UiLayerInfo>().rate, "로고·글자 = Frame");
|
||
|
|
Assert.IsNull(ts.Ui.GetComponent<GraphicRaycaster>(), "글자 층에 레이캐스터 없음(입력 = 폴링)");
|
||
|
|
var amb = ts.Ambient.GetComponent<UiLayerInfo>();
|
||
|
|
Assert.IsNotNull(amb);
|
||
|
|
Assert.AreEqual(UiRate.Frame, amb.rate, "연출·장수 = Frame");
|
||
|
|
}
|
||
|
|
|
||
|
|
[UnityTest]
|
||
|
|
public IEnumerator Show_HidesCanvasesBelowThenRestores()
|
||
|
|
{
|
||
|
|
var below = new GameObject("BelowCanvas");
|
||
|
|
var bc = UiLayer.Root(below, cam, 100, 1f, UiLayer.Reference, UiRate.Static, false);
|
||
|
|
var above = new GameObject("AboveCanvas");
|
||
|
|
var ac = UiLayer.Root(above, cam, 990, 0.2f, UiLayer.Reference, UiRate.Static, false);
|
||
|
|
var ts = TitleScreen.Show(cam, null);
|
||
|
|
yield return null;
|
||
|
|
Assert.IsFalse(bc.enabled, "타이틀 아래 루트 캔버스 = 끔(QA r1 M5)");
|
||
|
|
Assert.IsTrue(ac.enabled, "타이틀 위(페이드 등) = 그대로");
|
||
|
|
ts.Close();
|
||
|
|
yield return null;
|
||
|
|
Assert.IsTrue(bc.enabled, "닫으면 되살림");
|
||
|
|
Object.Destroy(below);
|
||
|
|
Object.Destroy(above);
|
||
|
|
}
|
||
|
|
|
||
|
|
[UnityTest]
|
||
|
|
public IEnumerator Stage_FitsInsideScreen()
|
||
|
|
{
|
||
|
|
var ts = TitleScreen.Show(cam, null);
|
||
|
|
yield return null;
|
||
|
|
var root = (RectTransform)ts.transform;
|
||
|
|
var sz = root.rect.size;
|
||
|
|
Assert.LessOrEqual(ts.Stage.localScale.x * TitleScreen.ArtW, sz.x + 0.5f, "무대 폭 ≤ 화면(contain · QA r1 B3·B4)");
|
||
|
|
Assert.LessOrEqual(ts.Stage.localScale.y * TitleScreen.ArtH, sz.y + 0.5f, "무대 높이 ≤ 화면(QA r1 B1·B2)");
|
||
|
|
Assert.GreaterOrEqual(ts.Art.localScale.x * TitleScreen.ArtW, sz.x - 0.5f, "배경 = 화면을 덮음(cover)");
|
||
|
|
Assert.GreaterOrEqual(ts.Art.localScale.y * TitleScreen.ArtH, sz.y - 0.5f);
|
||
|
|
Assert.AreEqual(ts.Stage, ts.Logo.rectTransform.parent, "로고 = 무대 위(장수와 같은 배율)");
|
||
|
|
}
|
||
|
|
|
||
|
|
[Test]
|
||
|
|
public void Provider_SkipsOnMemorySave()
|
||
|
|
{
|
||
|
|
SaveStore.UseMemory(true);
|
||
|
|
bool called = false;
|
||
|
|
Assert.IsFalse(TitleScreen.Provide(cam, () => called = true), "메모리 세이브(테스트·캡처) = 타이틀 건너뜀 → BootFlow 가 로비");
|
||
|
|
Assert.IsNull(TitleScreen.Current);
|
||
|
|
TitleScreen.ForceShow = true;
|
||
|
|
Assert.IsTrue(TitleScreen.Provide(cam, () => called = true), "ForceShow = 띄움");
|
||
|
|
Assert.IsNotNull(TitleScreen.Current);
|
||
|
|
Assert.IsFalse(called);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|