From d7571d771db00de985dedba260a86b9099f65cf3 Mon Sep 17 00:00:00 2001 From: yastreb Date: Mon, 30 Mar 2026 08:29:09 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20?= =?UTF-8?q?=20=D0=B8=D0=BD=D0=B8=D1=86=D0=B8=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0?= =?UTF-8?q?=D1=82=D0=BE=D1=80=20,=20=D1=80=D0=B5=D0=B6=D0=B8=D0=BC=D0=B0?= =?UTF-8?q?=20=D0=BF=D0=BE=D0=B4=D1=81=D0=B5=D0=B2=D0=B5=D1=82=D0=BA=D0=B8?= =?UTF-8?q?=20=D0=B8=D0=B7=20=D0=B8=D0=BD=D0=B8=D1=88=D0=BD=D0=B8=D0=BA?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Scripts/BlinkController.cs | 247 +++++++++++----------- Assets/Scripts/BlinkStartupInitializer.cs | 180 ++++++++++++++++ 2 files changed, 304 insertions(+), 123 deletions(-) create mode 100644 Assets/Scripts/BlinkStartupInitializer.cs diff --git a/Assets/Scripts/BlinkController.cs b/Assets/Scripts/BlinkController.cs index 73985c2c..6769d195 100644 --- a/Assets/Scripts/BlinkController.cs +++ b/Assets/Scripts/BlinkController.cs @@ -1,15 +1,15 @@ -using System.Collections; +using System.Collections; using System.Collections.Generic; using UnityEngine; public static class BlinkController { - // Èíòåðâàë ìèãàíèÿ ïî óìîë÷àíèþ (â ñåêóíäàõ) + // Интервал миганиÑ� по умолчанию (в Ñ�екундах) public static float DefaultInterval = 0.5f; - // Âíóòðåííèé ðàííåð äëÿ çàïóñêà êîðóòèí (äèíàìè÷åñêè ñîçäà¸òñÿ ïðè ïåðâîì èñïîëüçîâàíèè) + // Внутренний раннер длÑ� запуÑ�ка корутин (динамичеÑ�ки Ñ�оздаётÑ�Ñ� при первом иÑ�пользовании) private class Runner : MonoBehaviour { - // Ïðè âûõîäå èç ïðèëîæåíèÿ âûêëþ÷àåì âñå ïîäñâåòêè + // При выходе из приложениÑ� выключаем вÑ�е подÑ�ветки private void OnApplicationQuit() { BlinkController.TurnAllOff(); @@ -17,10 +17,10 @@ private void OnApplicationQuit() } private static Runner _runner; - // Õðàíèëèùå çàïóùåííûõ êîðóòèí: êëþ÷ ôîðìèðóåì êàê "A_1" (òèï_èãðîê) + // Хранилище запущенных корутин: ключ формируем как "A_1" (тип_игрок) private static readonly Dictionary _coroutines = new Dictionary(); - // Óáåäèìñÿ, ÷òî ðàííåð ñóùåñòâóåò è æèâ¸ò íà ñöåíå + // УбедимÑ�Ñ�, что раннер Ñ�ущеÑ�твует и живёт на Ñ�цене private static void EnsureRunner() { if (_runner != null) return; @@ -30,92 +30,92 @@ private static void EnsureRunner() go.hideFlags = HideFlags.HideAndDontSave; } - // ---- Àäðåñíûå êîìàíäû äëÿ ïîäñâåòîê A è B (ïî èãðîêó) ---- + // ---- Ð�дреÑ�ные команды длÑ� подÑ�веток A и B (по игроку) ---- public static void LightAOn(int playerId) => ComWrapper.Send("2171", playerId); public static void LightAOff(int playerId) => ComWrapper.Send("2170", playerId); public static void LightBOn(int playerId) => ComWrapper.Send("2131", playerId); public static void LightBOff(int playerId) => ComWrapper.Send("2130", playerId); - // ---- Ãëîáàëüíàÿ ïîäñâåòêà C (broadcast) ---- + // ---- ГлобальнаÑ� подÑ�ветка C (broadcast) ---- public static void LightCOn() => ComWrapper.Send("401"); public static void LightCOff() => ComWrapper.Send("400"); - // ---- Àäðåñíûå LED äèîäû (êîìàíäû 8xxx) ---- - // 81õõ - êàíàë 1, 82õõ - êàíàë 2, 83õõ - êàíàë 3, 84õõ - êàíàë 4 - // 80õõ - âñå êàíàëû ñðàçó - // Ïðîãðàììû: 0-None, 1-Repeat, 2-Off, 3-Red, 4-Green, 5-Blue, 6-Gray, 7-White, 9-Rainbow, 10+-Custom + // ---- Ð�дреÑ�ные LED диоды (команды 8xxx) ---- + // 81Ñ…Ñ… - канал 1, 82Ñ…Ñ… - канал 2, 83Ñ…Ñ… - канал 3, 84Ñ…Ñ… - канал 4 + // 80Ñ…Ñ… - вÑ�е каналы Ñ�разу + // Программы: 0-None, 1-Repeat, 2-Off, 3-Red, 4-Green, 5-Blue, 6-Gray, 7-White, 9-Rainbow, 10+-Custom - /// Óñòàíîâèòü ïðîãðàììó íà êàíàëå LED (1-4) äëÿ êîíêðåòíîãî èãðîêà + /// УÑ�тановить программу на канале LED (1-4) длÑ� конкретного игрока public static void SetLEDChannel(int channel, int program, int playerId) { if (channel < 1 || channel > 4) { - Debug.LogWarning($"BlinkController: Íåâåðíûé êàíàë LED {channel}. Äîïóñòèìî 1-4"); + Debug.LogWarning($"BlinkController: Ð�еверный канал LED {channel}. ДопуÑ�тимо 1-4"); return; } if (program < 0 || program > 99) { - Debug.LogWarning($"BlinkController: Íåâåðíàÿ ïðîãðàììà LED {program}. Äîïóñòèìî 0-99"); + Debug.LogWarning($"BlinkController: Ð�евернаÑ� программа LED {program}. ДопуÑ�тимо 0-99"); return; } string cmd = $"{80 + channel}{program:D2}"; ComWrapper.Send(cmd, playerId); - Debug.Log($"BlinkController: LED êàíàë {channel} -> ïðîãðàììà {program} äëÿ èãðîêà {playerId} (cmd: {cmd})"); + Debug.Log($"BlinkController: LED канал {channel} -> программа {program} длÑ� игрока {playerId} (cmd: {cmd})"); } - /// Óñòàíîâèòü ïðîãðàììó íà âñåõ LED êàíàëàõ äëÿ êîíêðåòíîãî èãðîêà + /// УÑ�тановить программу на вÑ�ех LED каналах длÑ� конкретного игрока public static void SetLEDAll(int program, int playerId) { if (program < 0 || program > 99) { - Debug.LogWarning($"BlinkController: Íåâåðíàÿ ïðîãðàììà LED {program}. Äîïóñòèìî 0-99"); + Debug.LogWarning($"BlinkController: Ð�евернаÑ� программа LED {program}. ДопуÑ�тимо 0-99"); return; } string cmd = $"80{program:D2}"; ComWrapper.Send(cmd, playerId); - Debug.Log($"BlinkController: LED âñå êàíàëû -> ïðîãðàììà {program} äëÿ èãðîêà {playerId} (cmd: {cmd})"); + Debug.Log($"BlinkController: LED вÑ�е каналы -> программа {program} длÑ� игрока {playerId} (cmd: {cmd})"); } - /// Óñòàíîâèòü ïðîãðàììó íà âñåõ LED êàíàëàõ ãëîáàëüíî (broadcast) + /// УÑ�тановить программу на вÑ�ех LED каналах глобально (broadcast) public static void SetLEDAllGlobal(int program) { if (program < 0 || program > 99) { - Debug.LogWarning($"BlinkController: Íåâåðíàÿ ïðîãðàììà LED {program}. Äîïóñòèìî 0-99"); + Debug.LogWarning($"BlinkController: Ð�евернаÑ� программа LED {program}. ДопуÑ�тимо 0-99"); return; } string cmd = $"80{program:D2}"; ComWrapper.Send(cmd); - Debug.Log($"BlinkController: LED âñå êàíàëû -> ïðîãðàììà {program} ãëîáàëüíî (cmd: {cmd})"); + Debug.Log($"BlinkController: LED вÑ�е каналы -> программа {program} глобально (cmd: {cmd})"); } - // Áûñòðûå êîìàíäû äëÿ LED (àäðåñíûå äëÿ èãðîêà) - public static void LEDOff(int channel, int playerId) => SetLEDChannel(channel, 2, playerId); // Ïîãàñèòü êàíàë - public static void LEDRed(int channel, int playerId) => SetLEDChannel(channel, 3, playerId); // Êðàñíûé - public static void LEDGreen(int channel, int playerId) => SetLEDChannel(channel, 4, playerId); // Çåëåíûé - public static void LEDBlue(int channel, int playerId) => SetLEDChannel(channel, 5, playerId); // Ñèíèé - public static void LEDWhite(int channel, int playerId) => SetLEDChannel(channel, 7, playerId); // Áåëûé - public static void LEDRainbow(int channel, int playerId) => SetLEDChannel(channel, 9, playerId); // Ðàäóãà + // БыÑ�трые команды длÑ� LED (адреÑ�ные длÑ� игрока) + public static void LEDOff(int channel, int playerId) => SetLEDChannel(channel, 2, playerId); // ПогаÑ�ить канал + public static void LEDRed(int channel, int playerId) => SetLEDChannel(channel, 3, playerId); // КраÑ�ный + public static void LEDGreen(int channel, int playerId) => SetLEDChannel(channel, 4, playerId); // Зеленый + public static void LEDBlue(int channel, int playerId) => SetLEDChannel(channel, 5, playerId); // Синий + public static void LEDWhite(int channel, int playerId) => SetLEDChannel(channel, 7, playerId); // Белый + public static void LEDRainbow(int channel, int playerId) => SetLEDChannel(channel, 9, playerId); // Радуга - public static void LEDWave(int channel, int playerId) => SetLEDChannel(channel, 11, playerId); // âîëíà + public static void LEDWave(int channel, int playerId) => SetLEDChannel(channel, 11, playerId); // волна - public static void LEDOffAll(int playerId) => SetLEDAll(2, playerId); // Ïîãàñèòü âñå äëÿ èãðîêà - public static void LEDRedAll(int playerId) => SetLEDAll(3, playerId); // Êðàñíûé íà âñåõ äëÿ èãðîêà - public static void LEDGreenAll(int playerId) => SetLEDAll(4, playerId); // Çåëåíûé íà âñåõ äëÿ èãðîêà - public static void LEDBlueAll(int playerId) => SetLEDAll(5, playerId); // Ñèíèé íà âñåõ äëÿ èãðîêà - public static void LEDWhiteAll(int playerId) => SetLEDAll(7, playerId); // Áåëûé íà âñåõ äëÿ èãðîêà - public static void LEDRainbowAll(int playerId) => SetLEDAll(9, playerId); // Ðàäóãà íà âñåõ äëÿ èãðîêà + public static void LEDOffAll(int playerId) => SetLEDAll(2, playerId); // ПогаÑ�ить вÑ�е длÑ� игрока + public static void LEDRedAll(int playerId) => SetLEDAll(3, playerId); // КраÑ�ный на вÑ�ех длÑ� игрока + public static void LEDGreenAll(int playerId) => SetLEDAll(4, playerId); // Зеленый на вÑ�ех длÑ� игрока + public static void LEDBlueAll(int playerId) => SetLEDAll(5, playerId); // Синий на вÑ�ех длÑ� игрока + public static void LEDWhiteAll(int playerId) => SetLEDAll(7, playerId); // Белый на вÑ�ех длÑ� игрока + public static void LEDRainbowAll(int playerId) => SetLEDAll(9, playerId); // Радуга на вÑ�ех длÑ� игрока - // Ãëîáàëüíûå êîìàíäû (broadcast äëÿ âñåõ èãðîêîâ) - public static void LEDOffAllGlobal() => SetLEDAllGlobal(2); // Ïîãàñèòü âñå ãëîáàëüíî - public static void LEDRedAllGlobal() => SetLEDAllGlobal(3); // Êðàñíûé íà âñåõ ãëîáàëüíî - public static void LEDGreenAllGlobal() => SetLEDAllGlobal(4); // Çåëåíûé íà âñåõ ãëîáàëüíî - public static void LEDBlueAllGlobal() => SetLEDAllGlobal(5); // Ñèíèé íà âñåõ ãëîáàëüíî - public static void LEDWhiteAllGlobal() => SetLEDAllGlobal(7); // Áåëûé íà âñåõ ãëîáàëüíî - public static void LEDRainbowAllGlobal() => SetLEDAllGlobal(9); // Ðàäóãà íà âñåõ ãëîáàëüíî + // Глобальные команды (broadcast длÑ� вÑ�ех игроков) + public static void LEDOffAllGlobal() => SetLEDAllGlobal(2); // ПогаÑ�ить вÑ�е глобально + public static void LEDRedAllGlobal() => SetLEDAllGlobal(3); // КраÑ�ный на вÑ�ех глобально + public static void LEDGreenAllGlobal() => SetLEDAllGlobal(4); // Зеленый на вÑ�ех глобально + public static void LEDBlueAllGlobal() => SetLEDAllGlobal(5); // Синий на вÑ�ех глобально + public static void LEDWhiteAllGlobal() => SetLEDAllGlobal(7); // Белый на вÑ�ех глобально + public static void LEDRainbowAllGlobal() => SetLEDAllGlobal(9); // Радуга на вÑ�ех глобально - // ---- Ïåðåêëþ÷åíèå öâåòîâîãî ÷åðåäîâàíèÿ (êîìàíäû 89x) ---- + // ---- Переключение цветового чередованиÑ� (команды 89x) ---- public enum ColorSequence { RGB = 0, // 890 - Red, Green, Blue @@ -126,7 +126,7 @@ public enum ColorSequence GBR = 5 // 895 - Green, Blue, Red } - /// Óñòàíîâèòü öâåòîâîå ÷åðåäîâàíèå. Åñëè playerId = 0 èëè íå óêàçàí - îòïðàâêà ãëîáàëüíî + /// УÑ�тановить цветовое чередование. ЕÑ�ли playerId = 0 или не указан - отправка глобально public static void SetColorSequence(ColorSequence sequence, int playerId = 0) { string cmd = $"89{(int)sequence}"; @@ -134,124 +134,124 @@ public static void SetColorSequence(ColorSequence sequence, int playerId = 0) if (playerId > 0) { ComWrapper.Send(cmd, playerId); - Debug.Log($"BlinkController: Öâåòîâîå ÷åðåäîâàíèå {sequence} äëÿ èãðîêà {playerId} (cmd: {cmd})"); + Debug.Log($"BlinkController: Цветовое чередование {sequence} длÑ� игрока {playerId} (cmd: {cmd})"); } else { ComWrapper.Send(cmd); - Debug.Log($"BlinkController: Öâåòîâîå ÷åðåäîâàíèå {sequence} ãëîáàëüíî (cmd: {cmd})"); + Debug.Log($"BlinkController: Цветовое чередование {sequence} глобально (cmd: {cmd})"); } } - // ---- Ïðîèçâîëüíûé RGB öâåò (êîìàíäà 08RRRGGGBBB) ---- + // ---- Произвольный RGB цвет (команда 08RRRGGGBBB) ---- - /// Óñòàíîâèòü ïðîèçâîëüíûé RGB öâåò äëÿ LED. Åñëè playerId = 0 - îòïðàâêà ãëîáàëüíî - /// Êðàñíûé êàíàë (0-255) - /// Çåëåíûé êàíàë (0-255) - /// Ñèíèé êàíàë (0-255) - /// ID èãðîêà (0 = ãëîáàëüíî) - /// Óñòàíîâèòü ïðîèçâîëüíûé RGB öâåò íà êîíêðåòíûé êàíàë (81-84) + /// УÑ�тановить произвольный RGB цвет длÑ� LED. ЕÑ�ли playerId = 0 - отправка глобально + /// КраÑ�ный канал (0-255) + /// Зеленый канал (0-255) + /// Синий канал (0-255) + /// ID игрока (0 = глобально) + /// УÑ�тановить произвольный RGB цвет на конкретный канал (81-84) public static void SetLEDChannelCustomColor(int channel, int r, int g, int b, int playerId = 0) { - // Îãðàíè÷èâàåì çíà÷åíèÿ + // Ограничиваем значениÑ� channel = Mathf.Clamp(channel, 81, 84); r = Mathf.Clamp(r, 0, 255); g = Mathf.Clamp(g, 0, 255); b = Mathf.Clamp(b, 0, 255); - // Ôîðìàò: 8X (êàíàë) + 08 (ïðîãðàììà ïðîèçâîëüíûé öâåò) + RRRGGGBBB + // Формат: 8X (канал) + 08 (программа произвольный цвет) + RRRGGGBBB string cmd = $"{channel}08{r:D3}{g:D3}{b:D3}"; if (playerId > 0) { ComWrapper.Send(cmd, playerId); - Debug.Log($"BlinkController: LED êàíàë {channel} ïðîèçâîëüíûé öâåò RGB({r},{g},{b}) äëÿ èãðîêà {playerId} (cmd: {cmd})"); + Debug.Log($"BlinkController: LED канал {channel} произвольный цвет RGB({r},{g},{b}) длÑ� игрока {playerId} (cmd: {cmd})"); } else { ComWrapper.Send(cmd); - Debug.Log($"BlinkController: LED êàíàë {channel} ïðîèçâîëüíûé öâåò RGB({r},{g},{b}) ãëîáàëüíî (cmd: {cmd})"); + Debug.Log($"BlinkController: LED канал {channel} произвольный цвет RGB({r},{g},{b}) глобально (cmd: {cmd})"); } } - /// Óñòàíîâèòü ïðîèçâîëüíûé RGB öâåò íà âñå êàíàëû (80) + /// УÑ�тановить произвольный RGB цвет на вÑ�е каналы (80) public static void SetLEDCustomColor(int r, int g, int b, int playerId = 0) { - // Îãðàíè÷èâàåì çíà÷åíèÿ 0-255 + // Ограничиваем значениÑ� 0-255 r = Mathf.Clamp(r, 0, 255); g = Mathf.Clamp(g, 0, 255); b = Mathf.Clamp(b, 0, 255); - // Ôîðìàò: 80 (âñå êàíàëû) + 08 (ïðîãðàììà ïðîèçâîëüíûé öâåò) + RRRGGGBBB + // Формат: 80 (вÑ�е каналы) + 08 (программа произвольный цвет) + RRRGGGBBB string cmd = $"8008{r:D3}{g:D3}{b:D3}"; if (playerId > 0) { ComWrapper.Send(cmd, playerId); - Debug.Log($"BlinkController: LED ïðîèçâîëüíûé öâåò RGB({r},{g},{b}) äëÿ èãðîêà {playerId} (cmd: {cmd})"); + Debug.Log($"BlinkController: LED произвольный цвет RGB({r},{g},{b}) длÑ� игрока {playerId} (cmd: {cmd})"); } else { ComWrapper.Send(cmd); - Debug.Log($"BlinkController: LED ïðîèçâîëüíûé öâåò RGB({r},{g},{b}) ãëîáàëüíî (cmd: {cmd})"); + Debug.Log($"BlinkController: LED произвольный цвет RGB({r},{g},{b}) глобально (cmd: {cmd})"); } } - // Ïðåäóñòàíîâëåííûå öâåòà - public static void LEDGold(int playerId = 0) => SetLEDCustomColor(255, 215, 0, playerId); // Çîëîòîé - public static void LEDOrange(int playerId = 0) => SetLEDCustomColor(255, 165, 0, playerId); // Îðàíæåâûé - public static void LEDPurple(int playerId = 0) => SetLEDCustomColor(128, 0, 128, playerId); // Ôèîëåòîâûé - public static void LEDPink(int playerId = 0) => SetLEDCustomColor(255, 192, 203, playerId); // Ðîçîâûé - public static void LEDCyan(int playerId = 0) => SetLEDCustomColor(0, 255, 255, playerId); // Ãîëóáîé - public static void LEDYellow(int playerId = 0) => SetLEDCustomColor(255, 255, 0, playerId); // Æåëòûé + // ПредуÑ�тановленные цвета + public static void LEDGold(int playerId = 0) => SetLEDCustomColor(255, 215, 0, playerId); // Золотой + public static void LEDOrange(int playerId = 0) => SetLEDCustomColor(255, 165, 0, playerId); // Оранжевый + public static void LEDPurple(int playerId = 0) => SetLEDCustomColor(128, 0, 128, playerId); // Фиолетовый + public static void LEDPink(int playerId = 0) => SetLEDCustomColor(255, 192, 203, playerId); // Розовый + public static void LEDCyan(int playerId = 0) => SetLEDCustomColor(0, 255, 255, playerId); // Голубой + public static void LEDYellow(int playerId = 0) => SetLEDCustomColor(255, 255, 0, playerId); // Желтый - // ---- Ìèãàíèå LED ñ âîçâðàòîì ê áàçîâîìó öâåòó ---- + // ---- Мигание LED Ñ� возвратом к базовому цвету ---- private static readonly Dictionary _playerBaseLEDColor = new Dictionary(); - // Õðàíèì êàñòîìíûå RGB öâåòà äëÿ êàæäîãî èãðîêà (åñëè óñòàíîâëåí) + // Храним каÑ�томные RGB цвета длÑ� каждого игрока (еÑ�ли уÑ�тановлен) private static readonly Dictionary _playerCustomLEDColor = new Dictionary(); - /// Óñòàíîâèòü áàçîâûé öâåò LED äëÿ èãðîêà (ê êîòîðîìó âîçâðàùàåìñÿ ïîñëå ìèãàíèÿ) + /// УÑ�тановить базовый цвет LED длÑ� игрока (к которому возвращаемÑ�Ñ� поÑ�ле миганиÑ�) public static void SetLEDBaseColor(int playerId, int program) { _playerBaseLEDColor[playerId] = program; - _playerCustomLEDColor.Remove(playerId); // Óáèðàåì êàñòîìíûé öâåò + _playerCustomLEDColor.Remove(playerId); // Убираем каÑ�томный цвет SetLEDAll(program, playerId); - Debug.Log($"BlinkController: Áàçîâûé LED öâåò äëÿ èãðîêà {playerId} óñòàíîâëåí: {program}"); + Debug.Log($"BlinkController: Базовый LED цвет длÑ� игрока {playerId} уÑ�тановлен: {program}"); } - /// Óñòàíîâèòü áàçîâûé êàñòîìíûé RGB öâåò LED äëÿ èãðîêà + /// УÑ�тановить базовый каÑ�томный RGB цвет LED длÑ� игрока public static void SetLEDBaseCustomColor(int playerId, int r, int g, int b) { - _playerBaseLEDColor[playerId] = -1; // Ìàðêåð êàñòîìíîãî öâåòà + _playerBaseLEDColor[playerId] = -1; // Маркер каÑ�томного цвета _playerCustomLEDColor[playerId] = new Vector3Int(r, g, b); SetLEDCustomColor(r, g, b, playerId); - Debug.Log($"BlinkController: Áàçîâûé êàñòîìíûé LED öâåò äëÿ èãðîêà {playerId} óñòàíîâëåí: RGB({r},{g},{b})"); + Debug.Log($"BlinkController: Базовый каÑ�томный LED цвет длÑ� игрока {playerId} уÑ�тановлен: RGB({r},{g},{b})"); } - /// Ïîëó÷èòü áàçîâûé öâåò LED äëÿ èãðîêà + /// Получить базовый цвет LED длÑ� игрока public static int GetLEDBaseColor(int playerId) { - return _playerBaseLEDColor.TryGetValue(playerId, out int color) ? color : 5; // Ïî óìîë÷àíèþ ñèíèé (5) + return _playerBaseLEDColor.TryGetValue(playerId, out int color) ? color : 5; // По умолчанию Ñ�иний (5) } - /// Âîññòàíîâèòü áàçîâûé öâåò LED äëÿ èãðîêà (âêëþ÷àÿ êàñòîìíûå RGB) + /// ВоÑ�Ñ�тановить базовый цвет LED длÑ� игрока (включаÑ� каÑ�томные RGB) public static void RestoreLEDBaseColor(int playerId) { if (_playerCustomLEDColor.TryGetValue(playerId, out Vector3Int rgb)) { - // Âîññòàíàâëèâàåì êàñòîìíûé öâåò + // ВоÑ�Ñ�танавливаем каÑ�томный цвет SetLEDCustomColor(rgb.x, rgb.y, rgb.z, playerId); } else { - // Âîññòàíàâëèâàåì îáû÷íûé öâåò ïî ïðîãðàììå + // ВоÑ�Ñ�танавливаем обычный цвет по программе int baseColor = GetLEDBaseColor(playerId); SetLEDAll(baseColor, playerId); } } - /// Ìèãàíèå LED N ðàç öâåòîì, çàòåì âîçâðàò ê áàçîâîìó öâåòó + /// Мигание LED N раз цветом, затем возврат к базовому цвету public static void BlinkLED(int playerId, int colorProgram, int times, float interval = 0.3f) { EnsureRunner(); @@ -262,20 +262,20 @@ private static IEnumerator BlinkLEDRoutine(int playerId, int colorProgram, int t { for (int i = 0; i < times; i++) { - // Âêëþ÷èòü öâåò + // Включить цвет SetLEDAll(colorProgram, playerId); yield return new WaitForSeconds(interval); - // Âåðíóòü áàçîâûé öâåò (âêëþ÷àÿ êàñòîìíûå RGB) + // Вернуть базовый цвет (включаÑ� каÑ�томные RGB) RestoreLEDBaseColor(playerId); - // Ïàóçà ìåæäó ìèãàíèÿìè (êðîìå ïîñëåäíåãî) + // Пауза между миганиÑ�ми (кроме поÑ�леднего) if (i < times - 1) yield return new WaitForSeconds(interval); } } - // Áûñòðûå êîìàíäû ìèãàíèÿ + // БыÑ�трые команды миганиÑ� public static void BlinkLEDRed(int playerId, int times = 1, float interval = 0.3f) => BlinkLED(playerId, 3, times, interval); @@ -285,14 +285,14 @@ public static void BlinkLEDGreen(int playerId, int times = 1, float interval = 0 public static void BlinkLEDBlue(int playerId, int times = 1, float interval = 0.3f) => BlinkLED(playerId, 5, times, interval); - // ---- Start/Stop ìèãàíèÿ ---- + // ---- Start/Stop миганиÑ� ---- public static void StartBlinkA(int playerId, float interval = -1f) { - // Îñòàâëÿåì îáðàòíóþ ñîâìåñòèìîñòü: áåñêîíå÷íîå ìèãàíèå + // ОÑ�тавлÑ�ем обратную Ñ�овмеÑ�тимоÑ�ть: беÑ�конечное мигание StartBlinkATimes(playerId, -1, interval); } - // Ìèãàíèå N ðàç (N <= 0 = áåñêîíå÷íî) + // Мигание N раз (N <= 0 = беÑ�конечно) public static void StartBlinkATimes(int playerId, int times, float interval = -1f) { StopBlinkA(playerId); @@ -311,11 +311,11 @@ public static void StopBlinkA(int playerId) public static void StartBlinkB(int playerId, float interval = -1f) { - // Îñòàâëÿåì îáðàòíóþ ñîâìåñòèìîñòü: áåñêîíå÷íîå ìèãàíèå + // ОÑ�тавлÑ�ем обратную Ñ�овмеÑ�тимоÑ�ть: беÑ�конечное мигание StartBlinkBTimes(playerId, -1, interval); } - // Ìèãàíèå N ðàç (N <= 0 = áåñêîíå÷íî) + // Мигание N раз (N <= 0 = беÑ�конечно) public static void StartBlinkBTimes(int playerId, int times, float interval = -1f) { StopBlinkB(playerId); @@ -332,14 +332,14 @@ public static void StopBlinkB(int playerId) LightBOff(playerId); } - // Ãëîáàëüíàÿ C + // ГлобальнаÑ� C public static void StartBlinkC(float interval = -1f) { - // Îñòàâëÿåì îáðàòíóþ ñîâìåñòèìîñòü: áåñêîíå÷íîå ìèãàíèå + // ОÑ�тавлÑ�ем обратную Ñ�овмеÑ�тимоÑ�ть: беÑ�конечное мигание StartBlinkCTimes(-1, interval); } - // Ìèãàíèå N ðàç ãëîáàëüíî (N <= 0 = áåñêîíå÷íî) + // Мигание N раз глобально (N <= 0 = беÑ�конечно) public static void StartBlinkCTimes(int times, float interval = -1f) { StopBlinkC(); @@ -369,7 +369,7 @@ private static void StopAndRemove(string key) private static IEnumerator BlinkRoutinePlayer(string key, int playerId, string onCmd, string offCmd, float interval, int repeatCount) { - // repeatCount <= 0 => áåñêîíå÷íî + // repeatCount <= 0 => беÑ�конечно if (repeatCount <= 0) { while (true) @@ -381,20 +381,20 @@ private static IEnumerator BlinkRoutinePlayer(string key, int playerId, string o } } - // Ïîâòîðÿåì çàäàííîå êîëè÷åñòâî ðàç + // ПовторÑ�ем заданное количеÑ�тво раз for (int i = 0; i < repeatCount; i++) { ComWrapper.Send(onCmd, playerId); yield return new WaitForSeconds(interval); ComWrapper.Send(offCmd, playerId); - // Ìåæäó èòåðàöèÿìè äåëàåì ïàóçó, êàê â îáû÷íîì ìèãàíèè, êðîìå ïîñëåäíåé + // Между итерациÑ�ми делаем паузу, как в обычном мигании, кроме поÑ�ледней if (i < repeatCount - 1) yield return new WaitForSeconds(interval); } - // Ãàðàíòèðóåì, ÷òî êîìàíäà âûêëþ÷åíèÿ ïîñëàíà + // Гарантируем, что команда выключениÑ� поÑ�лана ComWrapper.Send(offCmd, playerId); - // Óäàëÿåì çàïèñü î êîðóòèíå + // УдалÑ�ем запиÑ�ÑŒ о корутине try { _coroutines.Remove(key); } catch { } } @@ -423,84 +423,84 @@ private static IEnumerator BlinkRoutineGlobal(string key, string onCmd, string o try { _coroutines.Remove(key); } catch { } } - // Âñïîìîãàòåëüíûå: ìãíîâåííûé âêë/âûêë âñåõ + // Ð’Ñ�помогательные: мгновенный вкл/выкл вÑ�ех public static void StopAll() { var keys = new List(_coroutines.Keys); foreach (var k in keys) StopAndRemove(k); } - // Âêëþ÷èòü âñå ïîäñâåòêè (A è B — äëÿ îáîèõ èãðîêîâ, C — ãëîáàëüíî) + // Включить вÑ�е подÑ�ветки (A и B — длÑ� обоих игроков, C — глобально) public static void TurnAllOn() { Debug.Log("BlinkController: TurnAllOn"); - // A è B àäðåñíûå — äëÿ èãðîêîâ 1 è 2 + // A и B адреÑ�ные — длÑ� игроков 1 и 2 LightAOn(1); LightAOn(2); LightBOn(1); LightBOn(2); - // C — ãëîáàëüíî + // C — глобально LightCOn(); - // LED - ïðè ñòàðòå: êàíàë 1 âîëíà, êàíàë 2 ðàäóãà äëÿ îáîèõ èãðîêîâ - LEDWave(1, 1); // Èãðîê 1: êàíàë 1 -> âîëíà - LEDRainbow(2, 1); // Èãðîê 1: êàíàë 2 -> ðàäóãà - LEDWave(1, 2); // Èãðîê 2: êàíàë 1 -> âîëíà - LEDRainbow(2, 2); // Èãðîê 2: êàíàë 2 -> ðàäóãà + // LED - при Ñ�тарте: канал 1 волна, канал 2 радуга длÑ� обоих игроков + LEDWave(1, 1); // Игрок 1: канал 1 -> волна + LEDRainbow(2, 1); // Игрок 1: канал 2 -> радуга + LEDWave(1, 2); // Игрок 2: канал 1 -> волна + LEDRainbow(2, 2); // Игрок 2: канал 2 -> радуга - // Óñòàíàâëèâàåì áàçîâûé öâåò - ðàäóãà (äëÿ ìèãàíèÿ áóäåò âîçâðàò ê íåé) + // УÑ�танавливаем базовый цвет - радуга (длÑ� миганиÑ� будет возврат к ней) _playerBaseLEDColor[1] = 9; _playerBaseLEDColor[2] = 9; } - // Âûêëþ÷èòü âñå ïîäñâåòêè (è îñòàíîâèòü êîðóòèíû) + // Выключить вÑ�е подÑ�ветки (и оÑ�тановить корутины) public static void TurnAllOff() { Debug.Log("BlinkController: TurnAllOff"); - // Îñòàíîâèì âñå ìèãàíèÿ + // ОÑ�тановим вÑ�е миганиÑ� StopAll(); - // Îòïðàâèì êîìàíäû âûêëþ÷åíèÿ àäðåñíî äëÿ èãðîêîâ 1 è 2 + // Отправим команды выключениÑ� адреÑ�но длÑ� игроков 1 и 2 try { LightAOff(1); LightAOff(2); LightBOff(1); LightBOff(2); - // C — ãëîáàëüíî + // C — глобально LightCOff(); - // LED - âûêëþ÷èòü âñå êàíàëû ãëîáàëüíî + // LED - выключить вÑ�е каналы глобально LEDOffAllGlobal(); } catch (System.Exception e) { - Debug.LogWarning($"BlinkController: îøèáêà ïðè TurnAllOff: {e.Message}"); + Debug.LogWarning($"BlinkController: ошибка при TurnAllOff: {e.Message}"); } } [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] private static void OnRuntimeInitialized() { - // Ïîäïèñûâàåìñÿ íà ñîáûòèå èíèöèàëèçàöèè ComWrapper — âêëþ÷èì ïîäñâåòêè, êîãäà COM áóäåò ãîòîâ + // ПодпиÑ�ываемÑ�Ñ� на Ñ�обытие инициализации ComWrapper — включим подÑ�ветки, когда COM будет готов try { ComWrapper.OnInitialize += TurnAllOn; - // Ïîäïèñûâàåìñÿ, ÷òîáû âûêëþ÷èòü ïîäñâåòêè ÄÎ çàêðûòèÿ ïîðòîâ + // ПодпиÑ�ываемÑ�Ñ�, чтобы выключить подÑ�ветки ДО закрытиÑ� портов ComWrapper.OnBeforeClose += TurnAllOff; } catch { - // Åñëè ComWrapper åù¸ íå ñóùåñòâóåò èëè íå îïðåäåë¸í, ïðîèãíîðèðóåì — âûçîâ ïðîèçîéä¸ò ïðè èíèöèàëèçàöèè + // ЕÑ�ли ComWrapper ещё не Ñ�ущеÑ�твует или не определён, проигнорируем — вызов произойдёт при инициализации } - // Ñîçäà¸ì ðàííåð çàðàíåå, ÷òîáû îí áûë äîñòóïåí è ãàðàíòèðîâàííî ñóùåñòâîâàë + // Создаём раннер заранее, чтобы он был доÑ�тупен и гарантированно Ñ�ущеÑ�твовал EnsureRunner(); - // Ïîäïèøåìñÿ íà ñîáûòèå âûõîäà ïðèëîæåíèÿ — ãàðàíòèðîâàííî âûêëþ÷èò ïîäñâåòêè è ïî÷èñòèò ïîäïèñêè + // ПодпишемÑ�Ñ� на Ñ�обытие выхода приложениÑ� — гарантированно выключит подÑ�ветки и почиÑ�тит подпиÑ�ки Application.quitting += OnApplicationQuitting; } - // Îáðàáîò÷èê âûõîäà ïðèëîæåíèÿ: îòïèñûâàåìñÿ îò ñîáûòèé ComWrapper + // Обработчик выхода приложениÑ�: отпиÑ�ываемÑ�Ñ� от Ñ�обытий ComWrapper private static void OnApplicationQuitting() { try { ComWrapper.OnInitialize -= TurnAllOn; } catch { } @@ -513,3 +513,4 @@ private static void OnApplicationQuitting() } } } + diff --git a/Assets/Scripts/BlinkStartupInitializer.cs b/Assets/Scripts/BlinkStartupInitializer.cs new file mode 100644 index 00000000..70742929 --- /dev/null +++ b/Assets/Scripts/BlinkStartupInitializer.cs @@ -0,0 +1,180 @@ +using System; +using System.IO; +using UnityEngine; + +public static class BlinkStartupInitializer +{ + private const string ConfigFileName = "led_startup_config.ini"; + private static bool subscribed; + + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + private static void Register() + { + if (subscribed) + return; + + subscribed = true; + ComWrapper.OnInitialize += ApplyStartupCommands; + Application.quitting += Unregister; + } + + private static void Unregister() + { + ComWrapper.OnInitialize -= ApplyStartupCommands; + Application.quitting -= Unregister; + subscribed = false; + } + + private static void ApplyStartupCommands() + { + string configPath = Path.Combine(Application.streamingAssetsPath, ConfigFileName); + + try + { + if (!File.Exists(configPath)) + { + Directory.CreateDirectory(Application.streamingAssetsPath); + File.WriteAllText(configPath, GetDefaultConfig()); + Debug.Log($"BlinkStartupInitializer: Ñ�оздан файл конфигурации {configPath}"); + } + + string[] lines = File.ReadAllLines(configPath); + for (int i = 0; i < lines.Length; i++) + ProcessLine(lines[i], i + 1); + } + catch (Exception ex) + { + Debug.LogWarning($"BlinkStartupInitializer: ошибка чтениÑ� {configPath}: {ex.Message}"); + } + } + + private static void ProcessLine(string rawLine, int lineNumber) + { + if (string.IsNullOrWhiteSpace(rawLine)) + return; + + string line = StripComments(rawLine).Trim(); + if (string.IsNullOrWhiteSpace(line)) + return; + + int separatorIndex = line.IndexOf('='); + if (separatorIndex < 0) + { + Debug.LogWarning($"BlinkStartupInitializer: Ñ�трока {lineNumber} должна быть в формате player1=004 003"); + return; + } + + string playerToken = line.Substring(0, separatorIndex).Trim(); + string commandsToken = line.Substring(separatorIndex + 1).Trim(); + + if (!TryParsePlayerId(playerToken, out int playerId)) + { + Debug.LogWarning($"BlinkStartupInitializer: Ñ�трока {lineNumber}, неизвеÑ�тный идентификатор '{playerToken}'"); + return; + } + + if (string.IsNullOrWhiteSpace(commandsToken)) + { + Debug.LogWarning($"BlinkStartupInitializer: Ñ�трока {lineNumber} не Ñ�одержит команд"); + return; + } + + string[] commands = commandsToken.Split(new[] { ' ', '\t', ',', ';' }, StringSplitOptions.RemoveEmptyEntries); + foreach (string command in commands) + SendValidatedCommand(command, playerId, lineNumber); + } + + private static void SendValidatedCommand(string rawCommand, int playerId, int lineNumber) + { + if (!TryNormalizeCommand(rawCommand, out string command)) + { + Debug.LogWarning($"BlinkStartupInitializer: Ñ�трока {lineNumber}, команда '{rawCommand}' некорректна"); + return; + } + + ComWrapper.Send(command, playerId); + Debug.Log($"BlinkStartupInitializer: отправлена команда {command} длÑ� playerId={playerId}"); + } + + private static bool TryNormalizeCommand(string rawCommand, out string command) + { + command = null; + if (string.IsNullOrWhiteSpace(rawCommand)) + return false; + + string value = rawCommand.Trim(); + if (value.StartsWith("89", StringComparison.Ordinal)) + value = value.Substring(2); + + if (value.Length != 3) + return false; + + if (!char.IsDigit(value[0]) || !char.IsDigit(value[1]) || !char.IsDigit(value[2])) + return false; + + int channel = value[0] - '0'; + int mode = value[1] - '0'; + int sequence = value[2] - '0'; + + if (channel < 0 || channel > 4) + return false; + + if (mode != 0) + return false; + + if (sequence < 0 || sequence > 5) + return false; + + command = $"89{value}"; + return true; + } + + private static bool TryParsePlayerId(string token, out int playerId) + { + switch (token.Trim().ToLowerInvariant()) + { + case "1": + case "player1": + case "plate1": + playerId = 1; + return true; + case "2": + case "player2": + case "plate2": + playerId = 2; + return true; + default: + playerId = 0; + return false; + } + } + + private static string StripComments(string line) + { + int hashIndex = line.IndexOf('#'); + int slashIndex = line.IndexOf("//", StringComparison.Ordinal); + + int commentIndex = -1; + if (hashIndex >= 0) + commentIndex = hashIndex; + if (slashIndex >= 0 && (commentIndex < 0 || slashIndex < commentIndex)) + commentIndex = slashIndex; + + return commentIndex >= 0 ? line.Substring(0, commentIndex) : line; + } + + private static string GetDefaultConfig() + { + return string.Join(Environment.NewLine, new[] + { + "# Команды инициализации LED-лент.", + "# Формат: player1=004 003", + "# player1/player2 = номер платы = playerId", + "# Команда: X0Z или 89X0Z", + "# X: 0 - вÑ�е каналы, 1-4 - конкретный канал", + "# Z: 0-RGB, 1-RBG, 2-BRG, 3-BGR, 4-GRB, 5-GBR", + "player1=004", + "player2=004" + }); + } +}