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

267 lines
11 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.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using RR.Game2D.Core;
namespace RR.Game2D.Entities.Platforms
{
public class HidingTriggeringPlatform : MonoBehaviour
{
[Header("Настройки движения платформы")]
[SerializeField] private float moveSpeed = 3f;
[SerializeField] private float pathLength = 5f;
[SerializeField] private Direction2D moveDirection = Direction2D.Right;
[Header("Настройки матрицы объектов")]
[SerializeField] private int matrixWidth = 5;
[SerializeField] private int matrixHeight = 3;
[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 List<Transform> _platformChildren;
private readonly List<Transform> _hiddenChildren = new ();
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
};
// Собираем все дочерние объекты платформы
_platformChildren = new List<Transform>();
for (int i = 0; i < transform.childCount; i++)
{
_platformChildren.Add(transform.GetChild(i));
}
}
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 (!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);
}
public void Move()
{
StopAllCoroutines();
StartCoroutine(MovePlatformWithHiding(_endPosition, true));
}
public void MoveBack()
{
StopAllCoroutines();
StartCoroutine(MovePlatformWithHiding(_startPosition, false));
}
private IEnumerator MovePlatformWithHiding(Vector3 targetPosition, bool isMovingForward)
{
float distanceTraveled = 0f;
int lastUnitTraveled = 0;
Vector3 previousPosition = transform.position;
while (transform.position != targetPosition)
{
transform.position = Vector3.MoveTowards(
transform.position,
targetPosition,
Time.deltaTime * moveSpeed);
// Вычисляем пройденную дистанцию
distanceTraveled += Vector3.Distance(previousPosition, transform.position);
previousPosition = transform.position;
// Проверяем, прошли ли мы еще один юнит
int currentUnit = Mathf.FloorToInt(distanceTraveled);
if (currentUnit > lastUnitTraveled)
{
if (isMovingForward)
{
HideNextChild();
}
else
{
ShowNextChild();
}
lastUnitTraveled = currentUnit;
}
yield return null;
}
transform.position = targetPosition;
}
/// <summary>
/// Скрывает следующий крайний ряд или колонку в направлении движения
/// </summary>
private void HideNextChild()
{
// Находим видимые объекты
var visibleChildren = _platformChildren.Where(c => c.gameObject.activeSelf).ToList();
if (visibleChildren.Count == 0) return;
// Определяем, скрываем ряд или колонку в зависимости от направления
List<Transform> childrenToHide = moveDirection switch
{
Direction2D.Right => GetRightmostColumn(visibleChildren),
Direction2D.Left => GetLeftmostColumn(visibleChildren),
Direction2D.Up => GetTopmostRow(visibleChildren),
Direction2D.Down => GetBottommostRow(visibleChildren),
_ => new List<Transform>()
};
// Скрываем все объекты в ряду/колонке
foreach (var child in childrenToHide)
{
child.gameObject.SetActive(false);
_hiddenChildren.Add(child);
}
}
/// <summary>
/// Показывает следующий скрытый ряд или колонку (при движении назад)
/// </summary>
private void ShowNextChild()
{
if (_hiddenChildren.Count == 0) return;
// Определяем размер ряда/колонки в зависимости от направления
int countToShow = moveDirection is Direction2D.Right or Direction2D.Left
? matrixHeight
: matrixWidth;
// Показываем последние скрытые объекты (один ряд/колонку)
countToShow = Mathf.Min(countToShow, _hiddenChildren.Count);
for (int i = 0; i < countToShow; i++)
{
int lastIndex = _hiddenChildren.Count - 1;
Transform childToShow = _hiddenChildren[lastIndex];
childToShow.gameObject.SetActive(true);
_hiddenChildren.RemoveAt(lastIndex);
}
}
/// <summary>
/// Получает самую правую колонку объектов
/// </summary>
private static List<Transform> GetRightmostColumn(List<Transform> children)
{
if (children.Count == 0) return new List<Transform>();
// Находим максимальное X
float maxX = children.Max(c => c.position.x);
// Возвращаем все объекты с этим X (колонку)
return children.Where(c => Mathf.Approximately(c.position.x, maxX)).ToList();
}
/// <summary>
/// Получает самую левую колонку объектов
/// </summary>
private static List<Transform> GetLeftmostColumn(List<Transform> children)
{
if (children.Count == 0) return new List<Transform>();
float minX = children.Min(c => c.position.x);
return children.Where(c => Mathf.Approximately(c.position.x, minX)).ToList();
}
/// <summary>
/// Получает самый верхний ряд объектов
/// </summary>
private static List<Transform> GetTopmostRow(List<Transform> children)
{
if (children.Count == 0) return new List<Transform>();
float maxY = children.Max(c => c.position.y);
return children.Where(c => Mathf.Approximately(c.position.y, maxY)).ToList();
}
/// <summary>
/// Получает самый нижний ряд объектов
/// </summary>
private static List<Transform> GetBottommostRow(List<Transform> children)
{
if (children.Count == 0) return new List<Transform>();
float minY = children.Min(c => c.position.y);
return children.Where(c => Mathf.Approximately(c.position.y, minY)).ToList();
}
}
}