Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- доработан flow призовой витрины и связанного UI: добавлены `PrizeStandButtonView` и `RulesPrizeListPage`, обновлены `PrizeStandController`, `PrizeStandPrize`, `PrizeStandModal`, `PrizeStandSettingsModal`, `LevelPrizeSettingsModal`, `QRCodeSettingsModal`, `BalanceDisplay`, `ButtonTextFocusColor` и связанные prefab'ы, чтобы улучшить настройку, навигацию, отображение кнопок и prize flow - расширена система настроек и сохранения значений: `OptionPicker` получил отдельный формат `ValueSelector`, обновлены `SettingsDatabase`, `SettingEntryDrawer`, `SettingUIItem`, `SettingsUIGenerator`, `ValueSelectorRow` и `MainSettingsDatabase.asset`, а сохранение option picker-значений, включая `gameplay.gameDifficulty`, исправлено и синхронизировано с остальными настройками - добавлены универсальные спавнеры сущностей: реализованы `EntitySpawner` и `EntitySpawnerManager` с поддержкой весов префабов, точек спавна, заполнения по проценту, ограничений на количество и автоспавна при инициализации - доработана логика игрового спавна и сложности: процент инициализационного спавна может зависеть от настройки `gameplay.gameDifficulty` с градациями 25/50/75/100, в одном цикле массового спавна повторно не используется та же точка, а gizmo спавнера теперь показывает только сами точки без линий - упрощена работа в редакторе Unity и добавлены editor-инструменты: в `GameManager` лицензия пропускается при запуске в `UNITY_EDITOR`, а `HierarchySelectionCounter`, `HierarchySelectionCountOverlay` и `HierarchyBulkRenamerWindow` добавляют подсчет выделения, realtime overlay в `SceneView` и массовое переименование объектов по правилам нумерации из Unity Project Settings - синхронизированы сцены, ресурсы и UI-ассеты под новый flow: обновлены `MainMenu`, `Level UI`, уровни `1-1` и `1-2`, строки настроек, кнопки, TMP/font assets, изображения UI, `prizes.json` и recovery-данные после правок интерфейса и логики - очищены и переразложены часть внешних и служебных ресурсов: удалён пакет `Lava_Flowing_Shader`, переименованы/заменены отдельные UI-prefab'ы и добавлены сопутствующие проектные артефакты, которые сейчас используются обновлённой конфигурацией проекта
180 lines
7.5 KiB
C#
180 lines
7.5 KiB
C#
using System;
|
||
using System.IO;
|
||
using QRCoder;
|
||
using RR.Common.Prizes;
|
||
using UnityEngine;
|
||
using UnityEngine.Serialization;
|
||
using Color = System.Drawing.Color;
|
||
|
||
namespace RR.Common.Utility
|
||
{
|
||
/// <summary>
|
||
/// Утилитарный класс для создания QR-кодов в Unity.
|
||
/// Предоставляет статические методы для генерации QR-кодов в виде текстур.
|
||
/// </summary>
|
||
public class QRMaker : MonoBehaviour
|
||
{
|
||
/// <summary>
|
||
/// Создает текстуру QR-кода из переданного текста.
|
||
/// </summary>
|
||
/// <param name="text">Текст или данные для кодирования в QR-код.</param>
|
||
/// <param name="pixelsPerModule">Количество пикселей на один модуль QR-кода. По умолчанию 8.</param>
|
||
/// <param name="drawQuietZones">Определяет, нужно ли рисовать тихие зоны (белые поля) вокруг QR-кода. По умолчанию true.</param>
|
||
/// <param name="darkColor">Темный цвет в QR-коде</param>
|
||
/// <param name="lightColor">Светлый цвет в QR-коде</param>
|
||
/// <returns>Texture2D с изображением QR-кода, готовая для использования в UI или материалах.</returns>
|
||
public static Texture2D MakeTexture(string text,
|
||
int pixelsPerModule = 8,
|
||
bool drawQuietZones = true,
|
||
Color darkColor = default,
|
||
Color lightColor = default)
|
||
{
|
||
Color dark = darkColor == default ? Color.Black : darkColor;
|
||
Color light = lightColor == default ? Color.White : lightColor;
|
||
|
||
QRCodeGenerator gen = new();
|
||
QRCoder.QRCodeData data = gen.CreateQrCode(text, QRCodeGenerator.ECCLevel.M);
|
||
|
||
PngByteQRCode png = new(data);
|
||
// Генерим PNG-байты с указанными цветами
|
||
byte[] bytes = png.GetGraphic(pixelsPerModule, dark, light, drawQuietZones);
|
||
|
||
Texture2D tex = new(150, 150, TextureFormat.RGBA32, false);
|
||
tex.LoadImage(bytes); // раскодирует PNG → RGBA
|
||
tex.Apply(false, false);
|
||
return tex;
|
||
}
|
||
|
||
// public static Texture2D MakeTexture(string text, Image icon = null, int pixelsPerModule = 8,
|
||
// bool drawQuietZones = true)
|
||
// {
|
||
// QRCodeGenerator gen = new();
|
||
// QRCoder.QRCodeData data = gen.CreateQrCode(text, QRCodeGenerator.ECCLevel.M);
|
||
// QRCode code = new(data);
|
||
// Bitmap bitmapIcon = code.GetGraphic(pixelsPerModule,
|
||
// Color.Black,
|
||
// Color.White,
|
||
// icon == null ? null : new Bitmap(icon),
|
||
// 15,
|
||
// 10,
|
||
// drawQuietZones);
|
||
// Texture2D tex = new(150, 150, TextureFormat.RGBA32, false);
|
||
// tex.LoadImage(bitmapIcon);
|
||
// }
|
||
|
||
// Сохранить как PNG-файл
|
||
public static string SavePng(string text, string filePath, int ppm = 8)
|
||
{
|
||
QRCodeGenerator gen = new();
|
||
QRCoder.QRCodeData data = gen.CreateQrCode(text, QRCodeGenerator.ECCLevel.M);
|
||
PngByteQRCode png = new PngByteQRCode(data);
|
||
byte[] bytes = png.GetGraphic(ppm, Color.Black, Color.White, true);
|
||
File.WriteAllBytes(filePath, bytes);
|
||
return filePath;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Структура данных для конфигурации генерации QR-кода.
|
||
/// Содержит параметры для создания полных ссылок с префиксом и суффиксом.
|
||
/// </summary>
|
||
[Serializable]
|
||
public struct QRCodeInfo
|
||
{
|
||
/// <summary>
|
||
/// Флаг, определяющий, нужно ли генерировать QR-код.
|
||
/// </summary>
|
||
public bool generateQRCode;
|
||
/// <summary>
|
||
/// Ранг QR-кода.
|
||
/// </summary>
|
||
public QRTier tier;
|
||
/// <summary>
|
||
/// Префикс ссылки (например, "https://example.com/").
|
||
/// </summary>
|
||
public string linkPrefix;
|
||
/// <summary>
|
||
/// Шаблон сообщения для мессенджера. Используйте {0} для подстановки текста приза.
|
||
/// </summary>
|
||
[TextArea(2, 5)]
|
||
[FormerlySerializedAs("suffix")]
|
||
public string messageFormat;
|
||
/// <summary>
|
||
/// Короткий текст приза, который подставляется в шаблон сообщения.
|
||
/// </summary>
|
||
public string prizeText;
|
||
/// <summary>
|
||
/// Темный цвет в QR-коде.
|
||
/// </summary>
|
||
public Vector3 darkColor;
|
||
/// <summary>
|
||
/// Светлый цвет в QR-коде.
|
||
/// </summary>
|
||
public Vector3 lightColor;
|
||
|
||
public static Color GetColor(Vector3 colorRGB)
|
||
{
|
||
Color outColor = Color.FromArgb(255,
|
||
(int)(colorRGB.x * 255),
|
||
(int)(colorRGB.y * 255),
|
||
(int)(colorRGB.z * 255));
|
||
return outColor;
|
||
}
|
||
|
||
public static Vector3 GetTierDarkColorVector(QRTier tier)
|
||
{
|
||
return tier switch
|
||
{
|
||
QRTier.Bronze => new Vector3(166f / 255f, 104f / 255f, 64f / 255f),
|
||
QRTier.Silver => new Vector3(112f / 255f, 112f / 255f, 112f / 255f),
|
||
QRTier.Gold => new Vector3(184f / 255f, 134f / 255f, 11f / 255f),
|
||
_ => new Vector3(0f, 0f, 0f)
|
||
};
|
||
}
|
||
|
||
public static Color GetTierDarkColor(QRTier tier)
|
||
{
|
||
return GetColor(GetTierDarkColorVector(tier));
|
||
}
|
||
|
||
/// <summary>
|
||
/// Формирует полную ссылку, объединяя префикс, данные и суффикс.
|
||
/// </summary>
|
||
/// <param name="data">Основные данные для включения в ссылку.</param>
|
||
/// <returns>Полная ссылка в формате: {linkPrefix}{data}?text={suffix}</returns>
|
||
public string GetFullLink(string data) => $"{linkPrefix}{data}?text={ProcessSuffix(BuildMessageText())}";
|
||
|
||
public string BuildMessageText()
|
||
{
|
||
string format = messageFormat ?? string.Empty;
|
||
string prize = prizeText ?? string.Empty;
|
||
|
||
if (string.IsNullOrEmpty(format))
|
||
{
|
||
return prize;
|
||
}
|
||
|
||
try
|
||
{
|
||
if (format.Contains("{0}"))
|
||
{
|
||
return string.Format(format, prize);
|
||
}
|
||
}
|
||
catch (FormatException)
|
||
{
|
||
Debug.LogWarning($"[QRCodeInfo] Некорректный формат текста QR: '{format}'.");
|
||
}
|
||
|
||
if (string.IsNullOrWhiteSpace(prize))
|
||
{
|
||
return format;
|
||
}
|
||
|
||
return $"{format}\n{prize}";
|
||
}
|
||
|
||
private string ProcessSuffix(string rawSuffix) => Uri.EscapeDataString(rawSuffix);
|
||
}
|
||
}
|