Unity_lab3/Unity3_lab/Assets/Scripts/BallController.cs

222 lines
8.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
public class BallController : MonoBehaviour
{
[Header("Ссылки")]
private Transform currentPlatform = null;
[SerializeField] private GameObject gameOverPanel;
[Header("Настройки Движения")]
[SerializeField] private float moveSpeed = 15f;
[SerializeField] private float airControlSpeed = 5f;
[SerializeField] private float jumpForce = 7f;
[SerializeField] private float trampolineBounceForce = 1.5f; // Множитель для отскока от батута
[SerializeField] private float fallTimeLimit = 10f;
private Rigidbody rb;
private bool isGrounded;
private float timeNotInAir = 0f;
private bool isGameOver = false;
void Start()
{
rb = GetComponent<Rigidbody>();
rb.freezeRotation = true;
// Убедимся, что при старте игры панель выключена
if (gameOverPanel != null)
{
gameOverPanel.SetActive(false);
}
// Сброс TimeScale на случай, если предыдущая игра его остановила
Time.timeScale = 1f;
isGameOver = false;
}
void Update()
{
if (isGameOver) return;
HandleJumpInput();
}
public bool IsGrounded
{
get { return isGrounded; }
}
// --- Логика Управления Движением (Без изменений) ---
private void HandleMovement()
{
float moveX = Input.GetAxis("Horizontal");
float moveZ = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(moveX, 0.0f, moveZ).normalized;
if (currentPlatform != null && isGrounded)
{
// Случай 1: Прилипли к ДВИЖУЩЕЙСЯ платформе (двигаем относительно родителя)
if (movement.magnitude > 0.01f)
{
Vector3 localMovement = currentPlatform.TransformDirection(movement) * moveSpeed * Time.fixedDeltaTime;
rb.MovePosition(rb.position + localMovement);
}
}
else
{
// Случай 2: На СТАТИЧНОЙ земле, БАТУТЕ или в воздухе (используем AddForce)
float currentSpeed = isGrounded ? moveSpeed : airControlSpeed;
rb.AddForce(movement * currentSpeed, ForceMode.Acceleration);
}
}
private void HandleJumpInput()
{
if (Input.GetButtonDown("Jump") && isGrounded)
{
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
isGrounded = false;
transform.SetParent(null);
currentPlatform = null;
}
}
// --- Логика Взаимодействия и Прилипания (Обновленная) ---
private void OnCollisionEnter(Collision collision)
{
CheckIfGrounded(collision);
// 1. Проверяем, является ли объект БАТУТОМ
if (collision.gameObject.CompareTag("Trampoline"))
{
// Случай 3: БАТУТ -> Моментальный отскок
// Если мы уже прилипли к чему-то, отлипаем
if (currentPlatform != null)
{
transform.SetParent(null);
currentPlatform = null;
}
// *** ГЛАВНОЕ ИЗМЕНЕНИЕ: Находим нормаль к поверхности ***
// Получаем нормаль к поверхности в точке столкновения.
// Это и есть направление, перпендикулярное поверхности.
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);
return; // Завершаем проверку, это батут, прилипать не нужно
}
// 2. Проверяем на ДВИЖУЩУЮСЯ ПЛАТФОРМУ
MovingPlatform platformScript = collision.gameObject.GetComponent<MovingPlatform>();
if (platformScript != null)
{
// Случай 1: ДВИЖУЩАЯСЯ ПЛАТФОРМА -> Прилипаем
if (isGrounded)
{
transform.SetParent(collision.transform);
currentPlatform = collision.transform;
}
}
else
{
// Случай 2: СТАТИЧЕСКАЯ ЗЕМЛЯ (обычная неподвижная платформа)
// Гарантируем, что мы не прилипли, если это не MovingPlatform
if (currentPlatform != null)
{
transform.SetParent(null);
currentPlatform = null;
}
}
}
private void OnCollisionStay(Collision collision)
{
CheckIfGrounded(collision);
}
private void OnCollisionExit(Collision collision)
{
// Логика отлипания: Сбрасываем родителя только если покидаем MovingPlatform
MovingPlatform platformScript = collision.gameObject.GetComponent<MovingPlatform>();
if (platformScript != null)
{
transform.SetParent(null);
currentPlatform = null;
}
// Для батута и статики отлипание уже произошло в OnCollisionEnter
}
// Вспомогательный метод для проверки земли
private void CheckIfGrounded(Collision collision)
{
if (collision.contacts.Length > 0)
{
Vector3 normal = collision.contacts[0].normal;
if (normal.y > 0.7f)
{
isGrounded = true;
}
}
}
void FixedUpdate()
{
// Не обрабатываем физику, если игра окончена
if (isGameOver) return;
// Мы проверяем 'isGrounded' из *предыдущего* физического кадра.
if (isGrounded)
{
// Если были на земле, сбрасываем таймер
timeNotInAir = 0f;
}
else
{
// Если в воздухе, увеличиваем таймер
timeNotInAir += Time.fixedDeltaTime;
if (timeNotInAir >= fallTimeLimit)
{
GameOver();
}
}
// Теперь сбрасываем isGrounded.
// OnCollisionStay в этом кадре установит его обратно в 'true',
// если шарик все еще касается земли.
isGrounded = false;
HandleMovement();
}
private void GameOver()
{
if (isGameOver) return;
isGameOver = true;
Time.timeScale = 0f;
if (gameOverPanel != null)
{
gameOverPanel.SetActive(true);
}
}
}