Danro/Assets/Scripts/UI/UIRowPointer.cs
Danil Iarosh a40b761c81
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
feat(prizes, ui): add configurable level prize flow and launcher assets
- добавлена новая система призов:
  PrizeBase, PrizeType, PrizeDatabase, LevelPrizeConfig и реализации
  для hopper, QR-кода, отсутствия приза и стойки с призом
- добавлена конфигурация призов уровней через `prizes.json`
  и связанная логика загрузки/обработки в менеджерах игры
- расширен UI главного меню и настроек уровней:
  добавлены LevelSettingsModal, LevelPrizeSettingsModal,
  QRCodeModal и PrizeStandModal
- обновлена навигация модальных окон и связанный UI-код:
  ModalManager, MainMenu, RulesModal, PauseMenu,
  PrizeErrorDisplay, UIRowPointer и WinBannerController
- доработаны настройки и генерация UI-элементов:
  обновлены SettingsDatabase, SettingsUIGenerator,
  SettingUIItem, ValueSelectorRow и editor drawer для настроек
- обновлены и добавлены UI-prefab'ы для кнопок, строк настроек,
  панели групп и кнопки настроек уровня
- обновлены сцены `MainMenu` и `Level UI` под новый флоу
  настройки уровней, выбора призов и отображения модалок
- добавлены файлы лаунчера в `StreamingAssets`,
  включая `RRLauncher.exe`, служебные meta-файлы и документацию
- улучшено логирование через FileLogger
  и удален устаревший код лицензирования
- удалены больше неиспользуемые файлы:
  `Licensing.cs`, `SteppedSlider.cs` и recovery-сцены
2026-03-16 08:15:36 +03:00

146 lines
5.4 KiB
C#

using UnityEngine;
namespace RR.Common.UI
{
/// <summary>
/// Указатель-стрелка, который плавно перемещается к произвольному UI-элементу.
/// Управляется извне через <see cref="SetTarget(RectTransform)"/> или <see cref="SetTarget(int)"/>.
/// </summary>
public class UIRowPointer : MonoBehaviour
{
[Header("Указатель")]
[Tooltip("RectTransform указателя (стрелка, иконка и т.д.).")]
[SerializeField] private RectTransform pointer;
[Header("Цели (опционально)")]
[Tooltip("Фиксированные цели для SetTarget(int). Можно не заполнять, если используется SetTarget(RectTransform).")]
[SerializeField] private RectTransform[] targets;
[Header("Движение")]
[Tooltip("Скорость плавного перемещения. 0 — мгновенно.")]
[SerializeField] private float smoothSpeed = 12f;
[Tooltip("Смещение указателя относительно центра цели (в локальных координатах родителя указателя).")]
[SerializeField] private Vector2 offset;
[Header("Покачивание")]
[Tooltip("Включить покачивание указателя по горизонтали.")]
[SerializeField] private bool wobble = true;
[Tooltip("Амплитуда покачивания (пикселей).")]
[SerializeField] private float wobbleAmplitude = 4f;
[Tooltip("Скорость покачивания (циклов в секунду).")]
[SerializeField] private float wobbleSpeed = 2f;
// ── Состояние ──
private RectTransform _currentTarget;
private Vector2 _targetPos;
private Vector2 _baseX;
private bool _ready;
// ────────────── Публичный API ──────────────
/// <summary>
/// Перемещает указатель к произвольному <see cref="RectTransform"/>.
/// </summary>
public void SetTarget(RectTransform target)
{
if (target == null) return;
_currentTarget = target;
_targetPos = CalculatePosition(target);
if (!_ready)
{
if (pointer) pointer.anchoredPosition = _targetPos;
_ready = true;
}
ShowPointer();
}
/// <summary>
/// Перемещает указатель к цели по индексу из массива <see cref="targets"/>.
/// </summary>
public void SetTarget(int index)
{
if (targets == null || index < 0 || index >= targets.Length) return;
SetTarget(targets[index]);
}
/// <summary>
/// Скрывает указатель.
/// </summary>
public void Hide()
{
if (pointer) pointer.gameObject.SetActive(false);
_currentTarget = null;
}
/// <summary>
/// Задаёт массив целей из кода (если не задан через инспектор).
/// </summary>
public void SetTargets(RectTransform[] newTargets)
{
targets = newTargets;
}
// ────────────── Lifecycle ──────────────
private void Awake()
{
if (pointer) _baseX = new Vector2(pointer.anchoredPosition.x, 0f);
}
private void LateUpdate()
{
if (!pointer || _currentTarget == null) return;
// Пересчитываем позицию (цель может двигаться из-за layout)
_targetPos = CalculatePosition(_currentTarget);
// Плавное перемещение
Vector2 pos = smoothSpeed > 0
? Vector2.Lerp(pointer.anchoredPosition, _targetPos, Time.unscaledDeltaTime * smoothSpeed)
: _targetPos;
// Покачивание
if (wobble)
pos.x += Mathf.Sin(Time.unscaledTime * wobbleSpeed * Mathf.PI * 2f) * wobbleAmplitude;
pointer.anchoredPosition = pos;
}
// ────────────── Внутреннее ──────────────
private Vector2 CalculatePosition(RectTransform target)
{
RectTransform parent = pointer.parent as RectTransform;
if (parent == null) return pointer.anchoredPosition;
Vector3[] corners = new Vector3[4];
target.GetWorldCorners(corners);
Vector3 worldCenter = (corners[0] + corners[2]) * 0.5f;
RectTransformUtility.ScreenPointToLocalPointInRectangle(
parent,
RectTransformUtility.WorldToScreenPoint(null, worldCenter),
null,
out Vector2 localPos
);
return new Vector2(
_baseX.x + offset.x,
localPos.y + offset.y
);
}
private void ShowPointer()
{
if (pointer && !pointer.gameObject.activeSelf)
pointer.gameObject.SetActive(true);
}
}
}