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"; [SerializeField] private float _moveSpeed = 4f; private Rigidbody2D _rigidbody; private Animator _animator; private Vector2 _moveInput; private void Awake() { _rigidbody = GetComponent(); _animator = GetComponent(); } public void OnSceneLoaded() { // 씬이 다시 로드돼도 중복 구독되지 않도록 먼저 떼고 붙인다 InputManager.Instance.OnMove_Event -= this.OnMove; InputManager.Instance.OnMove_Event += this.OnMove; } private void OnDestroy() { if (InputManager.Instance != null) { InputManager.Instance.OnMove_Event -= this.OnMove; } } // 입력 콜백은 값을 받아두기만 한다. 실제 이동은 물리 주기에 맞춰 FixedUpdate에서 private void OnMove(Vector2 moveInput) { // 대각선 입력이 (1,1)로 들어오면 더 빨라지므로 길이를 1로 제한 _moveInput = Vector2.ClampMagnitude(moveInput, 1f); if (_moveInput.sqrMagnitude > 0.01f) { // 블렌드 트리는 스프라이트를 보간하지 못한다. 8방향 중 하나로 스냅해서 넣어야 // 한 클립에 가중치가 온전히 실려서 방향 경계에서 깜빡이지 않는다 Vector2 facing = SnapTo8(_moveInput); _animator.SetFloat(MoveXParam, facing.x); _animator.SetFloat(MoveYParam, facing.y); } // 입력이 0이면 MoveX/MoveY를 건드리지 않는다 -> 멈춰도 마지막으로 보던 방향을 유지 _animator.SetFloat(SpeedParam, _moveInput.magnitude); } private void FixedUpdate() { // Dynamic 바디지만 힘을 주지 않고 속도를 직접 대입하므로 관성 없이 딱딱 멈춘다 _rigidbody.linearVelocity = _moveInput * _moveSpeed; } // 입력 방향을 가장 가까운 45도 배수로 스냅한다 (대각선은 0.7071이 되어 블렌드 트리 좌표와 일치) private static Vector2 SnapTo8(Vector2 direction) { float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg; float snapped = Mathf.Round(angle / 45f) * 45f * Mathf.Deg2Rad; return new Vector2(Mathf.Cos(snapped), Mathf.Sin(snapped)); } }