using UnityEngine; [DisallowMultipleComponent] [RequireComponent(typeof(Rigidbody2D), typeof(Collider2D))] public class PlayerController : MonoBehaviour { [Header("Горизонтальное движение")] public float moveSpeed = 8f; public float accel = 40f; public float decel = 20f; [Header("Вертикальное движение")] public float maxJumpHeight = 5f; public float jumpCooldown = 0.1f; public float jumpBuffer = 0.1f; [Range(0f, 1f)] public float jumpCut = 0.5f; private float spriteScale = 0.35f; private Rigidbody2D rb; private Collider2D col; float inputX; float lastGroundedTime; float lastJumpPressedTime; bool IsGrounded => Grounded(); [Header("Аниматор [Опционально]")] [SerializeField] private Animator animator; // оставить в инспекторе на всякий случай 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.GetButtonUp("Jump")) // && rb.linearVelocity.y > 0f) rb.linearVelocity = new Vector2(rb.linearVelocity.x, rb.linearVelocity.y * jumpCut); if (inputX != 0f) transform.localScale = new Vector3(Mathf.Sign(inputX) * spriteScale, spriteScale, spriteScale); 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); } float CalcJumpSpeed(float height) { float g = Physics2D.gravity.y * rb.gravityScale; return Mathf.Sqrt(-2f * g * height); } 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; animator.SetBool("Jump", !isGrounded); return isGrounded; } private void OnTriggerEnter2D(Collider2D collision) { if (collision.CompareTag("Coin")) { GameManager.Instance.AddCoin(); Destroy(collision.gameObject); } if (collision.CompareTag("Finish")) { GameManager.Instance.NextLevel(); } } public void Die() { GameManager.Instance.ResetLevel(); } }