260 lines
7.2 KiB
C#
260 lines
7.2 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using UnityEngine.InputSystem;
|
||
using UnityEngine.InputSystem.Layouts;
|
||
using UnityEngine.InputSystem.LowLevel;
|
||
|
||
/// <summary>
|
||
/// Адаптер между системой ввода COM и Unity Input System.
|
||
/// Создаёт виртуальный Gamepad и транслирует состояние кнопок из:
|
||
/// - действий игроков (PlayerAction)
|
||
/// - физических пинов (int индексы)
|
||
/// </summary>
|
||
[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<ActionBinding> actionBindings = new();
|
||
|
||
[Header("Маппинг пинов (индекс пина -> Gamepad)")]
|
||
[SerializeField] private List<PinBinding> 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
|
||
} |