Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- добавлена новая система призов: 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-сцены
146 lines
5.4 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|