sea-battle/Assets/Scripts/BlinkController.cs

517 lines
24 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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();
}
}
private static Runner _runner;
// Хранилище запущенных корутин: ключ формируем как "A_1" (тип_игрок)
private static readonly Dictionary<string, Coroutine> _coroutines = new Dictionary<string, Coroutine>();
// Убедимся, что раннер существует и живёт на сцене
private static void EnsureRunner()
{
if (_runner != null) return;
var go = new GameObject("_BlinkControllerRunner");
Object.DontDestroyOnLoad(go);
_runner = go.AddComponent<Runner>();
go.hideFlags = HideFlags.HideAndDontSave;
}
// ---- Адресные команды для подсветок 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) ----
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
/// <summary>Установить программу на канале LED (1-4) для конкретного игрока</summary>
public static void SetLEDChannel(int channel, int program, int playerId)
{
if (channel < 1 || channel > 4)
{
Debug.LogWarning($"BlinkController: Неверный канал LED {channel}. Допустимо 1-4");
return;
}
if (program < 0 || program > 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})");
}
/// <summary>Установить программу на всех LED каналах для конкретного игрока</summary>
public static void SetLEDAll(int program, int playerId)
{
if (program < 0 || program > 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})");
}
/// <summary>Установить программу на всех LED каналах глобально (broadcast)</summary>
public static void SetLEDAllGlobal(int program)
{
if (program < 0 || program > 99)
{
Debug.LogWarning($"BlinkController: Неверная программа LED {program}. Допустимо 0-99");
return;
}
string cmd = $"80{program:D2}";
ComWrapper.Send(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); // Радуга
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); // Радуга на всех для игрока
// Глобальные команды (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) ----
public enum ColorSequence
{
RGB = 0, // 890 - Red, Green, Blue
RBG = 1, // 891 - Red, Blue, Green
BRG = 2, // 892 - Blue, Red, Green
BGR = 3, // 893 - Blue, Green, Red
GRB = 4, // 894 - Green, Red, Blue
GBR = 5 // 895 - Green, Blue, Red
}
/// <summary>Установить цветовое чередование. Если playerId = 0 или не указан - отправка глобально</summary>
public static void SetColorSequence(ColorSequence sequence, int playerId = 0)
{
string cmd = $"89{(int)sequence}";
if (playerId > 0)
{
ComWrapper.Send(cmd, playerId);
Debug.Log($"BlinkController: Цветовое чередование {sequence} для игрока {playerId} (cmd: {cmd})");
}
else
{
ComWrapper.Send(cmd);
Debug.Log($"BlinkController: Цветовое чередование {sequence} глобально (cmd: {cmd})");
}
}
// ---- Произвольный RGB цвет (команда 08RRRGGGBBB) ----
/// <summary>Установить произвольный RGB цвет для LED. Если playerId = 0 - отправка глобально</summary>
/// <param name="r">Красный канал (0-255)</param>
/// <param name="g">Зеленый канал (0-255)</param>
/// <param name="b">Синий канал (0-255)</param>
/// <param name="playerId">ID игрока (0 = глобально)</param>
/// <summary>Установить произвольный RGB цвет на конкретный канал (81-84)</summary>
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
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})");
}
else
{
ComWrapper.Send(cmd);
Debug.Log($"BlinkController: LED канал {channel} произвольный цвет RGB({r},{g},{b}) глобально (cmd: {cmd})");
}
}
/// <summary>Установить произвольный RGB цвет на все каналы (80)</summary>
public static void SetLEDCustomColor(int r, int g, int b, int playerId = 0)
{
// Ограничиваем значения 0-255
r = Mathf.Clamp(r, 0, 255);
g = Mathf.Clamp(g, 0, 255);
b = Mathf.Clamp(b, 0, 255);
// Формат: 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})");
}
else
{
ComWrapper.Send(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); // Желтый
// ---- Мигание LED с возвратом к базовому цвету ----
private static readonly Dictionary<int, int> _playerBaseLEDColor = new Dictionary<int, int>();
// Храним кастомные RGB цвета для каждого игрока (если установлен)
private static readonly Dictionary<int, Vector3Int> _playerCustomLEDColor = new Dictionary<int, Vector3Int>();
/// <summary>Установить базовый цвет LED для игрока (к которому возвращаемся после мигания)</summary>
public static void SetLEDBaseColor(int playerId, int program)
{
_playerBaseLEDColor[playerId] = program;
_playerCustomLEDColor.Remove(playerId); // Убираем кастомный цвет
SetLEDAll(program, playerId);
Debug.Log($"BlinkController: Базовый LED цвет для игрока {playerId} установлен: {program}");
}
/// <summary>Установить базовый кастомный RGB цвет LED для игрока</summary>
public static void SetLEDBaseCustomColor(int playerId, int r, int g, int b)
{
_playerBaseLEDColor[playerId] = -1; // Маркер кастомного цвета
_playerCustomLEDColor[playerId] = new Vector3Int(r, g, b);
SetLEDCustomColor(r, g, b, playerId);
Debug.Log($"BlinkController: Базовый кастомный LED цвет для игрока {playerId} установлен: RGB({r},{g},{b})");
}
/// <summary>Получить базовый цвет LED для игрока</summary>
public static int GetLEDBaseColor(int playerId)
{
return _playerBaseLEDColor.TryGetValue(playerId, out int color) ? color : 5; // По умолчанию синий (5)
}
/// <summary>Восстановить базовый цвет LED для игрока (включая кастомные RGB)</summary>
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);
}
}
/// <summary>Мигание LED N раз цветом, затем возврат к базовому цвету</summary>
public static void BlinkLED(int playerId, int colorProgram, int times, float interval = 0.3f)
{
EnsureRunner();
_runner.StartCoroutine(BlinkLEDRoutine(playerId, colorProgram, times, interval));
}
private static IEnumerator BlinkLEDRoutine(int playerId, int colorProgram, int times, float interval)
{
for (int i = 0; i < times; i++)
{
// Включить цвет
SetLEDAll(colorProgram, playerId);
yield return new WaitForSeconds(interval);
// Вернуть базовый цвет (включая кастомные 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);
public static void BlinkLEDGreen(int playerId, int times = 1, float interval = 0.3f)
=> BlinkLED(playerId, 4, times, interval);
public static void BlinkLEDBlue(int playerId, int times = 1, float interval = 0.3f)
=> BlinkLED(playerId, 5, times, interval);
// ---- Start/Stop мигания ----
public static void StartBlinkA(int playerId, float interval = -1f)
{
// Оставляем обратную совместимость: бесконечное мигание
StartBlinkATimes(playerId, -1, interval);
}
// Мигание N раз (N <= 0 = бесконечно)
public static void StartBlinkATimes(int playerId, int times, float interval = -1f)
{
StopBlinkA(playerId);
EnsureRunner();
string key = KeyFor("A", playerId);
var coro = _runner.StartCoroutine(BlinkRoutinePlayer(key, playerId, "2170", "2171", interval > 0 ? interval : DefaultInterval, times));
_coroutines[key] = coro;
}
public static void StopBlinkA(int playerId)
{
string key = KeyFor("A", playerId);
StopAndRemove(key);
LightAOff(playerId);
}
public static void StartBlinkB(int playerId, float interval = -1f)
{
// Оставляем обратную совместимость: бесконечное мигание
StartBlinkBTimes(playerId, -1, interval);
}
// Мигание N раз (N <= 0 = бесконечно)
public static void StartBlinkBTimes(int playerId, int times, float interval = -1f)
{
StopBlinkB(playerId);
EnsureRunner();
string key = KeyFor("B", playerId);
var coro = _runner.StartCoroutine(BlinkRoutinePlayer(key, playerId, "2130", "2131", interval > 0 ? interval : DefaultInterval, times));
_coroutines[key] = coro;
}
public static void StopBlinkB(int playerId)
{
string key = KeyFor("B", playerId);
StopAndRemove(key);
LightBOff(playerId);
}
// Глобальная C
public static void StartBlinkC(float interval = -1f)
{
// Оставляем обратную совместимость: бесконечное мигание
StartBlinkCTimes(-1, interval);
}
// Мигание N раз глобально (N <= 0 = бесконечно)
public static void StartBlinkCTimes(int times, float interval = -1f)
{
StopBlinkC();
EnsureRunner();
string key = KeyFor("C", 0);
var coro = _runner.StartCoroutine(BlinkRoutineGlobal(key, "400", "401", interval > 0 ? interval : DefaultInterval, times));
_coroutines[key] = coro;
}
public static void StopBlinkC()
{
string key = KeyFor("C", 0);
StopAndRemove(key);
LightCOff();
}
private static void StopAndRemove(string key)
{
if (_coroutines.TryGetValue(key, out Coroutine c))
{
if (_runner != null && c != null) _runner.StopCoroutine(c);
_coroutines.Remove(key);
}
}
private static string KeyFor(string type, int playerId) => $"{type}_{playerId}";
private static IEnumerator BlinkRoutinePlayer(string key, int playerId, string onCmd, string offCmd, float interval, int repeatCount)
{
// repeatCount <= 0 => бесконечно
if (repeatCount <= 0)
{
while (true)
{
ComWrapper.Send(onCmd, playerId);
yield return new WaitForSeconds(interval);
ComWrapper.Send(offCmd, playerId);
yield return new WaitForSeconds(interval);
}
}
// Повторяем заданное количество раз
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 { }
}
private static IEnumerator BlinkRoutineGlobal(string key, string onCmd, string offCmd, float interval, int repeatCount)
{
if (repeatCount <= 0)
{
while (true)
{
ComWrapper.Send(onCmd);
yield return new WaitForSeconds(interval);
ComWrapper.Send(offCmd);
yield return new WaitForSeconds(interval);
}
}
for (int i = 0; i < repeatCount; i++)
{
ComWrapper.Send(onCmd);
yield return new WaitForSeconds(interval);
ComWrapper.Send(offCmd);
if (i < repeatCount - 1) yield return new WaitForSeconds(interval);
}
ComWrapper.Send(offCmd);
try { _coroutines.Remove(key); } catch { }
}
// Вспомогательные: мгновенный вкл/выкл всех
public static void StopAll()
{
var keys = new List<string>(_coroutines.Keys);
foreach (var k in keys) StopAndRemove(k);
}
// Включить все подсветки (A и B — для обоих игроков, C — глобально)
public static void TurnAllOn()
{
Debug.Log("BlinkController: TurnAllOn");
// A и B адресные — для игроков 1 и 2
LightAOn(1);
LightAOn(2);
LightBOn(1);
LightBOn(2);
// 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 -> радуга
// Устанавливаем базовый цвет - радуга (для мигания будет возврат к ней)
_playerBaseLEDColor[1] = 9;
_playerBaseLEDColor[2] = 9;
}
// Выключить все подсветки (и остановить корутины)
public static void TurnAllOff()
{
Debug.Log("BlinkController: TurnAllOff");
// Остановим все мигания
StopAll();
// Отправим команды выключения адресно для игроков 1 и 2
try
{
LightAOff(1);
LightAOff(2);
LightBOff(1);
LightBOff(2);
// C — глобально
LightCOff();
// LED - выключить все каналы глобально
LEDOffAllGlobal();
}
catch (System.Exception e)
{
Debug.LogWarning($"BlinkController: ошибка при TurnAllOff: {e.Message}");
}
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
private static void OnRuntimeInitialized()
{
// Подписываемся на событие инициализации ComWrapper — включим подсветки, когда COM будет готов
try
{
ComWrapper.OnInitialize += TurnAllOn;
// Подписываемся, чтобы выключить подсветки ДО закрытия портов
ComWrapper.OnBeforeClose += TurnAllOff;
}
catch
{
// Если ComWrapper ещё не существует или не определён, проигнорируем — вызов произойдёт при инициализации
}
// Создаём раннер заранее, чтобы он был доступен и гарантированно существовал
EnsureRunner();
// Подпишемся на событие выхода приложения — гарантированно выключит подсветки и почистит подписки
Application.quitting += OnApplicationQuitting;
}
// Обработчик выхода приложения: отписываемся от событий ComWrapper
private static void OnApplicationQuitting()
{
try { ComWrapper.OnInitialize -= TurnAllOn; } catch { }
try { ComWrapper.OnBeforeClose -= TurnAllOff; } catch { }
try { TurnAllOff(); } catch { }
if (_runner != null)
{
Object.Destroy(_runner.gameObject);
_runner = null;
}
}
}