220 lines
8.3 KiB
C#
220 lines
8.3 KiB
C#
|
#if (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT
|
||
|
using System;
|
||
|
using UnityEngine.InputSystem.Layouts;
|
||
|
using UnityEngine.InputSystem.Utilities;
|
||
|
|
||
|
namespace UnityEngine.InputSystem.Steam
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Adds support for Steam controllers.
|
||
|
/// </summary>
|
||
|
#if UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION
|
||
|
public
|
||
|
#else
|
||
|
internal
|
||
|
#endif
|
||
|
static class SteamSupport
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Wrapper around the Steam controller API.
|
||
|
/// </summary>
|
||
|
/// <remarks>
|
||
|
/// This must be set by user code for Steam controller support to become functional.
|
||
|
/// </remarks>
|
||
|
public static ISteamControllerAPI api
|
||
|
{
|
||
|
get { return s_API; }
|
||
|
set
|
||
|
{
|
||
|
s_API = value;
|
||
|
InstallHooks(s_API != null);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal static ISteamControllerAPI GetAPIAndRequireItToBeSet()
|
||
|
{
|
||
|
if (s_API == null)
|
||
|
throw new InvalidOperationException("ISteamControllerAPI implementation has not been set on SteamSupport");
|
||
|
return s_API;
|
||
|
}
|
||
|
|
||
|
internal static SteamHandle<SteamController>[] s_ConnectedControllers;
|
||
|
internal static SteamController[] s_InputDevices;
|
||
|
internal static int s_InputDeviceCount;
|
||
|
internal static bool s_HooksInstalled;
|
||
|
internal static ISteamControllerAPI s_API;
|
||
|
|
||
|
private const int STEAM_CONTROLLER_MAX_COUNT = 16;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Enable support for the Steam controller API.
|
||
|
/// </summary>
|
||
|
public static void Initialize()
|
||
|
{
|
||
|
// We use this as a base layout.
|
||
|
InputSystem.RegisterLayout<SteamController>();
|
||
|
|
||
|
if (api != null)
|
||
|
InstallHooks(true);
|
||
|
}
|
||
|
|
||
|
private static void InstallHooks(bool state)
|
||
|
{
|
||
|
Debug.Assert(api != null);
|
||
|
if (state && !s_HooksInstalled)
|
||
|
{
|
||
|
InputSystem.onBeforeUpdate += OnUpdate;
|
||
|
InputSystem.onActionChange += OnActionChange;
|
||
|
}
|
||
|
else if (!state && s_HooksInstalled)
|
||
|
{
|
||
|
InputSystem.onBeforeUpdate -= OnUpdate;
|
||
|
InputSystem.onActionChange -= OnActionChange;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static void OnActionChange(object mapOrAction, InputActionChange change)
|
||
|
{
|
||
|
// We only care about action map activations. Steam has no support for enabling or disabling
|
||
|
// individual actions and also has no support disabling sets once enabled (can only switch
|
||
|
// to different set).
|
||
|
if (change != InputActionChange.ActionMapEnabled)
|
||
|
return;
|
||
|
|
||
|
// See if the map has any bindings to SteamControllers.
|
||
|
// NOTE: We only support a single SteamController on any action map here. The first SteamController
|
||
|
// we find is the one we're doing all the work on.
|
||
|
var actionMap = (InputActionMap)mapOrAction;
|
||
|
foreach (var action in actionMap.actions)
|
||
|
{
|
||
|
foreach (var control in action.controls)
|
||
|
{
|
||
|
var steamController = control.device as SteamController;
|
||
|
if (steamController == null)
|
||
|
continue;
|
||
|
|
||
|
// Yes, there's active bindings to a SteamController on the map. Look through the Steam action
|
||
|
// sets on the controller for a name match on the action map. If we have one, sync the enable/
|
||
|
// disable status of the set.
|
||
|
var actionMapName = actionMap.name;
|
||
|
foreach (var set in steamController.steamActionSets)
|
||
|
{
|
||
|
if (string.Compare(set.name, actionMapName, StringComparison.InvariantCultureIgnoreCase) != 0)
|
||
|
continue;
|
||
|
|
||
|
// Nothing to do if the Steam controller has auto-syncing disabled.
|
||
|
if (!steamController.autoActivateSets)
|
||
|
return;
|
||
|
|
||
|
// Sync status.
|
||
|
steamController.ActivateSteamActionSet(set.handle);
|
||
|
|
||
|
// Done.
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static void OnUpdate()
|
||
|
{
|
||
|
if (api == null)
|
||
|
return;
|
||
|
|
||
|
// Update controller state.
|
||
|
api.RunFrame();
|
||
|
|
||
|
// Check if we have any new controllers have appeared.
|
||
|
if (s_ConnectedControllers == null)
|
||
|
s_ConnectedControllers = new SteamHandle<SteamController>[STEAM_CONTROLLER_MAX_COUNT];
|
||
|
var numConnectedControllers = api.GetConnectedControllers(s_ConnectedControllers);
|
||
|
for (var i = 0; i < numConnectedControllers; ++i)
|
||
|
{
|
||
|
var handle = s_ConnectedControllers[i];
|
||
|
|
||
|
// See if we already have a device for this one.
|
||
|
if (s_InputDevices != null)
|
||
|
{
|
||
|
SteamController existingDevice = null;
|
||
|
for (var n = 0; n < s_InputDeviceCount; ++n)
|
||
|
{
|
||
|
if (s_InputDevices[n].steamControllerHandle == handle)
|
||
|
{
|
||
|
existingDevice = s_InputDevices[n];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Yes, we do.
|
||
|
if (existingDevice != null)
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
////FIXME: this should not create garbage
|
||
|
// No, so create a new device.
|
||
|
var controllerLayouts = InputSystem.ListLayoutsBasedOn("SteamController");
|
||
|
foreach (var layout in controllerLayouts)
|
||
|
{
|
||
|
// Rather than directly creating a device with the layout, let it go through
|
||
|
// the usual matching process.
|
||
|
var device = InputSystem.AddDevice(new InputDeviceDescription
|
||
|
{
|
||
|
interfaceName = SteamController.kSteamInterface,
|
||
|
product = layout
|
||
|
});
|
||
|
|
||
|
// Make sure it's a SteamController we got.
|
||
|
var steamDevice = device as SteamController;
|
||
|
if (steamDevice == null)
|
||
|
{
|
||
|
Debug.LogError(string.Format(
|
||
|
"InputDevice created from layout '{0}' based on the 'SteamController' layout is not a SteamController",
|
||
|
device.layout));
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Resolve the controller's actions.
|
||
|
steamDevice.InvokeResolveSteamActions();
|
||
|
|
||
|
// Assign it the Steam controller handle.
|
||
|
steamDevice.steamControllerHandle = handle;
|
||
|
|
||
|
ArrayHelpers.AppendWithCapacity(ref s_InputDevices, ref s_InputDeviceCount, steamDevice);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Update all controllers we have.
|
||
|
for (var i = 0; i < s_InputDeviceCount; ++i)
|
||
|
{
|
||
|
var device = s_InputDevices[i];
|
||
|
var handle = device.steamControllerHandle;
|
||
|
|
||
|
// Check if the device still exists.
|
||
|
var stillExists = false;
|
||
|
for (var n = 0; n < numConnectedControllers; ++n)
|
||
|
if (s_ConnectedControllers[n] == handle)
|
||
|
{
|
||
|
stillExists = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// If not, remove it.
|
||
|
if (!stillExists)
|
||
|
{
|
||
|
ArrayHelpers.EraseAtByMovingTail(s_InputDevices, ref s_InputDeviceCount, i);
|
||
|
////REVIEW: should this rather queue a device removal event?
|
||
|
InputSystem.RemoveDevice(device);
|
||
|
--i;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
////TODO: support polling Steam controllers on an async polling thread adhering to InputSystem.pollingFrequency
|
||
|
// Otherwise, update it.
|
||
|
device.InvokeUpdate();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif // (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT
|