feat: 피격 반응 면역을 경직/넉백/다운으로 분리

- 상태이상 데이터와 관리자에서 단일 피격 면역을 경직, 넉백, 다운 개별 면역으로 분리

- 플레이어 프리팹과 디버그 메뉴, 공용 경직 애니메이션을 갱신해 분리된 면역 상태를 테스트 가능하게 정리

- PlayMode 테스트를 추가해 각 면역이 대응하는 반응만 차단하는지 검증
This commit is contained in:
2026-04-09 23:22:02 +09:00
parent 7776f7ed05
commit 0fa23d4389
17 changed files with 42220 additions and 32 deletions

View File

@@ -8,6 +8,7 @@ using UnityEngine.TestTools;
using Unity.Netcode;
using Unity.Netcode.Transports.UTP;
using Colosseum.Abnormalities;
using Colosseum.Player;
namespace Colosseum.Tests
@@ -26,6 +27,7 @@ namespace Colosseum.Tests
private NetworkManager networkManager;
private GameObject playerObject;
private HitReactionController hitReactionController;
private AbnormalityManager abnormalityManager;
private Animator animator;
[UnitySetUp]
@@ -55,6 +57,7 @@ namespace Colosseum.Tests
playerObject.transform.position = Vector3.zero;
playerObject.AddComponent<NetworkObject>();
abnormalityManager = playerObject.AddComponent<AbnormalityManager>();
hitReactionController = playerObject.AddComponent<HitReactionController>();
GameObject visualObject = new GameObject("Visual");
@@ -72,6 +75,7 @@ namespace Colosseum.Tests
yield return null;
Assert.NotNull(hitReactionController, "HitReactionController를 생성하지 못했습니다.");
Assert.NotNull(abnormalityManager, "AbnormalityManager를 생성하지 못했습니다.");
Assert.NotNull(animator, "테스트용 Animator를 생성하지 못했습니다.");
}
@@ -120,6 +124,69 @@ namespace Colosseum.Tests
Assert.Greater(measuredDelta, 0.01f, "넉백에서 Hit 애니메이션이 재생되지 않았습니다.");
}
[UnityTest]
public IEnumerator StaggerImmunity_OnlyBlocksStagger()
{
AbnormalityData staggerImmunity = CreateImmunityAbnormality("테스트 경직 면역", ignoreStagger: true);
abnormalityManager.ApplyAbnormality(staggerImmunity, playerObject);
yield return null;
hitReactionController.ApplyStagger(0.4f, false);
Assert.IsFalse(hitReactionController.IsStaggered, "경직 면역 상태인데 경직이 적용되었습니다.");
yield return ResetHitReactionState();
hitReactionController.ApplyKnockback(Vector3.back * 3f, 0.25f, false);
Assert.IsTrue(hitReactionController.IsKnockbackActive, "경직 면역이 넉백까지 막으면 안 됩니다.");
yield return ResetHitReactionState();
hitReactionController.ApplyDown(0.3f);
Assert.IsTrue(hitReactionController.IsDowned, "경직 면역이 다운까지 막으면 안 됩니다.");
Object.DestroyImmediate(staggerImmunity);
}
[UnityTest]
public IEnumerator KnockbackImmunity_OnlyBlocksKnockback()
{
AbnormalityData knockbackImmunity = CreateImmunityAbnormality("테스트 넉백 면역", ignoreKnockback: true);
abnormalityManager.ApplyAbnormality(knockbackImmunity, playerObject);
yield return null;
hitReactionController.ApplyStagger(0.4f, false);
Assert.IsTrue(hitReactionController.IsStaggered, "넉백 면역이 경직까지 막으면 안 됩니다.");
yield return ResetHitReactionState();
hitReactionController.ApplyKnockback(Vector3.back * 3f, 0.25f, false);
Assert.IsFalse(hitReactionController.IsKnockbackActive, "넉백 면역 상태인데 넉백이 적용되었습니다.");
yield return ResetHitReactionState();
hitReactionController.ApplyDown(0.3f);
Assert.IsTrue(hitReactionController.IsDowned, "넉백 면역이 다운까지 막으면 안 됩니다.");
Object.DestroyImmediate(knockbackImmunity);
}
[UnityTest]
public IEnumerator DownImmunity_OnlyBlocksDown()
{
AbnormalityData downImmunity = CreateImmunityAbnormality("테스트 다운 면역", ignoreDown: true);
abnormalityManager.ApplyAbnormality(downImmunity, playerObject);
yield return null;
hitReactionController.ApplyStagger(0.4f, false);
Assert.IsTrue(hitReactionController.IsStaggered, "다운 면역이 경직까지 막으면 안 됩니다.");
yield return ResetHitReactionState();
hitReactionController.ApplyKnockback(Vector3.back * 3f, 0.25f, false);
Assert.IsTrue(hitReactionController.IsKnockbackActive, "다운 면역이 넉백까지 막으면 안 됩니다.");
yield return ResetHitReactionState();
hitReactionController.ApplyDown(0.3f);
Assert.IsFalse(hitReactionController.IsDowned, "다운 면역 상태인데 다운이 적용되었습니다.");
Object.DestroyImmediate(downImmunity);
}
private IEnumerator MeasureHitPlaybackDelta(System.Action applyReaction, float expectedSpeedMultiplier, System.Action<float> setMeasuredDelta)
{
hitReactionController.ClearHitReactionState();
@@ -164,8 +231,33 @@ namespace Colosseum.Tests
return (ushort)Random.Range(20000, 40000);
}
private static AbnormalityData CreateImmunityAbnormality(string abnormalityName, bool ignoreStagger = false, bool ignoreKnockback = false, bool ignoreDown = false)
{
AbnormalityData abnormalityData = ScriptableObject.CreateInstance<AbnormalityData>();
abnormalityData.abnormalityName = abnormalityName;
abnormalityData.duration = 5f;
abnormalityData.ignoreStagger = ignoreStagger;
abnormalityData.ignoreKnockback = ignoreKnockback;
abnormalityData.ignoreDown = ignoreDown;
return abnormalityData;
}
private IEnumerator ResetHitReactionState()
{
hitReactionController.ClearHitReactionState();
yield return null;
}
private void CleanupTestObjects()
{
if (NetworkManager.Singleton != null && NetworkManager.Singleton.gameObject != networkManagerObject)
{
if (NetworkManager.Singleton.IsListening)
NetworkManager.Singleton.Shutdown();
Object.DestroyImmediate(NetworkManager.Singleton.gameObject);
}
if (playerObject != null)
{
Object.DestroyImmediate(playerObject);