This commit is contained in:
2025-01-17 13:10:42 +01:00
commit 4536213c91
15115 changed files with 1442174 additions and 0 deletions

View File

@@ -0,0 +1,56 @@
#if (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT
namespace UnityEngine.InputSystem.Steam
{
/// <summary>
/// This is a wrapper around the Steamworks SDK controller API.
/// </summary>
/// <seealso href="https://partner.steamgames.com/doc/api/ISteamController"/>
public interface ISteamControllerAPI
{
void RunFrame();
int GetConnectedControllers(SteamHandle<SteamController>[] outHandles);
SteamHandle<InputActionMap> GetActionSetHandle(string actionSetName);
SteamHandle<InputAction> GetDigitalActionHandle(string actionName);
SteamHandle<InputAction> GetAnalogActionHandle(string actionName);
void ActivateActionSet(SteamHandle<SteamController> controllerHandle, SteamHandle<InputActionMap> actionSetHandle);
SteamHandle<InputActionMap> GetCurrentActionSet(SteamHandle<SteamController> controllerHandle);
void ActivateActionSetLayer(SteamHandle<SteamController> controllerHandle,
SteamHandle<InputActionMap> actionSetLayerHandle);
void DeactivateActionSetLayer(SteamHandle<SteamController> controllerHandle,
SteamHandle<InputActionMap> actionSetLayerHandle);
void DeactivateAllActionSetLayers(SteamHandle<SteamController> controllerHandle);
int GetActiveActionSetLayers(SteamHandle<SteamController> controllerHandle,
out SteamHandle<InputActionMap> handlesOut);
SteamAnalogActionData GetAnalogActionData(SteamHandle<SteamController> controllerHandle,
SteamHandle<InputAction> analogActionHandle);
SteamDigitalActionData GetDigitalActionData(SteamHandle<SteamController> controllerHandle,
SteamHandle<InputAction> digitalActionHandle);
}
public struct SteamDigitalActionData
{
public bool active { get; set; }
public bool pressed { get; set; }
}
public struct SteamAnalogActionData
{
public bool active { get; set; }
public Vector2 position { get; set; }
}
}
#endif // (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 425bde5d5c63e4c5d8d95b068a911236
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,137 @@
#if (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT
using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using UnityEngine.InputSystem.Utilities;
#if UNITY_EDITOR
using UnityEngine.InputSystem.Steam.Editor;
#endif
////TODO: support action set layers
namespace UnityEngine.InputSystem.Steam
{
/// <summary>
/// Base class for controllers made available through the Steam controller API.
/// </summary>
/// <remarks>
/// Unlike other controllers, the Steam controller is somewhat of an amorphous input
/// device which gains specific shape only in combination with an "In-Game Action File"
/// in VDF format installed alongside the application. These files specify the actions
/// that are supported by the application and are internally bound to specific controls
/// on a controller inside the Steam runtime. The bindings are set up in the Steam client
/// through its own binding UI.
///
/// Note that as the Steam controller API supports PS4 and Xbox controllers as well,
/// the actual hardware device behind a SteamController instance may not be a
/// Steam Controller. The <see cref="steamControllerType"/> property can be used what kind
/// of controller the Steam runtime is talking to internally.
///
/// This class is abstract. Specific Steam controller interfaces can either be implemented
/// manually based on this class or generated automatically from Steam IGA files using
/// <see cref="SteamIGAConverter"/>. This can be done in the editor by right-clicking
/// a .VDF file containing the actions and then selecting "Steam >> Generate Unity Input Device...".
/// The result is a newly generated device layout that will automatically register itself
/// with the input system and will represent a Steam controller with the specific action
/// sets and actions found in the .VDF file. The Steam handles for sets and actions will
/// automatically be exposed from the device and controls will be set up that correspond
/// to each action defined in the .VDF file.
///
/// Devices based on SteamController can be used in one of two ways.
///
/// The first method is by manually managing active action sets on a controller. This is done by
/// calling the various APIs (such as <see cref="ActivateSteamActionSet"/>) that correspond
/// to the methods in the <see cref="ISteamControllerAPI">Steam controller API</see>. The
/// controller handle is implicit in this case and corresponds to the <see cref="steamControllerHandle"/>
/// of the controller the methods are called on.
///
/// The second method is by using
///
///
///
///
///
/// By default, Steam controllers will automatically activate action set layers in
/// response to action maps being enabled and disabled. The correlation between Unity
/// actions and action maps and Steam actions and action sets happens entirely by name.
/// E.g. a Unity action map called "gameplay" will be looked up as a Steam action set
/// using the same name "gameplay".
/// </remarks>
public abstract class SteamController : InputDevice
{
internal const string kSteamInterface = "Steam";
/// <summary>
/// Handle in the <see cref="ISteamControllerAPI">Steam API</see> for the controller.
/// </summary>
public SteamHandle<SteamController> steamControllerHandle { get; internal set; }
/*
* TODO
public SteamControllerType steamControllerType
{
get { throw new NotImplementedException(); }
}*/
/// <summary>
/// The list of Steam action sets supported by this controller.
/// </summary>
/// <remarks>
/// Steam action sets are implicitly supplied to the Steam runtime rather than explicitly configured
/// by the application. ...
/// </remarks>
public abstract ReadOnlyArray<SteamActionSetInfo> steamActionSets { get; }
/// <summary>
/// Determine whether the controller automatically activates and deactivates action set
/// layers in response to <see cref="InputActionMap">input action map</see> being enabled
/// and disabled.
/// </summary>
/// <remarks>
/// This is on by default.
///
/// When on, if an <see cref="InputActionMap">action map</see> has bindings to a SteamController
/// and is enabled or disabled, the SteamController will automatically enable or disable
/// the correspondingly named Steam action set.
/// </remarks>
public bool autoActivateSets { get; set; }
protected SteamController()
{
autoActivateSets = true;
}
public void ActivateSteamActionSet(SteamHandle<InputActionMap> actionSet)
{
SteamSupport.GetAPIAndRequireItToBeSet().ActivateActionSet(steamControllerHandle, actionSet);
}
public SteamHandle<InputActionMap> currentSteamActionSet
{
get { return SteamSupport.GetAPIAndRequireItToBeSet().GetCurrentActionSet(steamControllerHandle); }
}
protected abstract void ResolveSteamActions(ISteamControllerAPI api);
protected abstract void Update(ISteamControllerAPI api);
// These methods avoid having an 'internal' modifier that every override needs to carry along.
internal void InvokeResolveSteamActions()
{
ResolveSteamActions(SteamSupport.GetAPIAndRequireItToBeSet());
}
internal void InvokeUpdate()
{
Update(SteamSupport.GetAPIAndRequireItToBeSet());
}
public struct SteamActionSetInfo
{
public string name { get; set; }
public SteamHandle<InputActionMap> handle { get; set; }
}
}
}
#endif // (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ab9baa68d9998419ba6b1bd76b11b630
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,15 @@
namespace UnityEngine.InputSystem.Steam
{
/*
TODO
public enum SteamControllerType
{
Unknown,
SteamController,
Xbox360,
XboxOne,
GenericXInput,
PS4
}
*/
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7ffe30fd9a2d64031a59c21418fb2067
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,59 @@
#if (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT
using System;
namespace UnityEngine.InputSystem.Steam
{
/// <summary>
/// A handle for a Steam controller API object typed <typeparamref name="TObject"/>.
/// </summary>
/// <typeparam name="TObject">A type used to type the Steam handle. The type itself isn't used other than
/// for providing type safety to the Steam handle.</typeparam>
public struct SteamHandle<TObject> : IEquatable<SteamHandle<TObject>>
{
private ulong m_Handle;
public SteamHandle(ulong handle)
{
m_Handle = handle;
}
public override string ToString()
{
return string.Format("Steam({0}): {1}", typeof(TObject).Name, m_Handle);
}
public bool Equals(SteamHandle<TObject> other)
{
return m_Handle == other.m_Handle;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
return false;
return obj is SteamHandle<TObject> && Equals((SteamHandle<TObject>)obj);
}
public override int GetHashCode()
{
return m_Handle.GetHashCode();
}
public static bool operator==(SteamHandle<TObject> a, SteamHandle<TObject> b)
{
return a.m_Handle == b.m_Handle;
}
public static bool operator!=(SteamHandle<TObject> a, SteamHandle<TObject> b)
{
return !(a == b);
}
public static explicit operator ulong(SteamHandle<TObject> handle)
{
return handle.m_Handle;
}
}
}
#endif // (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2f971d46c202b4b7a90865bf5600683f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bb4d45fbf0aa84484b251242fdb21800
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,219 @@
#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

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7bef150b341c4d0dbab3db81ccf2ea81
timeCreated: 1511055624