Danro-Jump/Assets/Scripts/Runtime/Gameplay/OneWayPlatformCollider.cs
Danil Iarosh c3bb250769 Integrate COM logging HUD fixes and hopper dispensing
Подробный список изменений:
- Добавлено файловое логирование игровых сессий в game.log и расширена диагностика COM-подключений.
- Подключен native COM transport с fallback-логикой для IL2CPP и добавлен плагин ComNative.dll.
- Исправлена логика one-way платформ, чтобы игрок проходил сквозь платформы при нахождении ниже их верхней точки.
- Переработана компоновка gameplay HUD: верхние плашки выровнены, левая уменьшена, центральная плашка очков центрирована и масштабирована.
- Добавлена общая выдача призов через хоппер с командами запуска/остановки мотора и ожиданием датчика.
- Подключен сервисный тест хоппера в настройках и обновлены связанные тесты.
- Добавлены скриншоты визуальной проверки HUD.
2026-07-06 16:47:27 +03:00

182 lines
6.8 KiB
C#
Raw Permalink 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 UnityEngine;
using Zenject;
namespace DanroJump.Gameplay
{
/// <summary>
/// Кастомная one-way логика платформы.
/// Игнорирует столкновение, если нижняя точка игрока ниже верхней границы платформы.
/// </summary>
[DisallowMultipleComponent]
[RequireComponent(typeof(Collider2D))]
public sealed class OneWayPlatformCollider : MonoBehaviour
{
[SerializeField] private JumpPlayerController player;
[SerializeField] private float topBoundaryPadding;
[SerializeField] private float landingVelocityThreshold = 0.05f;
private Collider2D platformCollider;
private Collider2D playerCollider;
private float cachedTopY;
private Vector3 lastPosition;
private bool isIgnoringPlayerCollision;
/// <summary>
/// Y-координата верхней границы коллайдера платформы (кэшированная).
/// </summary>
public float TopY => cachedTopY;
/// <summary>
/// Вычисляет и сохраняет Y-координату верхней границы платформы.
/// </summary>
public void UpdateCachedTopY()
{
cachedTopY = platformCollider != null ? platformCollider.bounds.max.y : transform.position.y;
}
/// <summary>
/// Получает игрока через DI, чтобы не искать его по сцене на каждой платформе.
/// </summary>
[Inject]
public void Construct(JumpPlayerController injectedPlayer)
{
player = injectedPlayer;
ResolvePlayerCollider();
}
/// <summary>
/// Подготавливает Collider2D и отключает стандартный PlatformEffector2D, если он остался на prefab.
/// </summary>
private void Awake()
{
platformCollider = GetComponent<Collider2D>();
platformCollider.usedByEffector = false;
if (TryGetComponent(out PlatformEffector2D platformEffector))
{
platformEffector.enabled = false;
}
UpdateCachedTopY();
lastPosition = transform.position;
RefreshColliderState();
}
/// <summary>
/// После повторного включения сразу синхронизирует IgnoreCollision, не ожидая следующего FixedUpdate.
/// </summary>
private void OnEnable()
{
UpdateCachedTopY();
lastPosition = transform.position;
RefreshColliderState();
}
/// <summary>
/// При выключении платформы обязательно возвращает столкновение с игроком.
/// </summary>
private void OnDisable()
{
SetPlayerCollisionIgnored(false);
}
/// <summary>
/// Синхронизирует состояние IgnoreCollision с текущей позицией GroundCheck игрока.
/// </summary>
private void FixedUpdate()
{
if (player == null && !ResolvePlayer())
{
return;
}
// Если платформа переместилась (например, была рециклирована), принудительно обновляем кэш
if (transform.position != lastPosition)
{
UpdateCachedTopY();
lastPosition = transform.position;
}
// Оптимизация: если игрок взлетает вверх, коллизия гарантированно игнорируется.
// Это избавляет от выполнения сложных вычислений расстояний для всех платформ.
var isFalling = player.VerticalVelocity <= landingVelocityThreshold ||
player.PreviousVerticalVelocity <= landingVelocityThreshold;
if (!isFalling)
{
SetPlayerCollisionIgnored(true);
return;
}
RefreshColliderState();
}
/// <summary>
/// Обновляет режим столкновения между платформой и игроком.
/// </summary>
private void RefreshColliderState()
{
if (playerCollider == null && !ResolvePlayerCollider())
{
return;
}
SetPlayerCollisionIgnored(!ShouldCollideWith(player));
}
/// <summary>
/// Проверяет, должен ли игрок столкнуться с платформой в текущем физическом шаге.
/// </summary>
/// <param name="targetPlayer">Игрок, для которого проверяется столкновение.</param>
/// <returns>True, если нижняя точка игрока не ниже верхней границы платформы.</returns>
public bool ShouldCollideWith(JumpPlayerController targetPlayer)
{
if (targetPlayer == null)
{
return false;
}
var topY = cachedTopY + Mathf.Max(0f, topBoundaryPadding);
var playerLowestY = playerCollider != null ? playerCollider.bounds.min.y : targetPlayer.GroundCheckY;
return playerLowestY >= topY;
}
/// <summary>
/// Возвращает true, если ссылка на игрока уже получена.
/// </summary>
private bool ResolvePlayer()
{
return player != null;
}
/// <summary>
/// Кэширует коллайдер игрока для Physics2D.IgnoreCollision.
/// </summary>
private bool ResolvePlayerCollider()
{
if (player == null)
{
return false;
}
return player.TryGetComponent(out playerCollider);
}
/// <summary>
/// Включает или выключает физическое столкновение пары игрок-платформа.
/// </summary>
private void SetPlayerCollisionIgnored(bool shouldIgnore)
{
if (platformCollider == null || playerCollider == null || isIgnoringPlayerCollision == shouldIgnore)
{
return;
}
Physics2D.IgnoreCollision(platformCollider, playerCollider, shouldIgnore);
isIgnoringPlayerCollision = shouldIgnore;
}
}
}