feat: 플레이어 행동 상태 통합 및 이상상태 디버그 추가
- PlayerActionState로 이동, 점프, 스킬 입력 가능 여부를 통합 - 기절과 침묵 이상상태 데이터 및 효과 에셋을 추가 - 로컬 플레이어용 이상상태 디버그 HUD와 자동 검증 러너를 추가 - 플레이어 이동, 스킬 입력, 네트워크 상태를 새 행동 상태 기준으로 정리
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
using System.Collections;
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
using Unity.Netcode;
|
||||
|
||||
using Colosseum.Abnormalities;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace Colosseum.Player
|
||||
{
|
||||
/// <summary>
|
||||
/// 플레이어 이상상태와 행동 제어 연동을 자동 검증하는 디버그 러너.
|
||||
/// 기절, 침묵, 사망, 리스폰 순으로 상태를 검사합니다.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public class PlayerAbnormalityVerificationRunner : NetworkBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
[SerializeField] private AbnormalityManager abnormalityManager;
|
||||
[SerializeField] private PlayerActionState actionState;
|
||||
[SerializeField] private PlayerNetworkController networkController;
|
||||
|
||||
[Header("Test Data")]
|
||||
[SerializeField] private AbnormalityData stunData;
|
||||
[SerializeField] private AbnormalityData silenceData;
|
||||
|
||||
[Header("Execution")]
|
||||
[Tooltip("에디터 플레이 시작 시 자동 검증 실행")]
|
||||
[SerializeField] private bool runOnStartInEditor = false;
|
||||
|
||||
[Tooltip("각 검증 단계 사이 대기 시간")]
|
||||
[Min(0.05f)]
|
||||
[SerializeField] private float settleDelay = 0.2f;
|
||||
|
||||
[Header("Result")]
|
||||
[SerializeField] private bool isRunning;
|
||||
[SerializeField] private bool lastRunPassed;
|
||||
[SerializeField] private int totalChecks;
|
||||
[SerializeField] private int failedChecks;
|
||||
[TextArea(5, 12)]
|
||||
[SerializeField] private string lastReport = string.Empty;
|
||||
|
||||
private readonly System.Text.StringBuilder reportBuilder = new System.Text.StringBuilder();
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
if (!IsOwner || !ShouldEnableRunner())
|
||||
{
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
ResolveReferences();
|
||||
LoadDefaultAssetsIfNeeded();
|
||||
|
||||
if (runOnStartInEditor)
|
||||
{
|
||||
StartCoroutine(RunVerificationRoutine());
|
||||
}
|
||||
}
|
||||
|
||||
[ContextMenu("Run Verification")]
|
||||
public void RunVerification()
|
||||
{
|
||||
if (!Application.isPlaying || !IsOwner || isRunning)
|
||||
return;
|
||||
|
||||
StartCoroutine(RunVerificationRoutine());
|
||||
}
|
||||
|
||||
private IEnumerator RunVerificationRoutine()
|
||||
{
|
||||
if (isRunning)
|
||||
yield break;
|
||||
|
||||
ResolveReferences();
|
||||
LoadDefaultAssetsIfNeeded();
|
||||
|
||||
if (abnormalityManager == null || actionState == null || networkController == null || stunData == null || silenceData == null)
|
||||
{
|
||||
Debug.LogWarning("[AbnormalityVerification] Missing references or test data.");
|
||||
yield break;
|
||||
}
|
||||
|
||||
isRunning = true;
|
||||
totalChecks = 0;
|
||||
failedChecks = 0;
|
||||
lastRunPassed = false;
|
||||
reportBuilder.Clear();
|
||||
AppendLine("=== Player Abnormality Verification Start ===");
|
||||
|
||||
abnormalityManager.RemoveAllAbnormalities();
|
||||
RequestRespawnRpc();
|
||||
yield return new WaitForSeconds(settleDelay);
|
||||
|
||||
Verify("초기 상태: 사망 아님", !networkController.IsDead);
|
||||
Verify("초기 상태: 이동 가능", actionState.CanMove);
|
||||
Verify("초기 상태: 스킬 사용 가능", actionState.CanUseSkills);
|
||||
|
||||
abnormalityManager.ApplyAbnormality(stunData, gameObject);
|
||||
yield return new WaitForSeconds(settleDelay);
|
||||
|
||||
Verify("기절 적용: IsStunned", abnormalityManager.IsStunned);
|
||||
Verify("기절 적용: ActionState.IsStunned", actionState.IsStunned);
|
||||
Verify("기절 적용: 이동 불가", !actionState.CanMove);
|
||||
Verify("기절 적용: 점프 불가", !actionState.CanJump);
|
||||
Verify("기절 적용: 스킬 사용 불가", !actionState.CanUseSkills);
|
||||
Verify("기절 적용: 이동속도 0", Mathf.Approximately(actionState.MoveSpeedMultiplier, 0f));
|
||||
|
||||
yield return new WaitForSeconds(stunData.duration + settleDelay);
|
||||
|
||||
Verify("기절 해제: IsStunned false", !abnormalityManager.IsStunned);
|
||||
Verify("기절 해제: 이동 가능 복구", actionState.CanMove);
|
||||
Verify("기절 해제: 스킬 사용 가능 복구", actionState.CanUseSkills);
|
||||
|
||||
abnormalityManager.ApplyAbnormality(silenceData, gameObject);
|
||||
yield return new WaitForSeconds(settleDelay);
|
||||
|
||||
Verify("침묵 적용: IsSilenced", abnormalityManager.IsSilenced);
|
||||
Verify("침묵 적용: 이동 가능 유지", actionState.CanMove);
|
||||
Verify("침묵 적용: 점프 가능 유지", actionState.CanJump);
|
||||
Verify("침묵 적용: 스킬 사용 불가", !actionState.CanUseSkills);
|
||||
|
||||
yield return new WaitForSeconds(silenceData.duration + settleDelay);
|
||||
|
||||
Verify("침묵 해제: IsSilenced false", !abnormalityManager.IsSilenced);
|
||||
Verify("침묵 해제: 스킬 사용 가능 복구", actionState.CanUseSkills);
|
||||
|
||||
abnormalityManager.ApplyAbnormality(stunData, gameObject);
|
||||
yield return new WaitForSeconds(settleDelay);
|
||||
networkController.TakeDamageRpc(networkController.Health + 1f);
|
||||
yield return new WaitForSeconds(settleDelay);
|
||||
|
||||
Verify("사망 처리: IsDead", networkController.IsDead);
|
||||
Verify("사망 처리: 입력 불가", !actionState.CanReceiveInput);
|
||||
Verify("사망 처리: 이동 불가", !actionState.CanMove);
|
||||
Verify("사망 처리: 스킬 사용 불가", !actionState.CanUseSkills);
|
||||
Verify("사망 처리: 활성 이상상태 제거", abnormalityManager.ActiveAbnormalities.Count == 0);
|
||||
|
||||
RequestRespawnRpc();
|
||||
yield return new WaitForSeconds(settleDelay);
|
||||
|
||||
Verify("리스폰: IsDead false", !networkController.IsDead);
|
||||
Verify("리스폰: 활성 이상상태 없음", abnormalityManager.ActiveAbnormalities.Count == 0);
|
||||
Verify("리스폰: 이동 가능", actionState.CanMove);
|
||||
Verify("리스폰: 스킬 사용 가능", actionState.CanUseSkills);
|
||||
|
||||
lastRunPassed = failedChecks == 0;
|
||||
AppendLine(lastRunPassed
|
||||
? "=== Verification Passed ==="
|
||||
: $"=== Verification Failed: {failedChecks}/{totalChecks} checks failed ===");
|
||||
|
||||
lastReport = reportBuilder.ToString();
|
||||
Debug.Log(lastReport);
|
||||
isRunning = false;
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server)]
|
||||
private void RequestRespawnRpc()
|
||||
{
|
||||
if (networkController == null)
|
||||
return;
|
||||
|
||||
networkController.Respawn();
|
||||
}
|
||||
|
||||
private void ResolveReferences()
|
||||
{
|
||||
if (abnormalityManager == null)
|
||||
abnormalityManager = GetComponent<AbnormalityManager>();
|
||||
if (actionState == null)
|
||||
actionState = GetComponent<PlayerActionState>();
|
||||
if (networkController == null)
|
||||
networkController = GetComponent<PlayerNetworkController>();
|
||||
}
|
||||
|
||||
private void LoadDefaultAssetsIfNeeded()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (stunData == null)
|
||||
{
|
||||
stunData = AssetDatabase.LoadAssetAtPath<AbnormalityData>("Assets/_Game/Data/Abnormalities/Data_Abnormality_Player_Stun.asset");
|
||||
}
|
||||
|
||||
if (silenceData == null)
|
||||
{
|
||||
silenceData = AssetDatabase.LoadAssetAtPath<AbnormalityData>("Assets/_Game/Data/Abnormalities/Data_Abnormality_Player_Silence.asset");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private void Verify(string label, bool condition)
|
||||
{
|
||||
totalChecks++;
|
||||
if (!condition)
|
||||
{
|
||||
failedChecks++;
|
||||
}
|
||||
|
||||
AppendLine($"{(condition ? "[PASS]" : "[FAIL]")} {label}");
|
||||
}
|
||||
|
||||
private void AppendLine(string text)
|
||||
{
|
||||
if (reportBuilder.Length > 0)
|
||||
{
|
||||
reportBuilder.AppendLine();
|
||||
}
|
||||
|
||||
reportBuilder.Append(text);
|
||||
}
|
||||
|
||||
private bool ShouldEnableRunner()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
return true;
|
||||
#else
|
||||
return Debug.isDebugBuild;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user