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 = 200f; // Сила торможения private Rigidbody rb; private bool isGrounded; void Start() { rb = GetComponent(); // УБЕДИСЬ, ЧТО ЭТА СТРОКА ЗАКОММЕНТИРОВАНА ИЛИ УДАЛЕНА! // rb.freezeRotation = true; } void Update() { HandleJumpInput(); } void FixedUpdate() { isGrounded = false; HandleMovement(); } // --- Логика Управления Движением --- private void HandleMovement() { // Используем GetAxisRaw для мгновенного отклика (1, 0, -1) float moveX = Input.GetAxisRaw("Horizontal"); float moveZ = Input.GetAxisRaw("Vertical"); if (isGrounded) { Vector3 currentVel = rb.linearVelocity; Vector3 currentAngularVel = rb.angularVelocity; // --- Управление Осью X (A/D) --- if (Mathf.Abs(moveX) > 0.01f) { // 1. Ускорение по X if (Mathf.Abs(currentVel.x) < maxSpeed) { rb.AddForce(new Vector3(moveX, 0, 0) * accelerationRate, ForceMode.Acceleration); } } else { // 2. Торможение по X (Линейное) // Применяем силу против текущей скорости X float brakeForceX = -currentVel.x; rb.AddForce(new Vector3(brakeForceX, 0, 0) * decelerationRate, ForceMode.Acceleration); // 2. Торможение по X (Угловое - качение по X = вращение по Z) // Применяем крутящий момент против вращения по Z float brakeTorqueZ = -currentAngularVel.z; rb.AddTorque(new Vector3(0, 0, brakeTorqueZ) * decelerationRate, ForceMode.Acceleration); } // --- Управление Осью Z (W/S) --- if (Mathf.Abs(moveZ) > 0.01f) { // 1. Ускорение по Z if (Mathf.Abs(currentVel.z) < maxSpeed) { rb.AddForce(new Vector3(0, 0, moveZ) * accelerationRate, ForceMode.Acceleration); } } else { // 2. Торможение по Z (Линейное) // Применяем силу против текущей скорости Z float brakeForceZ = -currentVel.z; rb.AddForce(new Vector3(0, 0, brakeForceZ) * decelerationRate, ForceMode.Acceleration); // 2. Торможение по Z (Угловое - качение по Z = вращение по X) // Применяем крутящий момент против вращения по X float brakeTorqueX = -currentAngularVel.x; rb.AddTorque(new Vector3(brakeTorqueX, 0, 0) * decelerationRate, ForceMode.Acceleration); } // Ограничение максимальной скорости 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); } } else { // Управление в воздухе Vector3 movement = new Vector3(moveX, 0.0f, moveZ).normalized; rb.AddForce(movement * airControlSpeed, ForceMode.Acceleration); } } private void HandleJumpInput() { if (Input.GetButtonDown("Jump") && isGrounded) { rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse); isGrounded = false; } } // --- Логика Взаимодействия и Тегирования --- private void OnCollisionEnter(Collision collision) { if (collision.gameObject.CompareTag("Trampoline")) { HandleTrampolineBounce(collision); return; } CheckIfGrounded(collision); } private void OnCollisionStay(Collision collision) { CheckIfGrounded(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); } private void CheckIfGrounded(Collision collision) { if (isGrounded) return; if (collision.contacts.Length > 0) { Vector3 normal = collision.contacts[0].normal; if (normal.y > 0.7f) // ~45 градусов { isGrounded = true; } } } }