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
}
}