Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- Интегрирована система кастомных визуальных эффектов (VFX): - Добавлена новая утилита MvpVFXUtility и настройки MvpVFXSpawnSettings для спавна эффектов. - Реализован пул эффектов VFXPool с поддержкой отслеживания версий спавна, сброса оверрайдов и анимации масштаба по времени. - Добавлены эффекты прыжка и приземления в PlayerAnimationEvents. - Интегрирован эффект DeathVignetteController для плавного затемнения экрана при смерти игрока. - Добавлены частицы stompImpactVFX при уничтожении врагов прыжком сверху (EnemyController). - Реализован эффект конфетти finishVFX при успешном завершении уровня (LevelFinish). - Интегрированы эффекты искр при сборе монеток, кристаллов и других собираемых предметов (Collectible, EnvironmentalCollectibleBase). - Улучшен и визуально доработан пользовательский интерфейс (UI): - Добавлен скрипт QRCodeTierHighlight для цветовой подсветки QR-кодов в зависимости от тира приза. - Реализована пульсация блока приза в баннере победы (WinBannerController). - Обновлено обозначение валюты в BalanceDisplay с "₽" на "руб.". - Добавлены новые префабы, сцены, ассеты тайлсетов и шейдеров для настройки визуального стиля. - Обновлён протокол и инициализация подсветки COM-платы: - Изменен префикс команд подсветки с 85/86 на 8550, а диапазон эффектов обновлен с 40...52 на 10...22 (LightController, LightMode, LightCycleDurations). - Исправлена хардкод-команда сброса подсветки при инициализации в ComDeviceClient с устаревшей 8600 на новую 8550 для предотвращения рассинхронизации буфера обмена платы.
230 lines
7.8 KiB
C#
230 lines
7.8 KiB
C#
using RR.Common.Prizes;
|
|
using RR.Common.Utility;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
namespace RR.Common.UI.FX
|
|
{
|
|
/// <summary>
|
|
/// Добавляет QR-коду цветную подсветку, соответствующую рангу приза.
|
|
/// </summary>
|
|
[DisallowMultipleComponent]
|
|
[AddComponentMenu("RR/UI/FX/QR Code Tier Highlight")]
|
|
public sealed class QRCodeTierHighlight : MonoBehaviour
|
|
{
|
|
[SerializeField] private QRTier tier;
|
|
[SerializeField] private Color fallbackColor = new(0.15f, 0.55f, 1f, 1f);
|
|
[SerializeField, Range(0f, 1f)] private float glowAlpha = 0.55f;
|
|
[SerializeField, Min(0f)] private float outlineDistance = 4f;
|
|
[SerializeField, Range(4, 16)] private int outlineSamples = 8;
|
|
[SerializeField, Range(1, 4)] private int blurLayers = 2;
|
|
[SerializeField, Min(0f)] private float blurSpread = 2f;
|
|
[SerializeField, Min(0f)] private float pulseScale = 0.035f;
|
|
[SerializeField, Min(0.01f)] private float pulseDuration = 1.35f;
|
|
[SerializeField] private bool pulse = true;
|
|
|
|
private Image _targetImage;
|
|
private RectTransform _highlightRect;
|
|
private GameObject _highlightObject;
|
|
private readonly List<Image> _highlightLayers = new();
|
|
|
|
public static QRCodeTierHighlight Apply(GameObject qrObject, QRTier qrTier)
|
|
{
|
|
if (qrObject == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
QRCodeTierHighlight highlight = qrObject.GetComponent<QRCodeTierHighlight>();
|
|
if (highlight == null)
|
|
{
|
|
highlight = qrObject.AddComponent<QRCodeTierHighlight>();
|
|
}
|
|
|
|
highlight.SetTier(qrTier);
|
|
return highlight;
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
ResolveReferences();
|
|
EnsureView();
|
|
ApplyColor();
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
ResolveReferences();
|
|
EnsureView();
|
|
ApplyColor();
|
|
SyncHighlightRect(1f);
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (!pulse || _highlightRect == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
float wave = (Mathf.Sin(Time.unscaledTime / pulseDuration * Mathf.PI * 2f) + 1f) * 0.5f;
|
|
float scale = 1f + wave * pulseScale;
|
|
SyncHighlightRect(scale);
|
|
}
|
|
|
|
public void SetTier(QRTier qrTier)
|
|
{
|
|
tier = qrTier;
|
|
EnsureView();
|
|
ApplyColor();
|
|
}
|
|
|
|
private void EnsureView()
|
|
{
|
|
if (_highlightLayers.Count > 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Transform highlightParent = transform.parent != null ? transform.parent : transform;
|
|
_highlightObject = new GameObject($"{name} QR Tier Highlight", typeof(RectTransform), typeof(LayoutElement));
|
|
_highlightObject.transform.SetParent(highlightParent, false);
|
|
_highlightObject.GetComponent<LayoutElement>().ignoreLayout = true;
|
|
_highlightRect = _highlightObject.GetComponent<RectTransform>();
|
|
_highlightObject.transform.SetSiblingIndex(transform.GetSiblingIndex());
|
|
SyncHighlightRect(1f);
|
|
RebuildHighlightLayers();
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
if (_highlightObject != null)
|
|
{
|
|
Destroy(_highlightObject);
|
|
}
|
|
}
|
|
|
|
private void ResolveReferences()
|
|
{
|
|
if (_targetImage == null)
|
|
{
|
|
_targetImage = GetComponent<Image>();
|
|
}
|
|
}
|
|
|
|
private void RebuildHighlightLayers()
|
|
{
|
|
_highlightLayers.Clear();
|
|
int samples = Mathf.Max(4, outlineSamples);
|
|
int layerIndex = 0;
|
|
|
|
for (int blur = 0; blur < Mathf.Max(1, blurLayers); blur++)
|
|
{
|
|
float distance = outlineDistance + blur * blurSpread;
|
|
for (int sample = 0; sample < samples; sample++)
|
|
{
|
|
GameObject layerObject = new($"QR Tier Outline {layerIndex + 1}", typeof(RectTransform), typeof(Image));
|
|
layerObject.transform.SetParent(_highlightRect, false);
|
|
layerObject.transform.SetSiblingIndex(layerIndex);
|
|
|
|
float angle = sample / (float)samples * Mathf.PI * 2f;
|
|
Vector2 offset = new(Mathf.Cos(angle) * distance, Mathf.Sin(angle) * distance);
|
|
|
|
RectTransform layerRect = layerObject.GetComponent<RectTransform>();
|
|
layerRect.anchorMin = Vector2.zero;
|
|
layerRect.anchorMax = Vector2.one;
|
|
layerRect.offsetMin = offset;
|
|
layerRect.offsetMax = offset;
|
|
|
|
Image layerImage = layerObject.GetComponent<Image>();
|
|
ApplyTargetSprite(layerImage);
|
|
layerImage.raycastTarget = false;
|
|
_highlightLayers.Add(layerImage);
|
|
layerIndex++;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ApplyTargetSprite(Image image)
|
|
{
|
|
if (image == null || _targetImage == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
image.sprite = _targetImage.sprite;
|
|
image.type = _targetImage.type;
|
|
image.preserveAspect = _targetImage.preserveAspect;
|
|
image.fillCenter = true;
|
|
image.fillMethod = _targetImage.fillMethod;
|
|
image.fillOrigin = _targetImage.fillOrigin;
|
|
image.fillAmount = _targetImage.fillAmount;
|
|
image.fillClockwise = _targetImage.fillClockwise;
|
|
image.pixelsPerUnitMultiplier = _targetImage.pixelsPerUnitMultiplier;
|
|
}
|
|
|
|
private void ApplyColor()
|
|
{
|
|
if (_highlightLayers.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < _highlightLayers.Count; i++)
|
|
{
|
|
Image layer = _highlightLayers[i];
|
|
if (layer == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int samples = Mathf.Max(4, outlineSamples);
|
|
int blurIndex = i / samples;
|
|
float layerT = Mathf.Max(1, blurLayers) <= 1 ? 0f : blurIndex / (float)(Mathf.Max(1, blurLayers) - 1);
|
|
Color color = ResolveTierColor(tier);
|
|
color.a = glowAlpha * Mathf.Lerp(0.36f, 0.12f, layerT);
|
|
layer.color = color;
|
|
}
|
|
}
|
|
|
|
private Color ResolveTierColor(QRTier qrTier)
|
|
{
|
|
if (qrTier == QRTier.None)
|
|
{
|
|
return fallbackColor;
|
|
}
|
|
|
|
Vector3 rgb = QRCodeInfo.GetTierDarkColorVector(qrTier);
|
|
return new Color(rgb.x, rgb.y, rgb.z, 1f);
|
|
}
|
|
|
|
private void SyncHighlightRect(float scale)
|
|
{
|
|
if (_highlightRect == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
RectTransform targetRect = transform as RectTransform;
|
|
if (targetRect == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_highlightRect.anchorMin = targetRect.anchorMin;
|
|
_highlightRect.anchorMax = targetRect.anchorMax;
|
|
_highlightRect.pivot = targetRect.pivot;
|
|
_highlightRect.anchoredPosition = targetRect.anchoredPosition;
|
|
_highlightRect.sizeDelta = targetRect.sizeDelta;
|
|
_highlightRect.localRotation = targetRect.localRotation;
|
|
_highlightRect.localScale = targetRect.localScale * scale;
|
|
|
|
if (_highlightRect.parent == targetRect.parent)
|
|
{
|
|
_highlightRect.SetSiblingIndex(Mathf.Max(0, targetRect.GetSiblingIndex() - 1));
|
|
}
|
|
}
|
|
}
|
|
}
|