Introduction

The Unity Input System package (2019+) provides a new way to map input actions from a wide range of devices to any sort of application interaction. Communication between the controls and the input map is made up of a sender and a receiver. The path to the receiver must coincide with a device or action path, as defined in the project InputActions.


The GameDriver XR Simulated Input plugin allows tests to be written for projects that utilize the Input System package. Tests can be executed as if a physical device were present, allowing for automated test execution independent of the physical device. Simulated devices are created by the GameDriver agent, which processes input commands passed into the project using the GameDriver API client.


Build Prerequisites

Before getting started with the GameDriver XR Simulated Input plugin, it is essential to have the following present in your Unity project.

  • Unity Input System package v1.3.0 or later

  • Add the GameDriver Agent to an empty object in the first loaded scene in your project. When in Play mode, you should see an additional component added to this object automatically. That is the XRSimulatedInput plugin.

  • The build flag GDIO_UNITY_NEW_INPUT_IL2CPP needs to be present for standalone IL2CPP builds. This can be found in the Project Settings > Player > Script Compilation section below:

  • For standalone builds using the Input System Package with GameDriver, we recommend setting the “Development Build” flag. In some isolated cases, IL2CPP builds have experienced issues when not using this build setting.

Quick Start

To work with simulated XR devices using GameDriver, follow these simple steps. Note: GDIO is automatically added to simulated devices, and needs to be included in Input commands.

  1. Add device(s) by first creating your simulated devices. Device names can be anything for the device you want to simulate. For example:


  1. Create a simulated HMD: 

api.CreateInputDevice("Unity.XR.Oculus.Input.OculusHMD", "OculusHMD", new string[] { "GDIOHMD" });


  1. Create simulated controller(s):

api.CreateInputDevice("Unity.XR.Oculus.Input.OculusTouchController", "OculusLeftHand", new string[] { "LeftHand" });
api.CreateInputDevice("Unity.XR.Oculus.Input.OculusTouchController", "OculusRightHand", new string[] { "RightHand" });


  1. To ensure that the controllers are tracked actively, you will need to use the following commands:


api.IntegerInputEvent("GDIOOculusLeftHand/trackingState", 63, 1);
api.IntegerInputEvent("GDIOOculusLeftHand/trackingState", 63, 1);

 

  1. Utilize these devices by calling input commands passing in the desired action path. Examples below.

    1. To update the HMD position, use the ‘centerEyePosition’ as shown below:

api.Vector3InputEvent("GDIOOculusHMD/centerEyePosition", new Vector3(0, 0, -5, 100);


  1. To update the controller position.            

api.Vector3InputEvent("GDIOOculusLeftHand/devicePosition", new Vector3(0, 0, -5), 100);


  1. To update the controller rotation. Note that once done updating the rotation, add another line to reset it to (0, 0, 0) to avoid unwanted offsets later on.

api.QuaternionInputEvent("GDIOOculusLeftHand/deviceRotation", api.EulerToQuat(-10f, 0f, 0f), 100);


  1. To press the buttons,  /gripPressed can be replaced with the path of the other buttons as required.

api.ButtonPress("GDIOOculusLeftHand/gripPressed", 100, 1f);


  1. To move the joystick on the x or y-axis:

api.Vector2InputEvent("GDIOOculusLeftHand/Primary2DAxis", new Vector2(0f, -1f), 100);



Understanding Input System Device Layouts

Device paths in the map are written as <Device>/inputControlName and are case-sensitive. For example, the following are unique paths:


/deviceName/inputControlName

/DeviceName/InputControlName


Paths can have symbols in the map:

  • Name

  • <LayoutName>

  • {usageOrTagName}

  • #(displayName)


For example, if the HMD position is mapped to be received as:

    <XRHMD>/centerEyePosition


The code to send it can be set for a specific device, using the name:

    /GDIOHMD/centerEyePosition


Or all devices of the layout type using:

    <XRHMD>/centerEyePosition


<XRHMD> means that the device in question was created having an XRHMD as a base layout. The map will look for any device of this type, and check the inputControl of that name. If the map is requesting something like:

    <XRController>{LeftHand}/devicePosition


This means that it's looking for any device of type XRController, but only those tagged as LeftHand. In this case, upon device creation, the tag must be passed to it, or the map won't identify it when receiving the command.



Simulated Device Creation

The GameDriver Agent allows you to add simulated devices for testing interactions and inputs without the need to plug in a physical device during automated testing.


Device creation is done as follows, and the GDIO tag is added by default:

api.CreateInputDevice("LayoutName", "CustomName", new string[] { "tags" });

For the HMD, the following will work since it doesn't require tags:

api.CreateInputDevice("OculusHMD", "OculusHMD");


However, for the controllers it is important that the tag matches what the map requested.

api.CreateInputDevice("OculusTouchController", "OculusLeftHand", new string[] { "LeftHand" });
api.CreateInputDevice("OculusTouchController", "OculusRightHand", new string[] { "RightHand" });



Input paths can be obtained using the GameDriver command:


    api.MapInputControlPathsUsed(int)


This accepts a range clamped to 0-2:


0 = string to output to debug

1 = Json file at GDIO folder

2 = both 0 and 1


Some paths will have more than 1 path, such as the LeftHand position, meaning any of them will be used. Some are for specific devices. Meta Quest, for example, doesn't have pointerPosition.


 


Looking at the paths, it's easier to see how to send events using simulated inputs.


    api.Vector3InputEvent("<XRHMD>/centerEyePosition", val, frame);


or more specifically


    api.Vector3InputEvent("GDIOHMD/centerEyePosition", val, frame);


If GDIOHMD is the device name. For the hands, it could be for all controllers:


    ("<XRController>/devicePosition", val, frame)


or for only controllers with the tag LeftHand


    ("<XRController>{LeftHand}/devicePosition", val, frame)


or  ("GDIOLeftHand/devicePosition", val, frame)


If GDIOLeftHand is the device name. The {} part indicates the tag must be added to the device.


Code example: Note that the trackingState is used to track the hands. In this example, the value must be set to 63. This value may be different depending on the implementation.




Supported Commands


The GameDriver API supports the following commands for use with the Unity Input System:

  • AxisPress
  • ButtonPress
  • IntegerInputEvent
  • Vector3InputEvent
  • Vector2InputEvent
  • QuaternionInputEvent


Details on the use of these commands can be found in the API reference.