216 lines
7.2 KiB
C#
216 lines
7.2 KiB
C#
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<Rigidbody>();
|
|
sphereRadius = GetComponent<SphereCollider>().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;
|
|
}
|
|
}
|