127 lines
5.2 KiB
Markdown
127 lines
5.2 KiB
Markdown
---
|
|
uid: input-system-step-4-device-registration
|
|
---
|
|
|
|
# Step 4 Device registration and creation
|
|
|
|
You now have a functioning device, but you haven't registered it (added it to the system) yet. This means you can't see the device when, for example, you create bindings in the [Action editor](actions-editor.md).
|
|
|
|
You can register your device type with the system from within the code that runs automatically as part of Unity's startup. To do so, modify the definition of `MyDevice` like so:
|
|
|
|
```CSharp
|
|
// Add the InitializeOnLoad attribute to automatically run the static
|
|
// constructor of the class after each C# domain load.
|
|
#if UNITY_EDITOR
|
|
[InitializeOnLoad]
|
|
#endif
|
|
public class MyDevice : InputDevice, IInputUpdateCallbackReceiver
|
|
{
|
|
//...
|
|
|
|
static MyDevice()
|
|
{
|
|
// RegisterLayout() adds a "Control layout" to the system.
|
|
// These can be layouts for individual Controls (like sticks)
|
|
// or layouts for entire Devices (which are themselves
|
|
// Controls) like in our case.
|
|
InputSystem.RegisterLayout<MyDevice>();
|
|
}
|
|
|
|
// You still need a way to trigger execution of the static constructor
|
|
// in the Player. To do this, you can add the RuntimeInitializeOnLoadMethod
|
|
// to an empty method.
|
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
|
private static void InitializeInPlayer() {}
|
|
}
|
|
```
|
|
|
|
This registers the Device type with the system and makes it available in the Control picker. However, you still need a way to add an instance of the Device when it is connected.
|
|
|
|
In theory, you could call [`InputSystem.AddDevice<MyDevice>()`](xref:UnityEngine.InputSystem.InputSystem) somewhere, but in a real-world setup you likely have to correlate the Input Devices you create with their identities in the third-party API.
|
|
|
|
It might be tempting to do something like this:
|
|
|
|
```CSharp
|
|
public class MyDevice : InputDevice, IInputUpdateCallbackReceiver
|
|
{
|
|
//...
|
|
|
|
// This does NOT work correctly.
|
|
public ThirdPartyAPI.DeviceId externalId { get; set; }
|
|
}
|
|
```
|
|
|
|
and then set that on the Device after calling [`AddDevice<MyDevice>`](xref:UnityEngine.InputSystem.InputSystem). However, this doesn't work as expected in the Editor, because the Input System requires Devices to be created solely from their [`InputDeviceDescription`](xref:UnityEngine.InputSystem.Layouts.InputDeviceDescription) in combination with the chosen layout (and layout variant). In addition, the system supports a fixed set of mutable per-device properties such as device usages (that is, [`InputSystem.SetDeviceUsage()`](xref:UnityEngine.InputSystem.InputSystem) and related methods). This allows the system to easily recreate Devices after domain reloads in the Editor, as well as to create replicas of remote Devices when connecting to a Player. To comply with this requirement, you must cast that information provided by the third-party API into an [`InputDeviceDescription`](xref:UnityEngine.InputSystem.Layouts.InputDeviceDescription) and then use an [`InputDeviceMatcher`](xref:UnityEngine.InputSystem.Layouts.InputDeviceMatcher) to match the description to our custom `MyDevice` layout.
|
|
|
|
This example assumes that the third-party API has two callbacks, like this:
|
|
|
|
```CSharp
|
|
public static ThirdPartyAPI
|
|
{
|
|
// This example assumes that the argument is a string that
|
|
// contains the name of the Device, and that no two Devices
|
|
// have the same name in the external API.
|
|
public static Action<string> deviceAdded;
|
|
public static Action<string> deviceRemoved;
|
|
}
|
|
```
|
|
|
|
You can hook into those callbacks and create and destroy devices in response.
|
|
|
|
```CSharp
|
|
// This example uses a MonoBehaviour with [ExecuteInEditMode]
|
|
// on it to run the setup code. You can do this many other ways.
|
|
[ExecuteInEditMode]
|
|
public class MyDeviceSupport : MonoBehaviour
|
|
{
|
|
protected void OnEnable()
|
|
{
|
|
ThirdPartyAPI.deviceAdded += OnDeviceAdded;
|
|
ThirdPartyAPI.deviceRemoved += OnDeviceRemoved;
|
|
}
|
|
|
|
protected void OnDisable()
|
|
{
|
|
ThirdPartyAPI.deviceAdded -= OnDeviceAdded;
|
|
ThirdPartyAPI.deviceRemoved -= OnDeviceRemoved;
|
|
}
|
|
|
|
private void OnDeviceAdded(string name)
|
|
{
|
|
// Feed a description of the Device into the system. In response, the
|
|
// system matches it to the layouts it has and creates a Device.
|
|
InputSystem.AddDevice(
|
|
new InputDeviceDescription
|
|
{
|
|
interfaceName = "ThirdPartyAPI",
|
|
product = name
|
|
});
|
|
}
|
|
|
|
private void OnDeviceRemoved(string name)
|
|
{
|
|
var device = InputSystem.devices.FirstOrDefault(
|
|
x => x.description == new InputDeviceDescription
|
|
{
|
|
interfaceName = "ThirdPartyAPI",
|
|
product = name,
|
|
});
|
|
|
|
if (device != null)
|
|
InputSystem.RemoveDevice(device);
|
|
}
|
|
|
|
// Move the registration of MyDevice from the
|
|
// static constructor to here, and change the
|
|
// registration to also supply a matcher.
|
|
protected void Awake()
|
|
{
|
|
// Add a match that catches any Input Device that reports its
|
|
// interface as "ThirdPartyAPI".
|
|
InputSystem.RegisterLayout<MyDevice>(
|
|
matches: new InputDeviceMatcher()
|
|
.WithInterface("ThirdPartyAPI"));
|
|
}
|
|
}
|
|
```
|