using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.LowLevel; namespace RR.Common.Controls { /// /// Адаптер между системой ввода COM и Unity Input System. /// Создаёт виртуальный Gamepad и транслирует состояние кнопок из: /// - действий игроков (PlayerAction) /// - физических пинов (int индексы) /// [DefaultExecutionOrder(-100)] public class ComInputToInputSystemAdapter : MonoBehaviour { private const string VirtualGamepadProduct = "COM Bridge Gamepad"; private const string VirtualGamepadManufacturer = "Robotic Retailers"; [Header("Источник состояния")] [Tooltip("Если true — использовать In.GetButtonAnyPlayer, иначе — In.GetButtonCurrentPlayer")] [SerializeField] private bool useAnyPlayer = true; [Header("Маппинг действий (PlayerAction -> Gamepad)")] [SerializeField] private List actionBindings = new(); [Header("Маппинг пинов (индекс пина -> Gamepad)")] [SerializeField] private List pinBindings = new(); [Header("Отладка")] [SerializeField] private bool logCreateDevice = true; private static Gamepad _virtualPad; private static bool _deviceCreated; private GamepadState _currentState; #region Структуры маппинга [Serializable] public struct ActionBinding { public PlayerAction action; public BindingTarget target; public GamepadButton button; public DpadDirection dpadDirection; } [Serializable] public struct PinBinding { public int pinIndex; public BindingTarget target; public GamepadButton button; public DpadDirection dpadDirection; } public enum BindingTarget { Button, Dpad } public enum DpadDirection { Up, Down, Left, Right } #endregion #region Unity Lifecycle // Если нет общего инициализатора, можно раскомментировать Awake // private void Awake() // { // Initialize(); // } private void OnEnable() { EnsureVirtualGamepadExists(); } private void Update() { if (_virtualPad == null) return; UpdateGamepadState(); InputSystem.QueueStateEvent(_virtualPad, _currentState); } #endregion #region Initialization public void Initialize() { EnsureVirtualGamepadExists(); InitializeDefaultBindings(); } #endregion #region State Update private void UpdateGamepadState() { _currentState = default; ProcessActionBindings(); ProcessPinBindings(); } private void ProcessActionBindings() { foreach (ActionBinding binding in actionBindings) { bool isPressed = useAnyPlayer ? In.GetButtonAnyPlayer(binding.action) : In.GetButtonCurrentPlayer(binding.action); if (isPressed) { ApplyBinding(ref _currentState, binding.target, binding.button, binding.dpadDirection); } } } private void ProcessPinBindings() { foreach (PinBinding binding in pinBindings) { if (In.GetButton(binding.pinIndex)) { ApplyBinding(ref _currentState, binding.target, binding.button, binding.dpadDirection); } } } #endregion #region Binding Logic private static void ApplyBinding( ref GamepadState state, BindingTarget target, GamepadButton button, DpadDirection direction) { if (target == BindingTarget.Button) { state = state.WithButton(button); } else // Dpad { GamepadButton dpadButton = direction switch { DpadDirection.Up => GamepadButton.DpadUp, DpadDirection.Down => GamepadButton.DpadDown, DpadDirection.Left => GamepadButton.DpadLeft, DpadDirection.Right => GamepadButton.DpadRight, _ => GamepadButton.DpadUp }; state = state.WithButton(dpadButton); } } #endregion #region Virtual Gamepad Management private void EnsureVirtualGamepadExists() { if (_deviceCreated && _virtualPad != null) return; // Проверяем, не создан ли уже геймпад if (TryFindExistingVirtualGamepad(out Gamepad existingGamepad)) { _virtualPad = existingGamepad; _deviceCreated = true; return; } CreateVirtualGamepad(); } private static bool TryFindExistingVirtualGamepad(out Gamepad gamepad) { gamepad = null; foreach (InputDevice device in InputSystem.devices) { if (device is not Gamepad { description: { product: VirtualGamepadProduct } } gp) continue; gamepad = gp; return true; } return false; } private void CreateVirtualGamepad() { InputDeviceDescription description = new() { interfaceName = "Virtual", deviceClass = "Gamepad", product = VirtualGamepadProduct, manufacturer = VirtualGamepadManufacturer }; _virtualPad = (Gamepad)InputSystem.AddDevice(description); _deviceCreated = true; if (logCreateDevice) { Debug.Log($"[ComInputAdapter] Виртуальный Gamepad создан: {_virtualPad.name}"); } } #endregion #region Default Bindings private void InitializeDefaultBindings() { if (actionBindings.Count > 0) return; AddDefaultActionBindings(); } private void AddDefaultActionBindings() { // Маппинг стрелок на D-Pad TryAddActionBinding(PlayerAction.up, BindingTarget.Dpad, GamepadButton.North, DpadDirection.Up); TryAddActionBinding(PlayerAction.down, BindingTarget.Dpad, GamepadButton.South, DpadDirection.Down); TryAddActionBinding(PlayerAction.left, BindingTarget.Dpad, GamepadButton.West, DpadDirection.Left); TryAddActionBinding(PlayerAction.right, BindingTarget.Dpad, GamepadButton.East, DpadDirection.Right); // Кнопки действий TryAddActionBinding(PlayerAction.action, BindingTarget.Button, GamepadButton.South, DpadDirection.Up); } private void TryAddActionBinding( PlayerAction action, BindingTarget target, GamepadButton button, DpadDirection direction) { try { actionBindings.Add(new ActionBinding { action = action, target = target, button = button, dpadDirection = direction }); } catch (Exception e) { Debug.LogWarning($"[ComInputAdapter] Не удалось добавить привязку для {action}: {e.Message}"); } } #endregion } }