129 lines
5.6 KiB
C#
129 lines
5.6 KiB
C#
using System.Collections;
|
||
using System.Linq;
|
||
using RR.Game2D.Core;
|
||
using UnityEngine;
|
||
|
||
namespace RR.Game2D.Entities.Platforms
|
||
{
|
||
public class ShiftingPlatform : MonoBehaviour
|
||
{
|
||
[Header("Настройки смещения платформы")]
|
||
[SerializeField] private Vector3 shiftAmount = new Vector3(0f, 2f, 0f);
|
||
[SerializeField] private float shiftDuration = 1f;
|
||
[SerializeField] private Direction2D shiftDirection = Direction2D.Down;
|
||
|
||
[Header("Прикрепление игрока")]
|
||
[SerializeField] private bool attachPlayer = true;
|
||
[Tooltip("Минимальный угол нормали контакта к вертикали для прикрепления (0.7 ≈ 45°)")]
|
||
[SerializeField, Range(0f, 1f)] private float normalThreshold = 0.7f;
|
||
|
||
private Rigidbody2D _attachedPlayerRb;
|
||
|
||
private void FixedUpdate()
|
||
{
|
||
// Если игрок прикреплён к платформе, корректируем его velocity
|
||
if (!_attachedPlayerRb) return;
|
||
// Конвертируем мировую скорость игрока в локальную относительно платформы
|
||
Vector2 worldVelocity = _attachedPlayerRb.linearVelocity;
|
||
Vector2 localVelocity = transform.InverseTransformDirection(worldVelocity);
|
||
|
||
// Устанавливаем обратно, чтобы избежать конфликта с движением платформы
|
||
_attachedPlayerRb.linearVelocity = transform.TransformDirection(localVelocity);
|
||
}
|
||
|
||
private void OnCollisionEnter2D(Collision2D other)
|
||
{
|
||
if (!other.gameObject.CompareTag("Player")) return;
|
||
|
||
// Проверяем, что игрок стоит на верхней части платформы
|
||
if (attachPlayer && IsPlayerOnTop(other))
|
||
{
|
||
other.transform.SetParent(transform);
|
||
_attachedPlayerRb = other.rigidbody;
|
||
}
|
||
|
||
Vector3 shiftVector = shiftDirection switch
|
||
{
|
||
Direction2D.Down => Vector3.down * shiftAmount.y,
|
||
Direction2D.Left => Vector3.left * shiftAmount.x,
|
||
Direction2D.Right => Vector3.right * shiftAmount.x,
|
||
Direction2D.Up => Vector3.up * shiftAmount.y,
|
||
_ => Vector3.zero
|
||
};
|
||
|
||
StopAllCoroutines();
|
||
StartCoroutine(ShiftPlatform(shiftVector));
|
||
}
|
||
|
||
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 (!other.gameObject.CompareTag("Player")) return;
|
||
|
||
if (attachPlayer && other.transform.parent == transform)
|
||
{
|
||
other.transform.SetParent(null);
|
||
_attachedPlayerRb = null;
|
||
}
|
||
|
||
Vector3 shiftVector = shiftDirection switch
|
||
{
|
||
Direction2D.Down => Vector3.up * shiftAmount.y,
|
||
Direction2D.Left => Vector3.right * shiftAmount.x,
|
||
Direction2D.Right => Vector3.left * shiftAmount.x,
|
||
Direction2D.Up => Vector3.down * shiftAmount.y,
|
||
_ => Vector3.zero
|
||
};
|
||
|
||
StopAllCoroutines();
|
||
StartCoroutine(ShiftPlatform(shiftVector));
|
||
}
|
||
|
||
/// <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);
|
||
}
|
||
|
||
private IEnumerator ShiftPlatform(Vector3 shiftVector)
|
||
{
|
||
Vector3 initialPosition = transform.position;
|
||
Vector3 finalPosition = initialPosition + shiftVector;
|
||
float elapsedTime = 0f;
|
||
|
||
while (elapsedTime < shiftDuration)
|
||
{
|
||
transform.position = Vector3.Lerp(initialPosition, finalPosition, (elapsedTime / shiftDuration));
|
||
elapsedTime += Time.deltaTime;
|
||
yield return null;
|
||
}
|
||
|
||
transform.position = finalPosition;
|
||
}
|
||
}
|
||
} |