using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.LowLevel; /// /// Мост из твоего фасада In (COM/клава и т.д.) в новый Input System. /// Создаёт виртуальный Gamepad и обновляет его состояние на основе: /// - действий игроков (PlayerAction), или /// - физических пинов (int / ButtonPinMapping) /// Выбирай способ через списки маппинга в инспекторе. /// [DefaultExecutionOrder(-100)] // пораньше, чтобы UI увидел ввод public class ComInputToInputSystemAdapter : MonoBehaviour { [Header("Источник состояния")] [Tooltip("Если true — брать состояние из In.GetButton* AnyPlayer; если false — из CurrentPlayer.")] public bool useAnyPlayer = true; [Header("Маппинг по действиям (PlayerAction -> Gamepad)")] public List actionBindings = new(); [Header("Маппинг по пинам (индекс пина -> Gamepad)")] public List pinBindings = new(); [Header("Отладка")] public bool logCreateDevice = true; private static Gamepad _virtualPad; private GamepadState _state; // Защита от дублей при отключённом Domain Reload private static bool _deviceCreated; [Serializable] public struct ActionBinding { public PlayerAction action; // твой enum public TargetTarget target; public GamepadButton button; // если target == Button public DpadDir dpadDirection; // если target == Dpad } [Serializable] public struct PinBinding { public int pinIndex; // твой физический индекс пина public TargetTarget target; public GamepadButton button; // если target == Button public DpadDir dpadDirection; // если target == Dpad } public enum TargetTarget { Button, Dpad } public enum DpadDir { Up, Down, Left, Right } private void Awake() { EnsureVirtualGamepad(); TryAddDefaultBindingsIfEmpty(); } private void OnEnable() => EnsureVirtualGamepad(); private void OnDisable() { // Обычно устройство не удаляем, чтобы не ломать PlayerInput при живых сценах. // Если нужно строго чистить — раскомментируй: // if (_virtualPad != null) { InputSystem.RemoveDevice(_virtualPad); _virtualPad = null; _deviceCreated = false; } } private void Update() { if (_virtualPad == null) return; _state = default; // 1) По действиям for (int i = 0; i < actionBindings.Count; i++) { var b = actionBindings[i]; bool pressed = useAnyPlayer ? In.GetButtonAnyPlayer(b.action) : In.GetButtonCurrentPlayer(b.action); ApplyBinding(ref _state, b.target, b.button, b.dpadDirection, pressed); } // 2) По пинам for (int i = 0; i < pinBindings.Count; i++) { var b = pinBindings[i]; bool pressed = In.GetButton(b.pinIndex); ApplyBinding(ref _state, b.target, b.button, b.dpadDirection, pressed); } InputSystem.QueueStateEvent(_virtualPad, _state); // Обновлять InputSystem вручную не нужно (обновится в цикле Unity) } private static void ApplyBinding(ref GamepadState st, TargetTarget target, GamepadButton btn, DpadDir dir, bool pressed) { if (!pressed) return; if (target == TargetTarget.Button) { st = st.WithButton(btn, true); } else { switch (dir) { case DpadDir.Up: st = st.WithButton(GamepadButton.DpadUp, true); break; case DpadDir.Down: st = st.WithButton(GamepadButton.DpadDown, true); break; case DpadDir.Left: st = st.WithButton(GamepadButton.DpadLeft, true); break; case DpadDir.Right: st = st.WithButton(GamepadButton.DpadRight, true); break; } } } private void EnsureVirtualGamepad() { if (_deviceCreated && _virtualPad != null) return; // ищем уже созданный «COM Bridge», чтобы не дублировать foreach (var dev in InputSystem.devices) { if (dev is Gamepad gp && gp.description.product == "COM Bridge Gamepad") { _virtualPad = gp; _deviceCreated = true; return; } } var desc = new InputDeviceDescription { interfaceName = "Virtual", deviceClass = "Gamepad", product = "COM Bridge Gamepad", manufacturer = "YourCompany" }; _virtualPad = (Gamepad)InputSystem.AddDevice(desc); _deviceCreated = true; if (logCreateDevice) Debug.Log("[InputBridge] Виртуальный Gamepad создан: " + _virtualPad); } private void TryAddDefaultBindingsIfEmpty() { // Если пусто — добавим базовый маппинг стрелок на D-Pad. if (actionBindings.Count == 0) { // Переименуй ниже на свои реальные имена в enum PlayerAction, если отличаются. TryAddActionBinding(PlayerAction.up, TargetTarget.Dpad, GamepadButton.North, DpadDir.Up); TryAddActionBinding(PlayerAction.down, TargetTarget.Dpad, GamepadButton.South, DpadDir.Down); TryAddActionBinding(PlayerAction.left, TargetTarget.Dpad, GamepadButton.West, DpadDir.Left); TryAddActionBinding(PlayerAction.right, TargetTarget.Dpad, GamepadButton.East, DpadDir.Right); // Часто удобно: // Confirm -> South (A), Back/Cancel -> East (B) SafeTry(() => actionBindings.Add(new ActionBinding { action = PlayerAction.action, target = TargetTarget.Button, button = GamepadButton.South, dpadDirection = DpadDir.Up })); SafeTry(() => actionBindings.Add(new ActionBinding { action = PlayerAction.extra, target = TargetTarget.Button, button = GamepadButton.East, dpadDirection = DpadDir.Up })); } } private void TryAddActionBinding(PlayerAction action, TargetTarget target, GamepadButton btn, DpadDir dir) { SafeTry(() => actionBindings.Add(new ActionBinding { action = action, target = target, button = btn, dpadDirection = dir })); } private static void SafeTry(Action a) { try { a(); } catch { /* если в твоём enum нет таких значений — просто пропустим */ } } }