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;
namespace RR.Common.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
}