- улучшил структуру уровня 1-3 - пофиксил баг с застреванием персонажа на уровне 1-3 - пофиксил баг с прилипанием игрока к платформе сбоку - изменил логику платформ - добавил больше динамичности в уровень 1-3
138 lines
5.7 KiB
C#
138 lines
5.7 KiB
C#
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 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)) return;
|
||
}
|
||
else
|
||
{
|
||
_currentTargetIndex++;
|
||
if (_currentTargetIndex < coordinates.Length) return;
|
||
|
||
// Возвращаемся в начальную точку
|
||
transform.position = coordinates[0];
|
||
_currentTargetIndex = 0;
|
||
|
||
// Активируем задержку перед следующим циклом
|
||
if (!(startDelay > 0f)) return;
|
||
}
|
||
|
||
_isWaiting = true;
|
||
_delayTimer = startDelay;
|
||
}
|
||
|
||
private void OnCollisionEnter2D(Collision2D other)
|
||
{
|
||
if (!attachPlayer) return;
|
||
if (!other.gameObject.CompareTag("Player")) return;
|
||
|
||
// Проверяем, что игрок стоит на верхней части платформы
|
||
if (IsPlayerOnTop(other))
|
||
{
|
||
other.transform.SetParent(transform);
|
||
}
|
||
}
|
||
|
||
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);
|
||
}
|
||
// Если игрок не прикреплён, но теперь на верхней части - прикрепляем
|
||
else if (other.transform.parent != transform && IsPlayerOnTop(other))
|
||
{
|
||
other.transform.SetParent(transform);
|
||
}
|
||
}
|
||
|
||
private void OnCollisionExit2D(Collision2D other)
|
||
{
|
||
if (!attachPlayer) return;
|
||
if (!other.gameObject.CompareTag("Player")) return;
|
||
|
||
if (other.transform.parent == transform)
|
||
{
|
||
other.transform.SetParent(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);
|
||
}
|
||
}
|
||
} |