Danro/Assets/Scripts/Controls/ComInputToInputSystemAdapter.cs
Danil Iarosh 37e29ce3ab
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
Add new metadata files and update input handling for improved COM communication
2026-01-20 09:24:21 +03:00

329 lines
10 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.LowLevel;
namespace RR.Common.Controls
{
/// <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);
}
/// <summary>
/// Синхронизирует pinBindings с ButtonPinMapping.
/// Вызывать из контекстного меню в инспекторе.
/// </summary>
[ContextMenu("Синхронизировать pinBindings с ButtonPinMapping")]
private void SyncPinBindingsWithButtonPinMapping()
{
pinBindings.Clear();
// Left
pinBindings.Add(new PinBinding
{
pinIndex = (int)ButtonPinMapping.left1,
target = BindingTarget.Dpad,
button = GamepadButton.DpadLeft,
dpadDirection = DpadDirection.Left
});
// Right
pinBindings.Add(new PinBinding
{
pinIndex = (int)ButtonPinMapping.right1,
target = BindingTarget.Dpad,
button = GamepadButton.DpadRight,
dpadDirection = DpadDirection.Right
});
// Up
pinBindings.Add(new PinBinding
{
pinIndex = (int)ButtonPinMapping.up1,
target = BindingTarget.Dpad,
button = GamepadButton.DpadUp,
dpadDirection = DpadDirection.Up
});
// Down
pinBindings.Add(new PinBinding
{
pinIndex = (int)ButtonPinMapping.down1,
target = BindingTarget.Dpad,
button = GamepadButton.DpadDown,
dpadDirection = DpadDirection.Down
});
// Action
pinBindings.Add(new PinBinding
{
pinIndex = (int)ButtonPinMapping.action1,
target = BindingTarget.Button,
button = GamepadButton.South,
dpadDirection = DpadDirection.Up
});
// Start
pinBindings.Add(new PinBinding
{
pinIndex = (int)ButtonPinMapping.startOnePlayer,
target = BindingTarget.Button,
button = GamepadButton.Start,
dpadDirection = DpadDirection.Up
});
Debug.Log($"[ComInputAdapter] pinBindings синхронизированы с ButtonPinMapping. Всего: {pinBindings.Count}");
}
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
}
}