using UnityEngine; public class BallController : MonoBehaviour { [Header("Настройки Движения")] [SerializeField] private float maxSpeed = 15f; // Максимальная скорость на земле [SerializeField] private float accelerationRate = 200f; // Как быстро шарик разгоняется [SerializeField] private float airControlSpeed = 5f; // Скорость управления в прыжке [SerializeField] private float jumpForce = 7f; // Сила прыжка [SerializeField] private float trampolineBounceForce = 1.5f; // Множитель для отскока от батута [Header("Настройки Контроля")] [Tooltip("Сила торможения/трения. Применяется, когда нет ввода.")] [SerializeField] private float decelerationRate = 5f; // Сила, замедляющая шарик (трение) [SerializeField] private float groundCheckDistance = 0.1f; // Дистанция для проверки земли private Rigidbody rb; private bool isGrounded; private float sphereRadius; // Переменные для считывания ввода private float moveX; private float moveZ; void Start() { rb = GetComponent(); sphereRadius = GetComponent().radius; } void Update() { // 1. Считываем ввод в Update moveX = Input.GetAxisRaw("Horizontal"); moveZ = Input.GetAxisRaw("Vertical"); HandleJumpInput(); } void FixedUpdate() { CheckGrounded(); HandleMovement(); } // Свойство для проверки состояния "на земле" из других скриптов public bool IsGrounded { get { return isGrounded; } } private void CheckGrounded() { isGrounded = false; Vector3 origin = transform.position; if (Physics.Raycast(origin, -Vector3.up, out RaycastHit hit, sphereRadius + groundCheckDistance)) { if (hit.normal.y > 0.7f) { isGrounded = true; } } } // --- Логика Управления Движением --- private void HandleMovement() { if (isGrounded) { // Определяем, есть ли ввод от игрока bool hasInput = Mathf.Abs(moveX) > 0.01f || Mathf.Abs(moveZ) > 0.01f; if (hasInput) { ApplyAcceleration(); } else { ApplyDeceleration(); } // Ограничение максимальной скорости (применяется всегда, если шарик на земле) ClampHorizontalSpeed(); } else { // Управление в воздухе Vector3 movement = new Vector3(moveX, 0.0f, moveZ).normalized; rb.AddForce(movement * airControlSpeed, ForceMode.Acceleration); } } private void ApplyAcceleration() { Vector3 currentVel = rb.linearVelocity; // 1. Ускорение по X (A/D) if (Mathf.Abs(moveX) > 0.01f) { if (Mathf.Abs(currentVel.x) < maxSpeed) { rb.AddForce(new Vector3(moveX, 0, 0) * accelerationRate, ForceMode.Acceleration); } } // 2. Ускорение по Z (W/S) if (Mathf.Abs(moveZ) > 0.01f) { if (Mathf.Abs(currentVel.z) < maxSpeed) { rb.AddForce(new Vector3(0, 0, moveZ) * accelerationRate, ForceMode.Acceleration); } } } private void ApplyDeceleration() { Vector3 horizontalVel = new Vector3(rb.linearVelocity.x, 0, rb.linearVelocity.z); // Применяем силу, противоположную скорости (имитация трения) if (horizontalVel.sqrMagnitude > 0.01f) { // Направление силы, противоположное скорости Vector3 frictionForce = -horizontalVel.normalized * decelerationRate; rb.AddForce(frictionForce, ForceMode.Acceleration); } else { // Если скорость очень низка, останавливаем горизонтальное движение, чтобы избежать "дрейфа" rb.linearVelocity = new Vector3(0, rb.linearVelocity.y, 0); } } private void ClampHorizontalSpeed() { Vector3 horizontalVel = new Vector3(rb.linearVelocity.x, 0, rb.linearVelocity.z); if (horizontalVel.sqrMagnitude > maxSpeed * maxSpeed) { Vector3 clampedVel = horizontalVel.normalized * maxSpeed; // Устанавливаем горизонтальную скорость, сохраняя вертикальную rb.linearVelocity = new Vector3(clampedVel.x, rb.linearVelocity.y, clampedVel.z); } } private void HandleJumpInput() { if (Input.GetButtonDown("Jump") && isGrounded) { rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse); } } // --- Логика Взаимодействия и Тегирования --- private void OnCollisionEnter(Collision collision) { if (collision.gameObject.CompareTag("Trampoline")) { HandleTrampolineBounce(collision); } } private void HandleTrampolineBounce(Collision collision) { Vector3 surfaceNormal = Vector3.up; if (collision.contacts.Length > 0) { surfaceNormal = collision.contacts[0].normal; } // Обнуляем скорость перед отскоком для стабильности rb.linearVelocity = Vector3.zero; rb.AddForce(surfaceNormal * jumpForce * trampolineBounceForce, ForceMode.Impulse); } }