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)); } /// /// Проверяет, находится ли игрок на верхней части платформы /// /// Данные о столкновении /// True, если игрок стоит сверху 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; } } }