175 lines
8.2 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|