Unity_lab3/Unity3_lab/Assets/Scripts/BallController.cs
2025-11-13 20:56:00 +03:00

178 lines
6.2 KiB
C#

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<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;
}
}
}
// --- Логика Управления Движением ---
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);
}
}