using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using RR.Game2D.Core.Managers; #if ENABLE_INPUT_SYSTEM using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; /// /// Реализация системы ввода с клавиатуры для тестирования и разработки. /// Эмулирует работу периферийной платы через стандартные клавиши клавиатуры. /// /// /// Соответствие клавиш и пинов: /// - Игрок 1: WASD + Space /// - Игрок 2: Стрелки + RightControl /// - Служебные: 1, 2, 3, F5 /// - Датчики хопера: O, P /// public class KeyboardInput : InputSource { private int currentPlayer = 1;//ПЕРЕОПРЕДЕЛИТЕ ОПРЕДЕЛЕНИЕ ТЕКУЩЕГО ИГРОКА ПОД СВОЮ СИСТЕМУ. private GameManager gm;//я получаю кто сейчас ходит через этого менеджера. Если у вас другое, допишите. // Кэш Key -> KeyControl + ссылка на то, для какой клавиатуры он построен private static readonly Dictionary _keyCache = new(); private static Keyboard _cachedKeyboard; private static void EnsureKeyboardCache() { var kb = Keyboard.current; if (kb == null) { _keyCache.Clear(); _cachedKeyboard = null; return; } // Если клавиатура сменилась (или кэш пуст) — перестроить if (_cachedKeyboard == kb && _keyCache.Count != 0) return; _cachedKeyboard = kb; _keyCache.Clear(); var keys = kb.allKeys; if (keys.Count <= 0) return; foreach (var kc in keys.Where(kc => kc != null)) { _keyCache[kc.keyCode] = kc; } } private static KeyControl GetKeyControl(Key key) { if (key == Key.None) return null; // Можем спокойно вызываться даже в самом раннем кадре EnsureKeyboardCache(); return _cachedKeyboard == null ? null : _keyCache.GetValueOrDefault(key); } // --- адаптер: из твоего старого KeyCode -> в новый Key --- private static Key ToNewKey(UnityEngine.KeyCode old) { // Пытаемся по имени (у многих совпадают) if (Enum.TryParse(old.ToString(), true, out var k)) return k; // Несовпадающие названия — руками: return old switch { KeyCode.Return => Key.Enter, KeyCode.KeypadEnter => Key.NumpadEnter, KeyCode.LeftControl => Key.LeftCtrl, KeyCode.RightControl => Key.RightCtrl, KeyCode.LeftShift => Key.LeftShift, KeyCode.RightShift => Key.RightShift, KeyCode.LeftAlt => Key.LeftAlt, KeyCode.RightAlt => Key.RightAlt, KeyCode.LeftCommand => Key.LeftMeta, KeyCode.RightCommand => Key.RightMeta, KeyCode.None => Key.None, _ => Key.None // если нужен редкий кей — добавь сюда }; } // Удобные обёртки private static bool IsPressed(Key k) { if (k == Key.None) return false; var c = GetKeyControl(k); return c is { isPressed: true }; } private static bool WasPressedThisFrame(Key k) { if (k == Key.None) return false; var c = GetKeyControl(k); return c is { wasPressedThisFrame: true }; } private static bool WasReleasedThisFrame(Key k) { if (k == Key.None) return false; var c = GetKeyControl(k); return c is { wasReleasedThisFrame: true }; } /// Проверяет состояние кнопки по её физическому пину через клавиатуру (Input System) public override bool GetButton(ButtonPinMapping button) => IsPressed(ToNewKey(getKeyCode(button))); /// Проверяет нажатие кнопки по пину в текущем кадре (Input System) public override bool GetButtonDown(ButtonPinMapping button) => WasPressedThisFrame(ToNewKey(getKeyCode(button))); /// Проверяет отпускание кнопки по пину в текущем кадре (Input System) public override bool GetButtonUp(ButtonPinMapping button) => WasReleasedThisFrame(ToNewKey(getKeyCode(button))); //---------------------------------------------------------------------------------- /// По числовому индексу пина public override bool GetButton(int pinIndex) => IsPressed(ToNewKey(getKeyCode(pinIndex))); public override bool GetButtonDown(int pinIndex) => WasPressedThisFrame(ToNewKey(getKeyCode(pinIndex))); public override bool GetButtonUp(int pinIndex) => WasReleasedThisFrame(ToNewKey(getKeyCode(pinIndex))); //---------------------------------------------------------------------------------- /// Действия текущего игрока (для клавиатуры — это по сути игрок 1) public override bool GetButtonCurrentPlayer(PlayerAction button) => GetButton(GetButtonForCurrentPlayer(button)); public override bool GetButtonDownCurrentPlayer(PlayerAction button) => GetButtonDown(GetButtonForCurrentPlayer(button)); public override bool GetButtonUpCurrentPlayer(PlayerAction button) => GetButtonUp(GetButtonForCurrentPlayer(button)); //---------------------------------------------------------------------------------- /// Любой игрок (для клавиатуры — эквивалент текущего) public override bool GetButtonAnyPlayer(PlayerAction button) => GetButtonCurrentPlayer(button); public override bool GetButtonDownAnyPlayer(PlayerAction button) => GetButtonDownCurrentPlayer(button); public override bool GetButtonUpAnyPlayer(PlayerAction button) => GetButtonUpCurrentPlayer(button); //---------------------------------------------------------------------------------- /// /// Инициализирует клавиатурный ввод. Всегда возвращает true так как клавиатура всегда доступна. /// /// Всегда true public override bool Initialize() { gm = GameManager.Instance; return true; } // ================================================================================ // ВСПОМОГАТЕЛЬНЫЕ МЕТОДЫ // ================================================================================ /// /// Определяет кнопку текущего игрока для указанного действия. /// Для клавиатуры всегда возвращает кнопку первого игрока. /// private ButtonPinMapping GetButtonForCurrentPlayer(PlayerAction button) { var buttons = _buttonByAction(button); return currentPlayer == 1 ? buttons.player1button : buttons.player2button; } /// /// Проверяет действие для обоих игроков используя указанную функцию проверки. /// private bool CheckBothPlayers(PlayerAction action, System.Func buttonFunc) { var buttons = _buttonByAction(action); return buttonFunc(buttons.player1button) || buttonFunc(buttons.player2button); } /// /// Возвращает соответствие между игровыми действиями и физическими кнопками игроков. /// private (ButtonPinMapping player1button, ButtonPinMapping player2button) _buttonByAction(PlayerAction playerAction) => playerAction switch { PlayerAction.up => (ButtonPinMapping.up1, ButtonPinMapping.up2), PlayerAction.down => (ButtonPinMapping.down1, ButtonPinMapping.down2), PlayerAction.left => (ButtonPinMapping.left1, ButtonPinMapping.left2), PlayerAction.right => (ButtonPinMapping.right1, ButtonPinMapping.right2), PlayerAction.action => (ButtonPinMapping.action1, ButtonPinMapping.action2), PlayerAction.extra => (ButtonPinMapping.extra, ButtonPinMapping.extra), _ => throw new System.ArgumentException($"Unknown player action: {playerAction}") }; /// /// Преобразует физический пин в соответствующую клавишу клавиатуры. /// private KeyCode getKeyCode(ButtonPinMapping button) { return button switch { ButtonPinMapping.up1 => KeyCode.W, ButtonPinMapping.right1 => KeyCode.D, ButtonPinMapping.down1 => KeyCode.S, ButtonPinMapping.left1 => KeyCode.A, ButtonPinMapping.action1 => KeyCode.Space, ButtonPinMapping.up2 => KeyCode.UpArrow, ButtonPinMapping.right2 => KeyCode.RightArrow, ButtonPinMapping.down2 => KeyCode.DownArrow, ButtonPinMapping.left2 => KeyCode.LeftArrow, ButtonPinMapping.action2 => KeyCode.RightControl, ButtonPinMapping.startOnePlayer => KeyCode.Alpha1, ButtonPinMapping.startTwoPlayers => KeyCode.Alpha2, ButtonPinMapping.secret => KeyCode.F5, ButtonPinMapping.extra => KeyCode.Alpha3, ButtonPinMapping.hasPrize => KeyCode.O, ButtonPinMapping.prizeGiven => KeyCode.P, _ => KeyCode.None, }; } /// /// Преобразует числовой индекс пина в соответствующую клавишу клавиатуры. /// Используется для методов с прямым доступом по индексу. /// private KeyCode getKeyCode(int pinIndex) { return pinIndex switch { 0 => KeyCode.W, // up1 1 => KeyCode.D, // right1 2 => KeyCode.S, // down1 3 => KeyCode.A, // left1 4 => KeyCode.Space, // action1 5 => KeyCode.UpArrow, // up2 6 => KeyCode.RightArrow, // right2 7 => KeyCode.DownArrow, // down2 8 => KeyCode.LeftArrow, // left2 9 => KeyCode.RightControl, // action2 10 => KeyCode.Alpha1, // startOnePlayer 11 => KeyCode.Alpha2, // startTwoPlayers 12 => KeyCode.F5, // secret 13 => KeyCode.Alpha3, // extra 14 => KeyCode.O, // hasPrize 15 => KeyCode.P, // prizeGiven _ => KeyCode.None, // невалидный индекс }; } } #else /// /// Реализация системы ввода с клавиатуры для тестирования и разработки. /// Эмулирует работу периферийной платы через стандартные клавиши клавиатуры. /// /// /// Соответствие клавиш и пинов: /// - Игрок 1: WASD + Space /// - Игрок 2: Стрелки + RightControl /// - Служебные: 1, 2, 3, F5 /// - Датчики хопера: O, P /// public class KeyboardInput : InputSource { private int currentPlayer = 1;//ПЕРЕОПРЕДЕЛИТЕ ОПРЕДЕЛЕНИЕ ТЕКУЩЕГО ИГРОКА ПОД СВОЮ СИСТЕМУ. private GameManager gm;//я получаю кто сейчас ходит через этого менеджера. Если у вас другое, допишите. /// Проверяет состояние кнопки по её физическому пину через клавиатуру public override bool GetButton(ButtonPinMapping button) => Input.GetKey(getKeyCode(button)); /// Проверяет нажатие кнопки по пину в текущем кадре через клавиатуру public override bool GetButtonDown(ButtonPinMapping button) => Input.GetKeyDown(getKeyCode(button)); /// Проверяет отпускание кнопки по пину в текущем кадре через клавиатуру public override bool GetButtonUp(ButtonPinMapping button) => Input.GetKeyUp(getKeyCode(button)); //---------------------------------------------------------------------------------- /// Проверяет состояние кнопки по числовому индексу пина через клавиатуру public override bool GetButton(int pinIndex) => Input.GetKey(getKeyCode(pinIndex)); /// Проверяет нажатие кнопки по индексу пина в текущем кадре через клавиатуру public override bool GetButtonDown(int pinIndex) => Input.GetKeyDown(getKeyCode(pinIndex)); /// Проверяет отпускание кнопки по индексу пина в текущем кадре через клавиатуру public override bool GetButtonUp(int pinIndex) => Input.GetKeyUp(getKeyCode(pinIndex)); //---------------------------------------------------------------------------------- /// Проверяет состояние действия для текущего игрока (всегда игрок 1 для клавиатуры) public override bool GetButtonCurrentPlayer(PlayerAction button) => GetButton(GetButtonForCurrentPlayer(button)); /// Проверяет начало действия текущим игроком в текущем кадре public override bool GetButtonDownCurrentPlayer(PlayerAction button) => GetButtonDown(GetButtonForCurrentPlayer(button)); /// Проверяет завершение действия текущим игроком в текущем кадре public override bool GetButtonUpCurrentPlayer(PlayerAction button) => GetButtonUp(GetButtonForCurrentPlayer(button)); //---------------------------------------------------------------------------------- /// Проверяет состояние действия для любого из игроков (игрок 1 ИЛИ игрок 2) public override bool GetButtonAnyPlayer(PlayerAction button) => CheckBothPlayers(button, GetButton); /// Проверяет начало действия любым игроком в текущем кадре public override bool GetButtonDownAnyPlayer(PlayerAction button) => CheckBothPlayers(button, GetButtonDown); /// Проверяет завершение действия любым игроком в текущем кадре public override bool GetButtonUpAnyPlayer(PlayerAction button) => CheckBothPlayers(button, GetButtonUp); //---------------------------------------------------------------------------------- /// /// Инициализирует клавиатурный ввод. Всегда возвращает true так как клавиатура всегда доступна. /// /// Всегда true public override bool Initialize() => gm = GameManager.Instance; // ================================================================================ // ВСПОМОГАТЕЛЬНЫЕ МЕТОДЫ // ================================================================================ /// /// Определяет кнопку текущего игрока для указанного действия. /// Для клавиатуры всегда возвращает кнопку первого игрока. /// private ButtonPinMapping GetButtonForCurrentPlayer(PlayerAction button) { var buttons = _buttonByAction(button); return currentPlayer == 1 ? buttons.player1button : buttons.player2button; } /// /// Проверяет действие для обоих игроков используя указанную функцию проверки. /// private bool CheckBothPlayers(PlayerAction action, System.Func buttonFunc) { var buttons = _buttonByAction(action); return buttonFunc(buttons.player1button) || buttonFunc(buttons.player2button); } /// /// Возвращает соответствие между игровыми действиями и физическими кнопками игроков. /// private (ButtonPinMapping player1button, ButtonPinMapping player2button) _buttonByAction(PlayerAction playerAction) => playerAction switch { PlayerAction.up => (ButtonPinMapping.up1, ButtonPinMapping.up2), PlayerAction.down => (ButtonPinMapping.down1, ButtonPinMapping.down2), PlayerAction.left => (ButtonPinMapping.left1, ButtonPinMapping.left2), PlayerAction.right => (ButtonPinMapping.right1, ButtonPinMapping.right2), PlayerAction.action => (ButtonPinMapping.action1, ButtonPinMapping.action2), PlayerAction.extra => (ButtonPinMapping.extra, ButtonPinMapping.extra), _ => throw new System.ArgumentException($"Unknown player action: {playerAction}") }; /// /// Преобразует физический пин в соответствующую клавишу клавиатуры. /// private KeyCode getKeyCode(ButtonPinMapping button) { return button switch { ButtonPinMapping.up1 => KeyCode.W, ButtonPinMapping.right1 => KeyCode.D, ButtonPinMapping.down1 => KeyCode.S, ButtonPinMapping.left1 => KeyCode.A, ButtonPinMapping.action1 => KeyCode.Space, ButtonPinMapping.up2 => KeyCode.UpArrow, ButtonPinMapping.right2 => KeyCode.RightArrow, ButtonPinMapping.down2 => KeyCode.DownArrow, ButtonPinMapping.left2 => KeyCode.LeftArrow, ButtonPinMapping.action2 => KeyCode.RightControl, ButtonPinMapping.startOnePlayer => KeyCode.Alpha1, ButtonPinMapping.startTwoPlayers => KeyCode.Alpha2, ButtonPinMapping.secret => KeyCode.F5, ButtonPinMapping.extra => KeyCode.Alpha3, ButtonPinMapping.hasPrize => KeyCode.O, ButtonPinMapping.prizeGiven => KeyCode.P, _ => KeyCode.None, }; } /// /// Преобразует числовой индекс пина в соответствующую клавишу клавиатуры. /// Используется для методов с прямым доступом по индексу. /// private KeyCode getKeyCode(int pinIndex) { return pinIndex switch { 0 => KeyCode.W, // up1 1 => KeyCode.D, // right1 2 => KeyCode.S, // down1 3 => KeyCode.A, // left1 4 => KeyCode.Space, // action1 5 => KeyCode.UpArrow, // up2 6 => KeyCode.RightArrow, // right2 7 => KeyCode.DownArrow, // down2 8 => KeyCode.LeftArrow, // left2 9 => KeyCode.RightControl, // action2 10 => KeyCode.Alpha1, // startOnePlayer 11 => KeyCode.Alpha2, // startTwoPlayers 12 => KeyCode.F5, // secret 13 => KeyCode.Alpha3, // extra 14 => KeyCode.O, // hasPrize 15 => KeyCode.P, // prizeGiven _ => KeyCode.None, // невалидный индекс }; } } #endif