Danro/Assets/Scripts/Entities/Props/Platforms/TrajectoryPlatform.cs
Danil Iarosh ebc768d7e1 some bugfixes
- Небольшие багфиксы
2025-12-03 16:16:42 +03:00

154 lines
6.8 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 System.Linq;
using UnityEngine;
namespace RR.Game2D.Entities.Platforms
{
public class TrajectoryPlatform : MonoBehaviour
{
[Header("Настройки движения платформы")]
[SerializeField] private float moveSpeed = 1f;
[SerializeField] private Vector3[] coordinates;
[SerializeField] private bool loop = true;
[Header("Прикрепление игрока")]
[SerializeField] private bool attachPlayer = true;
[Tooltip("Минимальный угол нормали контакта к вертикали для прикрепления (0.7 ≈ 45°)")]
[SerializeField, Range(0f, 1f)] private float normalThreshold = 0.7f;
[Header("Задержка перед началом движения (в секундах)")]
[SerializeField] private float startDelay;
private int _currentTargetIndex;
private float _delayTimer;
private bool _isWaiting;
private Rigidbody2D _attachedPlayerRb;
private void Start()
{
// Если задержка задана, начинаем с ожидания
if (!(startDelay > 0f)) return;
_isWaiting = true;
_delayTimer = startDelay;
}
private void FixedUpdate()
{
if (coordinates == null || coordinates.Length == 0) return;
// Если платформа в режиме ожидания, уменьшаем таймер
if (_isWaiting)
{
_delayTimer -= Time.fixedDeltaTime;
if (_delayTimer <= 0f)
{
_isWaiting = false;
}
else
{
return; // пока ждём, не двигаемся
}
}
// Определяем целевую точку
Vector3 targetPoint = coordinates[_currentTargetIndex];
// Двигаем платформу к целевой точке
transform.position = Vector3.MoveTowards(transform.position, targetPoint, moveSpeed * Time.fixedDeltaTime);
// Проверяем, достигли ли целевой точки
if (!(Vector3.Distance(transform.position, targetPoint) < 0.01f)) return;
if (loop)
{
// Режим зацикливания: переходим к следующей точке по кругу
_currentTargetIndex = (_currentTargetIndex + 1) % coordinates.Length;
// Если вернулись в начальную точку (индекс 0), активируем задержку
if (_currentTargetIndex == 0 && startDelay > 0f)
{
_isWaiting = true;
_delayTimer = startDelay;
}
}
else
{
_currentTargetIndex++;
if (_currentTargetIndex >= coordinates.Length)
{
// Возвращаемся в начальную точку
transform.position = coordinates[0];
_currentTargetIndex = 0;
// Активируем задержку перед следующим циклом
if (startDelay > 0f)
{
_isWaiting = true;
_delayTimer = startDelay;
}
}
}
// Если игрок прикреплён к платформе, корректируем его velocity
if (!_attachedPlayerRb) return;
// Конвертируем мировую скорость игрока в локальную относительно платформы
Vector2 worldVelocity = _attachedPlayerRb.linearVelocity;
Vector2 localVelocity = transform.InverseTransformDirection(worldVelocity);
// Устанавливаем обратно, чтобы избежать конфликта с движением платформы
_attachedPlayerRb.linearVelocity = transform.TransformDirection(localVelocity);
}
private void OnCollisionEnter2D(Collision2D other)
{
if (!attachPlayer) return;
if (!other.gameObject.CompareTag("Player")) return;
// Проверяем, что игрок стоит на верхней части платформы
if (!IsPlayerOnTop(other)) return;
other.transform.SetParent(transform);
_attachedPlayerRb = other.rigidbody;
}
private void OnCollisionStay2D(Collision2D other)
{
if (!attachPlayer) return;
if (!other.gameObject.CompareTag("Player")) return;
// Если игрок уже прикреплён, но больше не на верхней части - отсоединяем
if (other.transform.parent == transform && !IsPlayerOnTop(other))
{
other.transform.SetParent(null);
_attachedPlayerRb = null;
}
// Если игрок не прикреплён, но теперь на верхней части - прикрепляем
else if (other.transform.parent != transform && IsPlayerOnTop(other))
{
other.transform.SetParent(transform);
_attachedPlayerRb = other.rigidbody;
}
}
private void OnCollisionExit2D(Collision2D other)
{
if (!attachPlayer) return;
if (!other.gameObject.CompareTag("Player")) return;
if (other.transform.parent != transform) return;
other.transform.SetParent(null);
_attachedPlayerRb = null;
}
/// <summary>
/// Проверяет, находится ли игрок на верхней части платформы
/// </summary>
/// <param name="collision">Данные о столкновении</param>
/// <returns>True, если игрок стоит сверху</returns>
private bool IsPlayerOnTop(Collision2D collision)
{
// Проверяем нормали всех контактных точек
// Нормаль направлена от другого объекта (игрока) к платформе,
// поэтому если игрок сверху, нормаль будет направлена вниз
return collision.contacts.Any(contact => Vector2.Dot(contact.normal, Vector2.down) > normalThreshold);
}
}
}