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 нет таких значений — просто пропустим */ }
}
}