feat: 플레이어 행동 상태 통합 및 이상상태 디버그 추가

- PlayerActionState로 이동, 점프, 스킬 입력 가능 여부를 통합
- 기절과 침묵 이상상태 데이터 및 효과 에셋을 추가
- 로컬 플레이어용 이상상태 디버그 HUD와 자동 검증 러너를 추가
- 플레이어 이동, 스킬 입력, 네트워크 상태를 새 행동 상태 기준으로 정리
This commit is contained in:
2026-03-19 18:21:39 +09:00
parent d39e13f032
commit e863d108da
23 changed files with 1286 additions and 25 deletions

View File

@@ -12,6 +12,7 @@ namespace Colosseum.Player
/// - 서버: NetworkVariable을 읽어 CharacterController 구동 → NetworkTransform으로 동기화
/// </summary>
[RequireComponent(typeof(CharacterController))]
[RequireComponent(typeof(PlayerActionState))]
public class PlayerMovement : NetworkBehaviour
{
[Header("Movement Settings")]
@@ -25,6 +26,7 @@ namespace Colosseum.Player
[Header("References")]
[SerializeField] private SkillController skillController;
[SerializeField] private Animator animator;
[SerializeField] private PlayerActionState actionState;
private CharacterController characterController;
private Vector3 velocity;
@@ -43,7 +45,7 @@ namespace Colosseum.Player
private Vector3 blockedDirection;
private readonly Collider[] overlapBuffer = new Collider[8];
public float CurrentMoveSpeed => netMoveInput.Value.magnitude * moveSpeed;
public float CurrentMoveSpeed => netMoveInput.Value.magnitude * moveSpeed * GetMoveSpeedMultiplier();
public bool IsGrounded => characterController != null ? characterController.isGrounded : false;
public bool IsJumping => isJumping;
@@ -62,6 +64,8 @@ namespace Colosseum.Player
skillController = GetComponent<SkillController>();
if (animator == null)
animator = GetComponentInChildren<Animator>();
if (actionState == null)
actionState = GetOrCreateActionState();
SetSpawnPosition();
}
@@ -69,6 +73,9 @@ namespace Colosseum.Player
// 오너: 입력 및 카메라 초기화
if (IsOwner)
{
if (actionState == null)
actionState = GetOrCreateActionState();
InitializeInputActions();
SetupCamera();
}
@@ -144,6 +151,8 @@ namespace Colosseum.Player
private void OnJumpPerformed(InputAction.CallbackContext context)
{
if (!IsOwner) return;
if (actionState != null && !actionState.CanJump) return;
JumpRequestRpc();
}
@@ -153,6 +162,9 @@ namespace Colosseum.Player
[Rpc(SendTo.Server)]
private void JumpRequestRpc()
{
if (actionState != null && !actionState.CanJump)
return;
if (!isJumping && characterController != null && characterController.isGrounded)
Jump();
}
@@ -187,6 +199,14 @@ namespace Colosseum.Player
/// </summary>
private void UpdateNetworkInput()
{
if (actionState != null && !actionState.CanMove)
{
if (netMoveInput.Value != Vector2.zero)
netMoveInput.Value = Vector2.zero;
return;
}
Vector3 dir = new Vector3(moveInput.x, 0f, moveInput.y);
if (dir.sqrMagnitude > 0.001f)
dir = TransformDirectionByCamera(dir).normalized;
@@ -251,10 +271,14 @@ namespace Colosseum.Player
if (moveDirection.sqrMagnitude > 0.001f)
moveDirection.Normalize();
if (actionState != null && !actionState.CanMove)
moveDirection = Vector3.zero;
if (blockedDirection != Vector3.zero && Vector3.Dot(moveDirection, blockedDirection) > 0f)
moveDirection = Vector3.zero;
characterController.Move((moveDirection * moveSpeed + velocity) * Time.deltaTime);
float actualMoveSpeed = moveSpeed * GetMoveSpeedMultiplier();
characterController.Move((moveDirection * actualMoveSpeed + velocity) * Time.deltaTime);
if (moveDirection != Vector3.zero)
{
@@ -291,6 +315,23 @@ namespace Colosseum.Player
return right * direction.x + forward * direction.z;
}
private float GetMoveSpeedMultiplier()
{
if (actionState == null)
return 1f;
return actionState.MoveSpeedMultiplier;
}
private PlayerActionState GetOrCreateActionState()
{
var foundState = GetComponent<PlayerActionState>();
if (foundState != null)
return foundState;
return gameObject.AddComponent<PlayerActionState>();
}
/// <summary>
/// 루트 모션 처리 (서버 전용 — NetworkTransform으로 동기화)
/// </summary>