Files
tiny_tackle_heroes/Assets/_Project/Tests/PlayMode/Defense25DTowerBodyAnimationPlayModeTests.cs
T
2026-09-27 21:08:05 +09:00

175 lines
8.2 KiB
C#

using System.Reflection;
using System.Linq;
using BumpCombat.Constants;
using BumpCombat.Defense;
using BumpCombat.Defense.Comparison;
using BumpCombat.Enemies;
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
namespace BumpCombat.Tests
{
public sealed class Defense25DTowerBodyAnimationPlayModeTests
{
private Defense25DComparison world;
private Defense25DComparisonSettings settings;
private EnemyDefinition highHealthEnemy;
[SetUp]
public void SetUp()
{
settings = Object.Instantiate(AssetDatabase.LoadAssetAtPath<Defense25DComparisonSettings>(
"Assets/_Project/Constants/Defense25DComparison/ComparisonSettings.asset"));
EnemyDefinition sourceEnemy = settings.Enemy;
highHealthEnemy = Object.Instantiate(sourceEnemy);
highHealthEnemy.name = sourceEnemy.name + " Tower Body Animation Test";
highHealthEnemy.Configure(sourceEnemy.Kind, sourceEnemy.AttackShape, 10000f,
sourceEnemy.MoveSpeed, sourceEnemy.AttackDamage, sourceEnemy.WarningDuration,
sourceEnemy.ActiveDuration, sourceEnemy.RecoveryDuration, sourceEnemy.AttackRange,
sourceEnemy.PreferredRange, sourceEnemy.AttackLength, sourceEnemy.AttackWidth,
sourceEnemy.AttackRadius, sourceEnemy.ExperienceValue, sourceEnemy.AttackAnimationLeadTime);
highHealthEnemy.ConfigureRole(sourceEnemy.Role);
highHealthEnemy.ConfigureAttackPatterns(sourceEnemy.AttackPatterns);
settings.Enemy = highHealthEnemy;
world = new GameObject("2.5D tower body animation test").AddComponent<Defense25DComparison>();
world.Initialize(settings);
world.AutoSpawn = false;
world.ReadInput = false;
}
[TearDown]
public void TearDown()
{
if (world != null) Object.DestroyImmediate(world.gameObject);
if (highHealthEnemy != null) Object.DestroyImmediate(highHealthEnemy);
if (settings != null) Object.DestroyImmediate(settings);
}
[Test]
public void SuccessfulAttackAdvancesAuthoredFramesThenReturnsToIdle()
{
Defense25DTower tower = BuildTower(DefenseTowerKind.Direct, 0);
Defense25DEnemy target = SpawnTarget(tower);
Sprite[] attack = DefenseTowerArt.Load(DefenseTowerArt.BodyKey(tower.Kind, tower.Tier, "Attack01"));
Sprite[] idle = DefenseTowerArt.Load(DefenseTowerArt.BodyKey(tower.Kind, tower.Tier, "Idle"));
SpriteRenderer body = tower.transform.Find("Tower billboard").GetComponent<SpriteRenderer>();
tower.Step(0f);
Assert.That(tower.IsBodyAttacking, Is.True);
Assert.That(body.sprite, Is.EqualTo(attack[0]));
tower.Step(.125f);
Assert.That(tower.BodyAnimationFrame, Is.EqualTo(1));
Assert.That(body.sprite, Is.EqualTo(attack[1]));
target.Actor.ResetAt(tower.Position + Vector2.right
* (GameplayConstants.Current.Defense.TowerRange + 2f));
Physics.SyncTransforms();
tower.Step(.625f);
Assert.That(tower.IsBodyAttacking, Is.False);
Assert.That(tower.BodyAnimationFrame, Is.EqualTo(0));
Assert.That(body.sprite, Is.EqualTo(idle[0]));
Assert.That(target.IsAvailable, Is.True);
}
[Test]
public void FailedShotRestoresThePriorBodyAnimationState()
{
Defense25DTower tower = BuildTower(DefenseTowerKind.Lightning, 0);
tower.Step(.125f);
SpriteRenderer body = tower.transform.Find("Tower billboard").GetComponent<SpriteRenderer>();
Sprite previousSprite = body.sprite;
bool previousFlip = body.flipX;
int previousFrame = tower.BodyAnimationFrame;
bool previousAttacking = tower.IsBodyAttacking;
Defense25DEnemy target = SpawnTarget(tower);
target.gameObject.layer = 0;
Physics.SyncTransforms();
tower.StepParityAttacks(0f);
Assert.That(tower.BodyAnimationFrame, Is.EqualTo(previousFrame));
Assert.That(tower.IsBodyAttacking, Is.EqualTo(previousAttacking));
Assert.That(body.flipX, Is.EqualTo(previousFlip));
Assert.That(body.sprite, Is.EqualTo(previousSprite));
}
[Test]
public void ProjectileLaunchMatchesRenderedStaffTipThroughPitchedCamera()
{
Defense25DTower tower = BuildTower(DefenseTowerKind.Direct, 0);
SpawnTarget(tower);
tower.Step(0f);
SpriteRenderer body = tower.transform.Find("Tower billboard").GetComponent<SpriteRenderer>();
Camera camera = CreatePitchedCamera(body.transform.position);
Vector3 staffTip = tower.ParityStaffTipVisualWorldPosition;
GameObject projectile = GameObject.Find("Tower Projectile");
Assert.That(projectile, Is.Not.Null);
Vector3 projectedStaffTip = camera.WorldToScreenPoint(staffTip);
Vector3 projectedLaunch = camera.WorldToScreenPoint(projectile.transform.position);
Assert.That(projectedStaffTip.z, Is.GreaterThan(0f));
Assert.That(projectedLaunch.z, Is.GreaterThan(0f));
Assert.That(Vector2.Distance(projectedStaffTip, projectedLaunch), Is.LessThan(.01f),
"The projectile's rendered launch point must coincide with the active frame's staff tip.");
}
[Test]
public void SupportAuraUsesMaskFrameAndTracksBillboardTransform()
{
Defense25DTower support = BuildTower(DefenseTowerKind.Empowering, 0);
Defense25DTower receiver = BuildTower(DefenseTowerKind.Direct, 4);
Assert.That(Vector2.Distance(support.Position, receiver.Position),
Is.LessThanOrEqualTo(GameplayConstants.Current.Defense.SupportRadius));
world.TowerManager.Step(0f);
SpriteRenderer body = receiver.transform.Find("Tower billboard").GetComponent<SpriteRenderer>();
SpriteRenderer aura = receiver.transform.Find("Support Aura Art").GetComponent<SpriteRenderer>();
Assert.That(receiver.BodySupportAuraTier, Is.EqualTo(support.Tier));
Assert.That(aura.enabled, Is.True);
Assert.That(aura.sprite.name, Is.EqualTo(body.sprite.name));
Assert.That(aura.flipX, Is.EqualTo(body.flipX));
Assert.That(Vector3.Distance(aura.transform.position, body.transform.position), Is.LessThan(.001f));
Assert.That(Vector3.Distance(aura.transform.localScale, body.transform.localScale), Is.LessThan(.001f));
}
private Defense25DTower BuildTower(DefenseTowerKind kind, int pad)
{
world.Player.ResetAt(GameplayConstants.Current.Defense.GetBuildPadPosition(pad));
world.TowerManager.AddCurrency(1000);
Assert.That(world.TowerManager.TryBuildTowerAtPad(pad, kind), Is.True,
$"pad={pad} kind={kind}");
Defense25DTower tower = world.TowerManager.GetTowerAtPad(pad);
tower.SendMessage("LateUpdate", SendMessageOptions.DontRequireReceiver);
return tower;
}
private Defense25DEnemy SpawnTarget(Defense25DTower tower)
{
Defense25DEnemy target = Enumerable.Range(0,
GameplayConstants.Current.Defense.PortalEnemyCount * 2)
.Select(_ => world.SpawnNextEnemy())
.First(enemy => enemy != null && !world.IsTowerImmuneToEnemy(tower.Kind, enemy));
target.Actor.ResetAt(tower.Position + Vector2.right);
target.ApplyStun(30f);
Physics.SyncTransforms();
return target;
}
private Camera CreatePitchedCamera(Vector3 target)
{
GameObject cameraObject = new("Staff tip launch projection camera");
Camera camera = cameraObject.AddComponent<Camera>();
camera.orthographic = true;
camera.transform.rotation = Quaternion.Euler(settings.CameraPitch, 0f, 0f);
camera.transform.position = target - camera.transform.forward * 10f;
camera.transform.SetParent(world.transform, true);
return camera;
}
}
}