using RR.Game2D.Core; using UnityEngine; using UnityEngine.InputSystem; namespace RR.Game2D.Behaviour { [DisallowMultipleComponent] [RequireComponent(typeof(Rigidbody2D), typeof(Collider2D), typeof(Animator))] public class PlayerController : MonoBehaviour { [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] private float maxJumpHeight = 5f; [SerializeField] private float jumpCooldown = 0.1f; [SerializeField] private float jumpBuffer = 0.1f; [SerializeField, Range(0f, 1f)] private float jumpCut = 0.5f; private Rigidbody2D rb; private Collider2D col; float inputX; float lastGroundedTime; float lastJumpPressedTime; bool IsGrounded => Grounded(); [Header("Аниматор [Опционально]")] [SerializeField] private Animator animator; // оставить в инспекторе на всякий случай 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; lastJumpPressedTime = -999f; // если не задано в инспекторе — находим автоматически if (animator == null) animator = GetComponent(); if (animator == null) animator = GetComponentInChildren(true); if (animator == null) Debug.LogError($"[{name}] Animator не найден!"); } void Update() { //// Передвижение //inputX = Input.GetAxisRaw("Horizontal"); ////if (Input.GetButtonDown("Jump")) //// lastJumpPressedTime = Time.time; //if (Input.GetButton("Jump")) // lastJumpPressedTime = Time.time; //if (Input.GetButtonUp("Jump")) // && rb.linearVelocity.y > 0f) // rb.linearVelocity = new Vector2(rb.linearVelocity.x, rb.linearVelocity.y * jumpCut); Vector2 move = moveAction ? moveAction.action.ReadValue() : Vector2.zero; inputX = Mathf.Abs(move.x) > 0.01f ? Mathf.Sign(move.x) : 0f; if (jumpAction && jumpAction.action.IsPressed()) lastJumpPressedTime = Time.time; if (jumpAction && jumpAction.action.WasReleasedThisFrame()) rb.linearVelocity = new Vector2(rb.linearVelocity.x, rb.linearVelocity.y * jumpCut); if (inputX != 0f) transform.localEulerAngles = Mathf.Sign(inputX) > 0 ? Vector3.zero : new Vector3(0, 180f, 0); if (IsGrounded) lastGroundedTime = Time.time; // Данные для анимаций animator.SetFloat("Speed", Mathf.Abs(rb.linearVelocity.x)); animator.SetFloat("VSpeed", rb.linearVelocity.y); animator.SetBool("Grounded", IsGrounded); } void FixedUpdate() { float target = inputX * moveSpeed; float rate = (Mathf.Abs(target) > 0.01f) ? accel : decel; float vx = Mathf.MoveTowards(rb.linearVelocity.x, target, rate * Time.fixedDeltaTime); if (transform.position.y < -5) Die(); // Возможность прыжка bool canJump = (Time.time - lastGroundedTime) <= jumpCooldown; bool wantJump = (Time.time - lastJumpPressedTime) <= jumpBuffer; if (wantJump && canJump && IsGrounded) { float vy = CalcJumpSpeed(maxJumpHeight); rb.linearVelocity = new Vector2(vx, vy); animator.SetBool("Jump", true); lastJumpPressedTime = -999f; return; } rb.linearVelocity = new Vector2(vx, rb.linearVelocity.y); } private void OnTriggerEnter2D(Collider2D collision) { if (collision.CompareTag("Coin")) { GameManager.Instance.AddCoin(); Destroy(collision.gameObject); } if (collision.CompareTag("Finish")) { GameManager.Instance.NextLevel(); } } //private void OnDestroy() => Debug.Log("Player destroyed"); /// /// Метод для просчитывания скорости прыжка с учетом высоты прыжка /// /// Высота прыжка (высшая точка по y) /// float jumpSpeed - высчитанная скорость прыжка float CalcJumpSpeed(float height) { float g = Physics2D.gravity.y * rb.gravityScale; return Mathf.Sqrt(-2f * g * height); } /// /// Метод для проверки соприкосновения с землей /// /// bool isGrounded - на земле ли игрок bool Grounded() { RaycastHit2D hit = Physics2D.CircleCast(new Vector2(col.bounds.center.x, col.bounds.min.y), 0.01f, Vector2.down, 0.1f); bool isGrounded = hit.collider != null && hit.collider.CompareTag("Ground"); animator.SetBool("Jump", !isGrounded); return isGrounded; } /// /// Метод смерти персонажа. /// Базовое поведение - сброс уровня в GameManager /// public void Die() { GameManager.Instance.ResetLevel(); } } }