165 lines
6.5 KiB
C#
165 lines
6.5 KiB
C#
using UnityEngine;
|
||
|
||
public class BallController : MonoBehaviour
|
||
{
|
||
[Header("Ссылки")]
|
||
private Transform currentPlatform = null;
|
||
|
||
[Header("Настройки Движения")]
|
||
[SerializeField] private float moveSpeed = 15f;
|
||
[SerializeField] private float airControlSpeed = 5f;
|
||
[SerializeField] private float jumpForce = 7f;
|
||
[SerializeField] private float trampolineBounceForce = 1.5f; // Множитель для отскока от батута
|
||
|
||
private Rigidbody rb;
|
||
private bool isGrounded;
|
||
|
||
void Start()
|
||
{
|
||
rb = GetComponent<Rigidbody>();
|
||
rb.freezeRotation = true;
|
||
}
|
||
|
||
void Update()
|
||
{
|
||
HandleJumpInput();
|
||
}
|
||
|
||
void FixedUpdate()
|
||
{
|
||
isGrounded = false;
|
||
HandleMovement();
|
||
}
|
||
|
||
// --- Логика Управления Движением (Без изменений) ---
|
||
|
||
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 (isGrounded) return;
|
||
|
||
if (collision.contacts.Length > 0)
|
||
{
|
||
Vector3 normal = collision.contacts[0].normal;
|
||
if (normal.y > 0.7f) // ~45 градусов
|
||
{
|
||
isGrounded = true;
|
||
}
|
||
}
|
||
}
|
||
} |