using System.Collections;
using System.Linq;
using UnityEngine;
using RR.Game2D.Core;
namespace RR.Game2D.Entities.Platforms
{
public class TriggeringPlatform : MonoBehaviour
{
[Header("Настройки движения платформы")]
[SerializeField] [Min(0.01f)] private float moveSpeed = 3f;
[SerializeField] private float pathLength = 5f;
[SerializeField] private Direction2D moveDirection = Direction2D.Right;
[Header("Прикрепление игрока")]
[SerializeField] private bool attachPlayer = true;
[Tooltip("Минимальный угол нормали контакта к вертикали для прикрепления (0.7 ≈ 45°)")]
[SerializeField, Range(0f, 1f)] private float normalThreshold = 0.7f;
private Vector3 _startPosition;
private Vector3 _endPosition;
private Rigidbody2D _attachedPlayerRb;
private void Awake()
{
_startPosition = transform.position;
_endPosition = moveDirection switch
{
Direction2D.Down => _startPosition + Vector3.down * pathLength,
Direction2D.Left => _startPosition + Vector3.left * pathLength,
Direction2D.Right => _startPosition + Vector3.right * pathLength,
Direction2D.Up => _startPosition + Vector3.up * pathLength,
_ => _startPosition
};
}
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;
}
private void FixedUpdate()
{
// Если игрок прикреплён к платформе и прыгает (velocity.y > 0),
// сохраняем его velocity в локальном пространстве
if (!_attachedPlayerRb) return;
// Конвертируем мировую скорость игрока в локальную относительно платформы
Vector2 worldVelocity = _attachedPlayerRb.linearVelocity;
Vector2 localVelocity = transform.InverseTransformDirection(worldVelocity);
// Устанавливаем обратно, чтобы избежать конфликта с движением платформы
_attachedPlayerRb.linearVelocity = transform.TransformDirection(localVelocity);
}
///
/// Проверяет, находится ли игрок на верхней части платформы
///
/// Данные о столкновении
/// True, если игрок стоит сверху
private bool IsPlayerOnTop(Collision2D collision)
{
// Проверяем нормали всех контактных точек
// Нормаль направлена от другого объекта (игрока) к платформе,
// поэтому если игрок сверху, нормаль будет направлена вниз
return collision.contacts.Any(contact => Vector2.Dot(contact.normal, Vector2.down) > normalThreshold);
}
public void Move()
{
StopAllCoroutines();
StartCoroutine(MovePlatform(_endPosition));
}
public void MoveBack()
{
StopAllCoroutines();
StartCoroutine(MovePlatform(_startPosition));
}
private IEnumerator MovePlatform(Vector3 targetPosition)
{
while (transform.position != targetPosition)
{
transform.position = Vector3.MoveTowards(
transform.position,
targetPosition,
Time.deltaTime * moveSpeed);
yield return null;
}
transform.position = targetPosition;
}
}
}