using UnityEngine; using UnityEngine.InputSystem; using RR.Common.Interfaces; using RR.Game2D.Core; namespace RR.Game2D.Behaviour { [DisallowMultipleComponent] [RequireComponent(typeof(Rigidbody2D), typeof(Collider2D), typeof(Animator))] public class PlayerController : MonoBehaviour, IDamageable { [Header("Управление")] [SerializeField] private InputActionReference moveAction; // Value(Vector2), например "Move" (A/D, ←/→, stick) [SerializeField] private InputActionReference jumpAction; [Header("Горизонтальное движение")] [SerializeField] private float moveSpeed = 8f; [SerializeField] private float accel = 40f; [SerializeField] private float decel = 20f; [Header("Вертикальное движение")] [SerializeField] public float jumpVelocity = 50f; [SerializeField] private float jumpCut = 2f; [Header("Проверка призмеления")] [SerializeField] private Transform groundCheck; [SerializeField] private float groundCheckRadius = 0.05f; [SerializeField] private LayerMask groundMask; [Header("Аниматор [Опционально]")] [SerializeField] private Animator animator; // оставить в инспекторе на всякий случай [Header("Здоровье")] [SerializeField] private int maxHealth = 3; private Health health; public int CurrentHealth => health?.CurrentHealth ?? 0; public int MaxHealth => health?.MaxHealth ?? 0; private Rigidbody2D rb; //private Collider2D col; private float inputX; private bool jumpPressed, jumpReleased, jumpHold; private float _fallSpeedYDampingChangeThreshold; private bool IsGrounded => Grounded(); // Аниматор private static readonly int Jump = Animator.StringToHash("Jump"); private static readonly int Speed = Animator.StringToHash("Speed"); private static readonly int VSpeed = Animator.StringToHash("VSpeed"); private static readonly int GroundedAnimator = Animator.StringToHash("Grounded"); void OnEnable() { moveAction.action.Enable(); jumpAction.action.Enable(); } void OnDisable() { moveAction.action.Disable(); jumpAction.action.Disable(); } void Awake() { rb = GetComponent(); //col = GetComponent(); rb.freezeRotation = true; _fallSpeedYDampingChangeThreshold = CameraManager.instance._fallSpeedYDampingChangeThreshold; // если не задано в инспекторе — находим автоматически if (animator == null) animator = GetComponent(); if (animator == null) animator = GetComponentInChildren(true); if (animator == null) Debug.LogError($"[{name}] Animator не найден!"); health = GetComponent(); if (health == null) { health = gameObject.AddComponent(); } health.ResetHealth(); health.SetOwner(this); health.SetMaxHealth(maxHealth); } void Update() { // --- Ввод --- Vector2 move = moveAction ? moveAction.action.ReadValue() : Vector2.zero; inputX = Mathf.Abs(move.x) > 0.01f ? Mathf.Sign(move.x) : 0f; jumpPressed |= jumpAction.action.WasPressedThisFrame(); jumpReleased |= jumpAction.action.WasReleasedThisFrame(); jumpHold |= jumpAction.action.IsPressed(); if (inputX != 0f) transform.localEulerAngles = Mathf.Sign(inputX) > 0 ? Vector3.zero : new Vector3(0, 180f, 0); if (rb.linearVelocity.y < _fallSpeedYDampingChangeThreshold && !CameraManager.instance.IsLerpingYDamping && !CameraManager.instance.LerpedFromPlayerFalling) { CameraManager.instance.LerpYDamping(true); } if (rb.linearVelocity.y >= 0 && !CameraManager.instance.IsLerpingYDamping && CameraManager.instance.LerpedFromPlayerFalling) { CameraManager.instance.LerpedFromPlayerFalling = false; CameraManager.instance.LerpYDamping(false); } // --- Анимации --- animator.SetFloat(Speed, Mathf.Abs(rb.linearVelocity.x)); animator.SetFloat(VSpeed, rb.linearVelocity.y); animator.SetBool(GroundedAnimator, IsGrounded); } private void FixedUpdate() { // Горизонтальное движение float target = inputX * moveSpeed; float rate = (Mathf.Abs(target) > 0.01) ? accel : decel; float velocityX = Mathf.MoveTowards(rb.linearVelocity.x, target, rate * Time.fixedDeltaTime); Vector2 newVelocity = rb.linearVelocity; newVelocity.x = velocityX; // Вертикальное движение if ((jumpPressed || jumpHold)&& IsGrounded) newVelocity.y = jumpVelocity; if (jumpReleased && newVelocity.y > 0) newVelocity.y /= Mathf.Max(1f, jumpCut); rb.linearVelocity = newVelocity; jumpPressed = jumpReleased = jumpHold = false; // Смерть // if (transform.position.y < deathY) Die(); } private void OnTriggerEnter2D(Collider2D collision) { if (collision.CompareTag("Coin")) { GameManager.Instance.AddCoin(); Destroy(collision.gameObject); } if (collision.CompareTag("Finish")) { GameManager.Instance.NextLevel(); } if (collision.CompareTag("Enemy")) { TakeDamage(1); } } public void TakeDamage(int amount) { health.TakeDamage(amount); } public void Heal(int amount) { health.Heal(amount); } public void Die() { GameManager.Instance.ResetLevel(); } /// /// Метод для проверки соприкосновения с землей /// /// bool isGrounded - на земле ли игрок bool Grounded() { Collider2D hit = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundMask); bool isGrounded = hit != null && hit.CompareTag("Ground"); //&& rb.linearVelocity.y < 0.01f; animator.SetBool(Jump, !isGrounded); return isGrounded; } } }