using UnityEngine; public class BallController : MonoBehaviour { [Header("Настройки Движения")] [SerializeField] private float maxSpeed = 15f; // Максимальная скорость на земле [SerializeField] private float defaultAcceleration = 200f; // Как быстро шарик разгоняется [SerializeField] private float defaultDeceleration = 10f; // трение [SerializeField] private float airControlSpeed = 5f; // Скорость управления в прыжке [SerializeField] private float jumpForce = 7f; // Сила прыжка [SerializeField] private float trampolineBounceForce = 1.5f; // Множитель для отскока от батута [SerializeField] private GameObject gameOverPanel; [Header("Настройки Льда")] [SerializeField] private float iceAcceleration = 50f; [SerializeField] private float iceDeceleration = 0.5f; [Header("Настройки Контроля")] [Tooltip("Сила торможения/трения. Применяется, когда нет ввода.")] [SerializeField] private float decelerationRate = 5f; // Сила, замедляющая шарик (трение) [SerializeField] private float groundCheckDistance = 0.1f; // Дистанция для проверки земли private Rigidbody rb; private bool isGrounded; private float sphereRadius; private bool isDead = false; // Внутренние переменные для хранения текущих параметров private float currentAcceleration; private float currentDeceleration; // Переменные для считывания ввода 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; if (hit.collider.CompareTag("Ice")) { currentAcceleration = iceAcceleration; currentDeceleration = iceDeceleration; } else { currentAcceleration = defaultAcceleration; currentDeceleration = defaultDeceleration; } } } } // --- Логика Управления Движением --- 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; // Используем currentAcceleration if (Mathf.Abs(moveX) > 0.01f) { if (Mathf.Abs(currentVel.x) < maxSpeed) { rb.AddForce(new Vector3(moveX, 0, 0) * currentAcceleration, ForceMode.Acceleration); } } if (Mathf.Abs(moveZ) > 0.01f) { if (Mathf.Abs(currentVel.z) < maxSpeed) { rb.AddForce(new Vector3(0, 0, moveZ) * currentAcceleration, ForceMode.Acceleration); } } } private void ApplyDeceleration() { // Используем currentDeceleration Vector3 horizontalVel = new Vector3(rb.linearVelocity.x, 0, rb.linearVelocity.z); if (horizontalVel.sqrMagnitude > 0.01f) { Vector3 frictionForce = -horizontalVel.normalized * currentDeceleration; 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); } public void GameOver() { if (isDead) return; isDead = true; if (rb != null) { rb.linearVelocity = Vector3.zero; rb.isKinematic = true; } if (gameOverPanel != null) { gameOverPanel.SetActive(true); } Time.timeScale = 0f; } }