126 lines
4.9 KiB
C#
126 lines
4.9 KiB
C#
using UnityEngine;
|
|
|
|
[RequireComponent(typeof(Rigidbody2D))]
|
|
[RequireComponent(typeof(Animator))]
|
|
public class PlayerController : MonoBehaviour,ISceneInitializable
|
|
{
|
|
// RedAnimController의 파라미터 이름과 정확히 일치해야 한다
|
|
private const string MoveXParam = "MoveX";
|
|
private const string MoveYParam = "MoveY";
|
|
private const string SpeedParam = "Speed";
|
|
private const string AttackParam = "Attack";
|
|
private const string AttackXParam = "AttackX";
|
|
private const string AttackYParam = "AttackY";
|
|
|
|
// 방향은 8개로 고정이다. 각도가 아니라 이 중 하나로 분류해서 들고 다닌다
|
|
private enum Direction
|
|
{
|
|
Right, UpRight, Up, UpLeft, Left, DownLeft, Down, DownRight
|
|
}
|
|
|
|
// 블렌드 트리에 박아둔 자식 좌표와 같은 값이어야 가중치가 그 클립에 온전히 실린다
|
|
private static readonly Vector2[] DirectionVectors =
|
|
{
|
|
new Vector2( 1f, 0f), // Right
|
|
new Vector2( 0.7071f, 0.7071f), // UpRight
|
|
new Vector2( 0f, 1f), // Up
|
|
new Vector2(-0.7071f, 0.7071f), // UpLeft
|
|
new Vector2(-1f, 0f), // Left
|
|
new Vector2(-0.7071f, -0.7071f), // DownLeft
|
|
new Vector2( 0f, -1f), // Down
|
|
new Vector2( 0.7071f, -0.7071f) // DownRight
|
|
};
|
|
|
|
// 오른손으로 휘두르므로 정면을 볼 땐 시계방향 45도 쪽을 벤다.
|
|
// 대각선을 볼 땐 그대로 (Slash 시트가 대각선 4장뿐이라 정면 그림 자체가 없다)
|
|
private static readonly Direction[] AttackDirections =
|
|
{
|
|
Direction.DownRight, // Right -> DownRight
|
|
Direction.UpRight, // UpRight -> UpRight
|
|
Direction.UpRight, // Up -> UpRight
|
|
Direction.UpLeft, // UpLeft -> UpLeft
|
|
Direction.UpLeft, // Left -> UpLeft
|
|
Direction.DownLeft, // DownLeft -> DownLeft
|
|
Direction.DownLeft, // Down -> DownLeft
|
|
Direction.DownRight // DownRight -> DownRight
|
|
};
|
|
|
|
[SerializeField] private float _moveSpeed = 4f;
|
|
|
|
private Rigidbody2D _rigidbody;
|
|
private Animator _animator;
|
|
private Vector2 _moveInput;
|
|
private Direction _facing = Direction.Down;
|
|
|
|
private void Awake()
|
|
{
|
|
_rigidbody = GetComponent<Rigidbody2D>();
|
|
_animator = GetComponent<Animator>();
|
|
}
|
|
|
|
public void OnSceneLoaded()
|
|
{
|
|
// 씬이 다시 로드돼도 중복 구독되지 않도록 먼저 떼고 붙인다
|
|
InputManager.Instance.OnMove_Event -= this.OnMove;
|
|
InputManager.Instance.OnMove_Event += this.OnMove;
|
|
InputManager.Instance.OnAttack_Event -= this.OnAttack;
|
|
InputManager.Instance.OnAttack_Event += this.OnAttack;
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
if (InputManager.Instance != null)
|
|
{
|
|
InputManager.Instance.OnMove_Event -= this.OnMove;
|
|
InputManager.Instance.OnAttack_Event -= this.OnAttack;
|
|
}
|
|
}
|
|
|
|
// 입력 콜백은 값을 받아두기만 한다. 실제 이동은 물리 주기에 맞춰 FixedUpdate에서
|
|
private void OnMove(Vector2 moveInput)
|
|
{
|
|
// 대각선 입력이 (1,1)로 들어오면 더 빨라지므로 길이를 1로 제한
|
|
_moveInput = Vector2.ClampMagnitude(moveInput, 1f);
|
|
|
|
if (_moveInput.sqrMagnitude > 0.01f)
|
|
{
|
|
_facing = ToDirection(_moveInput);
|
|
|
|
Vector2 facingVector = DirectionVectors[(int)_facing];
|
|
_animator.SetFloat(MoveXParam, facingVector.x);
|
|
_animator.SetFloat(MoveYParam, facingVector.y);
|
|
}
|
|
// 입력이 0이면 방향을 갱신하지 않는다 -> 멈춰도 마지막으로 보던 쪽을 유지
|
|
|
|
_animator.SetFloat(SpeedParam, _moveInput.magnitude);
|
|
}
|
|
|
|
private void OnAttack()
|
|
{
|
|
// Slash 블렌드 트리는 MoveX/MoveY가 아니라 AttackX/AttackY를 읽는다.
|
|
// 그래서 공격이 끝나도 바라보던 방향은 그대로 남는다
|
|
Vector2 attackVector = DirectionVectors[(int)AttackDirections[(int)_facing]];
|
|
_animator.SetFloat(AttackXParam, attackVector.x);
|
|
_animator.SetFloat(AttackYParam, attackVector.y);
|
|
_animator.SetTrigger(AttackParam);
|
|
}
|
|
|
|
private void FixedUpdate()
|
|
{
|
|
// Dynamic 바디지만 힘을 주지 않고 속도를 직접 대입하므로 관성 없이 딱딱 멈춘다
|
|
_rigidbody.linearVelocity = _moveInput * _moveSpeed;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 입력 각도를 45도씩 8개 구간으로 나눠 분류한다.
|
|
/// 예를 들어 Up은 90도 한 점이 아니라 67.5 ~ 112.5도 구간 전체다.
|
|
/// </summary>
|
|
private static Direction ToDirection(Vector2 input)
|
|
{
|
|
float angle = Mathf.Atan2(input.y, input.x) * Mathf.Rad2Deg;
|
|
int sector = Mathf.RoundToInt(angle / 45f);
|
|
|
|
return (Direction)((sector + 8) % 8); // -180도는 -4 -> 4(Left)로 접힌다
|
|
}
|
|
}
|