diff --git a/Assets/Tests/InputSystem/CoreTests_Devices.cs b/Assets/Tests/InputSystem/CoreTests_Devices.cs index ed90efe605..08301680ca 100644 --- a/Assets/Tests/InputSystem/CoreTests_Devices.cs +++ b/Assets/Tests/InputSystem/CoreTests_Devices.cs @@ -25,6 +25,7 @@ using Is = NUnit.Framework.Is; using Quaternion = UnityEngine.Quaternion; using TouchPhase = UnityEngine.InputSystem.TouchPhase; +using DeviceOrientation = UnityEngine.InputSystem.DeviceOrientation; using Vector2 = UnityEngine.Vector2; using Vector3 = UnityEngine.Vector3; @@ -2649,6 +2650,7 @@ public void Devices_DeltaControlsResetBetweenUpdates(string layoutName, string c [TestCase("Joystick", typeof(Joystick))] [TestCase("Accelerometer", typeof(Accelerometer))] [TestCase("Gyroscope", typeof(Gyroscope))] + [TestCase("DeviceOrientationSensor", typeof(DeviceOrientationSensor))] public void Devices_CanCreateDevice(string layout, System.Type type) { var device = InputSystem.AddDevice(layout); @@ -3870,6 +3872,18 @@ public void Devices_CanGetLinearAccelerationReading() Assert.That(LinearAccelerationSensor.current, Is.SameAs(sensor)); } + [Test] + [Category("Devices")] + public void Devices_CanGetDeviceOrientationReading() + { + var sensor = InputSystem.AddDevice(); + InputSystem.QueueStateEvent(sensor, new DeviceOrientationState { orientation = (int)DeviceOrientation.LandscapeLeft }); + InputSystem.Update(); + + Assert.That(sensor.orientation.ReadValue(), Is.EqualTo(DeviceOrientation.LandscapeLeft)); + Assert.That(DeviceOrientationSensor.current, Is.SameAs(sensor)); + } + [Test] [Category("Devices")] [TestCase("Accelerometer", "acceleration")] diff --git a/Assets/Tests/InputSystem/Plugins/UnityRemoteTests.cs b/Assets/Tests/InputSystem/Plugins/UnityRemoteTests.cs index fc2c8d1001..e2eceed858 100644 --- a/Assets/Tests/InputSystem/Plugins/UnityRemoteTests.cs +++ b/Assets/Tests/InputSystem/Plugins/UnityRemoteTests.cs @@ -7,6 +7,7 @@ using UnityEngine.InputSystem; using UnityEngine.TestTools.Utils; using Gyroscope = UnityEngine.InputSystem.Gyroscope; +using DeviceOrientation = UnityEngine.InputSystem.DeviceOrientation; internal class UnityRemoteTests : CoreTestsFixture { @@ -269,6 +270,42 @@ public void Remote_CanReceiveAccelerometerInputFromUnityRemote() Assert.That(Accelerometer.current, Is.Null); } + [Test] + [Category("Remote")] + public void Remote_CanReceiveDeviceOrientationFromUnityRemote() + { + SendUnityRemoteMessage(UnityRemoteSupport.HelloMessage.Create()); + + // Like the accelerometer, the orientation sensor is assumed present on every device running the + // Unity Remote and does not require explicit enabling. + Assert.That(DeviceOrientationSensor.current, Is.Not.Null); + Assert.That(DeviceOrientationSensor.current.remote, Is.True); + Assert.That(DeviceOrientationSensor.current.enabled, Is.True); + + SendUnityRemoteMessage(new UnityRemoteSupport.DeviceOrientationMessage + { + orientation = (int)DeviceOrientation.LandscapeLeft + }); + InputSystem.Update(); + + Assert.That(DeviceOrientationSensor.current.orientation.ReadValue(), Is.EqualTo(DeviceOrientation.LandscapeLeft)); + + // Disabling it should stop updates. + InputSystem.DisableDevice(DeviceOrientationSensor.current); + + SendUnityRemoteMessage(new UnityRemoteSupport.DeviceOrientationMessage + { + orientation = (int)DeviceOrientation.FaceUp + }); + InputSystem.Update(); + + Assert.That(DeviceOrientationSensor.current.orientation.ReadValue(), Is.EqualTo(DeviceOrientation.LandscapeLeft)); + + SendUnityRemoteMessage(new UnityRemoteSupport.GoodbyeMessage()); + + Assert.That(DeviceOrientationSensor.current, Is.Null); + } + // We don't currently support joystick input coming from the Unity Remote. [Test] [Category("Remote")] diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md index 5e13c71fe8..a4a731ac15 100644 --- a/Packages/com.unity.inputsystem/CHANGELOG.md +++ b/Packages/com.unity.inputsystem/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] - yyyy-mm-dd +### Added + +- Added `DeviceOrientationSensor`, a sensor that reports the physical orientation of the device as a discrete value (portrait, landscape, face up or face down), providing parity with the legacy `UnityEngine.Input.deviceOrientation` property. Read it via `DeviceOrientationSensor.current.orientation.ReadValue()`. + ### Fixed - Fixed an `OverflowException` when creating a control scheme (or other named item) whose all-numeric name exceeds `Int32.MaxValue`, which previously discarded the entered name and fell back to the default [UUM-145766](https://issuetracker.unity3d.com/product/unity/issues/guid/UUM-145766) diff --git a/Packages/com.unity.inputsystem/Documentation~/corresponding-old-new-api.md b/Packages/com.unity.inputsystem/Documentation~/corresponding-old-new-api.md index dcad91390f..a122e8f4e6 100644 --- a/Packages/com.unity.inputsystem/Documentation~/corresponding-old-new-api.md +++ b/Packages/com.unity.inputsystem/Documentation~/corresponding-old-new-api.md @@ -106,7 +106,7 @@ Note: [`UnityEngine.TouchScreenKeyboard`](https://docs.unity3d.com/ScriptReferen [`Input.accelerationEventCount`](https://docs.unity3d.com/ScriptReference/Input-accelerationEventCount.html)
[`Input.accelerationEvents`](https://docs.unity3d.com/ScriptReference/Input-accelerationEvents.html)|Acceleration events aren't made available separately from other input events. See the [accelerometer code sample on the Sensors page](query-sensors.md#measure-a-devices-acceleration). [`Input.compass`](https://docs.unity3d.com/ScriptReference/Input-compass.html)|No corresponding API yet. [`Input.compensateSensors`](https://docs.unity3d.com/ScriptReference/Input-compensateSensors.html)|[`InputSettings.compensateForScreenOrientation`](xref:UnityEngine.InputSystem.InputSettings). -[`Input.deviceOrientation`](https://docs.unity3d.com/ScriptReference/Input-deviceOrientation.html)|No corresponding API yet. +[`Input.deviceOrientation`](https://docs.unity3d.com/ScriptReference/Input-deviceOrientation.html)|[`DeviceOrientationSensor.current.orientation.ReadValue()`](xref:UnityEngine.InputSystem.DeviceOrientationSensor). [`Input.gyro`](https://docs.unity3d.com/ScriptReference/Input-gyro.html)|The `UnityEngine.Gyroscope` class is replaced by multiple separate sensor Devices in the new Input System:
[`Gyroscope`](xref:UnityEngine.InputSystem.Gyroscope) to measure angular velocity.
[`GravitySensor`](xref:UnityEngine.InputSystem.GravitySensor) to measure the direction of gravity.
[`AttitudeSensor`](xref:UnityEngine.InputSystem.AttitudeSensor) to measure the orientation of the device.
[`Accelerometer`](xref:UnityEngine.InputSystem.Accelerometer) to measure the total acceleration applied to the device.
[`LinearAccelerationSensor`](xref:UnityEngine.InputSystem.LinearAccelerationSensor) to measure acceleration applied to the device, compensating for gravity. [`Input.gyro.attitude`](https://docs.unity3d.com/ScriptReference/Gyroscope-attitude.html)|[`AttitudeSensor.current.orientation.ReadValue()`](xref:UnityEngine.InputSystem.AttitudeSensor). [`Input.gyro.enabled`](https://docs.unity3d.com/ScriptReference/Gyroscope-enabled.html)|Get: `Gyroscope.current.enabled`
Set:
`EnableDevice(Gyroscope.current);`
`DisableDevice(Gyroscope.current);`

**Note:** The new Input System replaces `UnityEngine.Gyroscope` with multiple separate sensor devices. Substitute [`Gyroscope`](xref:UnityEngine.InputSystem.Gyroscope) with other sensors in the sample as needed. See the notes for `Input.gyro` above for details. diff --git a/Packages/com.unity.inputsystem/Documentation~/supported-sensors-reference.md b/Packages/com.unity.inputsystem/Documentation~/supported-sensors-reference.md index 8b33dee155..696a041067 100644 --- a/Packages/com.unity.inputsystem/Documentation~/supported-sensors-reference.md +++ b/Packages/com.unity.inputsystem/Documentation~/supported-sensors-reference.md @@ -12,6 +12,7 @@ Each sensor device implements a single control which represents the data read by | [`Gyroscope`](xref:UnityEngine.InputSystem.Gyroscope) | Measures the angular velocity of a device. | Yes | Yes | Yes | | [`GravitySensor`](xref:UnityEngine.InputSystem.GravitySensor) | Determines the direction of the gravity vector relative to the device. | Yes | Yes | Yes | | [`AttitudeSensor`](xref:UnityEngine.InputSystem.AttitudeSensor) | Determine the orientation of a device. | Yes | Yes | Yes | +| [`DeviceOrientationSensor`](xref:UnityEngine.InputSystem.DeviceOrientationSensor) | Reports the physical orientation of the device as a discrete value (for example portrait, landscape, face up or face down). | Yes | Yes | Yes | | [`LinearAccelerationSensor`](xref:UnityEngine.InputSystem.LinearAccelerationSensor) | Measures the acceleration of a device unaffected by gravity. | Yes | Yes | Yes | | [`MagneticFieldSensor`](xref:UnityEngine.InputSystem.MagneticFieldSensor) | Represents the magnetic field that affects the device. | Yes | No | No | | [`LightSensor`](xref:UnityEngine.InputSystem.LightSensor) | Represents the ambient light measured by the device. | Yes | No | No | diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/Plugins/UnityRemote/UnityRemoteSupport.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/Plugins/UnityRemote/UnityRemoteSupport.cs index 4dd8cc234e..da927db86d 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/Plugins/UnityRemote/UnityRemoteSupport.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/Plugins/UnityRemote/UnityRemoteSupport.cs @@ -125,6 +125,8 @@ private static unsafe bool ProcessMessageFromUnityRemote(IntPtr messageData) s_State.touchscreen.m_DeviceFlags |= InputDevice.DeviceFlags.Remote; s_State.accelerometer = InputSystem.AddDevice(); s_State.accelerometer.m_DeviceFlags |= InputDevice.DeviceFlags.Remote; + s_State.orientation = InputSystem.AddDevice(); + s_State.orientation.m_DeviceFlags |= InputDevice.DeviceFlags.Remote; // Gryo etc. added only when we receive GyroSettingsMessage. s_State.connected = true; @@ -248,6 +250,17 @@ private static unsafe bool ProcessMessageFromUnityRemote(IntPtr messageData) accelerometerMessage->accelerationZ) }); break; + + case (byte)MessageType.DeviceOrientation: + if (s_State.orientation == null) + break; + var orientationMessage = (DeviceOrientationMessage*)messageData; + // The remote sends the DeviceOrientation enum value directly (same values as ours). + InputSystem.QueueStateEvent(s_State.orientation, new DeviceOrientationState + { + orientation = orientationMessage->orientation + }); + break; } return false; @@ -257,6 +270,8 @@ private static void Disconnect() { InputSystem.RemoveDevice(s_State.touchscreen); InputSystem.RemoveDevice(s_State.accelerometer); + if (s_State.orientation != null) + InputSystem.RemoveDevice(s_State.orientation); if (s_State.gyroscope != null) InputSystem.RemoveDevice(s_State.gyroscope); if (s_State.attitude != null) @@ -287,6 +302,8 @@ private static void OnDeviceChange(InputDevice device, InputDeviceChange change) s_State.touchscreen = null; else if (device == s_State.linearAcceleration) s_State.linearAcceleration = null; + else if (device == s_State.orientation) + s_State.orientation = null; break; case InputDeviceChange.Enabled: @@ -534,6 +551,17 @@ internal struct AccelerometerInputMessage : IUnityRemoteMessage public byte staticType => (byte)MessageType.AccelerometerInput; } + // See HandleOrientationMessage() in Editor/Src/RemoteInput/GenericRemote.cpp: a single int32 holding + // the DeviceOrientation enum value. + [StructLayout(LayoutKind.Explicit)] + internal struct DeviceOrientationMessage : IUnityRemoteMessage + { + [FieldOffset(0)] public MessageHeader header; + [FieldOffset(5)] public int orientation; + + public byte staticType => (byte)MessageType.DeviceOrientation; + } + private struct State { public bool connected; @@ -548,6 +576,7 @@ private struct State // Devices that we create for receiving input from the remote. public Touchscreen touchscreen; public Accelerometer accelerometer; + public DeviceOrientationSensor orientation; public Gyroscope gyroscope; public AttitudeSensor attitude; public GravitySensor gravity; diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Controls/DeviceOrientationControl.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Controls/DeviceOrientationControl.cs new file mode 100644 index 0000000000..0c72ca11d5 --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Controls/DeviceOrientationControl.cs @@ -0,0 +1,44 @@ +using UnityEngine.InputSystem.Layouts; +using UnityEngine.InputSystem.LowLevel; + +namespace UnityEngine.InputSystem.Controls +{ + /// + /// A control reading a value. + /// + /// + /// This is used by to report the physical orientation of the device + /// (see ). It provides feature parity with the legacy + /// UnityEngine.Input.deviceOrientation property. + /// + /// + [InputControlLayout(hideInUI = true)] + public class DeviceOrientationControl : InputControl + { + /// + /// Default-initialize the control. + /// + /// + /// Format of the control is + /// by default. + /// + public DeviceOrientationControl() + { + m_StateBlock.format = InputStateBlock.FormatInt; + } + + /// + public override unsafe DeviceOrientation ReadUnprocessedValueFromState(void* statePtr) + { + var intValue = stateBlock.ReadInt(statePtr); + return (DeviceOrientation)intValue; + } + + /// + public override unsafe void WriteValueIntoState(DeviceOrientation value, void* statePtr) + { + var valuePtr = (byte*)statePtr + (int)m_StateBlock.byteOffset; + *(int*)valuePtr = (int)value; + } + } +} diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Controls/DeviceOrientationControl.cs.meta b/Packages/com.unity.inputsystem/InputSystem/Runtime/Controls/DeviceOrientationControl.cs.meta new file mode 100644 index 0000000000..d89d235298 --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Controls/DeviceOrientationControl.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 92dc56acb38a47d3994fdd8162746807 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Sensor.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Sensor.cs index c4cd9c952d..2184199778 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Sensor.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Devices/Sensor.cs @@ -60,6 +60,18 @@ internal struct LinearAccelerationState : IInputStateTypeInfo public FourCC format => kFormat; } + + internal struct DeviceOrientationState : IInputStateTypeInfo + { + public static FourCC kFormat => new FourCC('O', 'R', 'N', 'T'); + + // Note: unlike the other sensors this value is *not* compensated for screen orientation. It reports + // the physical orientation of the device and thus must be independent of how the content is rendered. + [InputControl(name = "orientation", displayName = "Orientation", layout = "DeviceOrientation")] + public int orientation; + + public FourCC format => kFormat; + } } namespace UnityEngine.InputSystem @@ -694,4 +706,109 @@ protected override void FinishSetup() base.FinishSetup(); } } + + /// + /// Enum describing the physical orientation of a device as reported by . + /// + /// + /// The values mirror the legacy UnityEngine.DeviceOrientation enum so that content migrating from + /// UnityEngine.Input.deviceOrientation to the Input System observes identical semantics. Note that this + /// is a package-local enum, kept independent of the legacy input module. + /// + /// + public enum DeviceOrientation + { + /// The orientation of the device cannot be determined. + Unknown = 0, + + /// The device is in portrait mode, with the device held upright and the home button at the bottom. + Portrait = 1, + + /// The device is in portrait mode but upside down, with the device held upright and the home button at the top. + PortraitUpsideDown = 2, + + /// The device is in landscape mode, with the device held upright and the home button on the right side. + LandscapeLeft = 3, + + /// The device is in landscape mode, with the device held upright and the home button on the left side. + LandscapeRight = 4, + + /// The device is held parallel to the ground with the screen facing upwards. + FaceUp = 5, + + /// The device is held parallel to the ground with the screen facing downwards. + FaceDown = 6, + } + + /// + /// Input device representing the physical orientation of the device playing the content. + /// + /// + /// The orientation sensor reports the physical orientation of the device (for example, whether it is held in + /// portrait or landscape, or lying face up or face down) as a discrete value. + /// It provides feature parity with the legacy UnityEngine.Input.deviceOrientation property. + /// + /// Unlike the other motion sensors, the reported value is not compensated for screen orientation; it always + /// describes the physical orientation of the hardware. + /// + /// + /// + /// class MyBehavior : MonoBehaviour + /// { + /// protected void OnEnable() + /// { + /// InputSystem.EnableDevice(DeviceOrientationSensor.current); + /// } + /// + /// protected void OnDisable() + /// { + /// InputSystem.DisableDevice(DeviceOrientationSensor.current); + /// } + /// + /// protected void Update() + /// { + /// var orientation = DeviceOrientationSensor.current.orientation.ReadValue(); + /// //... + /// } + /// } + /// + /// + /// + [InputControlLayout(stateType = typeof(DeviceOrientationState), displayName = "DeviceOrientation")] + public class DeviceOrientationSensor : Sensor + { + /// + /// The physical orientation of the device. + /// + /// Control reporting the current . + public DeviceOrientationControl orientation { get; protected set; } + + /// + /// The orientation sensor that was last added or had activity last. + /// + /// Current orientation sensor or null. + public static DeviceOrientationSensor current { get; private set; } + + /// + public override void MakeCurrent() + { + base.MakeCurrent(); + current = this; + } + + /// + protected override void OnRemoved() + { + base.OnRemoved(); + if (current == this) + current = null; + } + + /// + protected override void FinishSetup() + { + orientation = GetChildControl("orientation"); + base.FinishSetup(); + } + } } diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/InputManager.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/InputManager.cs index 994538ecc2..859535fa04 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/InputManager.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/InputManager.cs @@ -2053,6 +2053,7 @@ internal void InitializeData() RegisterControlLayout("Touch", typeof(TouchControl)); RegisterControlLayout("TouchPhase", typeof(TouchPhaseControl)); RegisterControlLayout("TouchPress", typeof(TouchPressControl)); + RegisterControlLayout("DeviceOrientation", typeof(DeviceOrientationControl)); RegisterControlLayout("Gamepad", typeof(Gamepad)); // Devices. RegisterControlLayout("Joystick", typeof(Joystick)); @@ -2073,6 +2074,7 @@ internal void InitializeData() RegisterControlLayout("HumiditySensor", typeof(HumiditySensor)); RegisterControlLayout("AmbientTemperatureSensor", typeof(AmbientTemperatureSensor)); RegisterControlLayout("StepCounter", typeof(StepCounter)); + RegisterControlLayout("DeviceOrientationSensor", typeof(DeviceOrientationSensor)); RegisterControlLayout("TrackedDevice", typeof(TrackedDevice)); // Precompiled layouts. diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Plugins/Android/AndroidSupport.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Plugins/Android/AndroidSupport.cs index 1a7619c501..54b6557096 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Plugins/Android/AndroidSupport.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Plugins/Android/AndroidSupport.cs @@ -111,6 +111,13 @@ public static void Initialize() .WithDeviceClass("AndroidSensor") .WithCapability("sensorType", AndroidSensorType.HingeAngle)); + // Device orientation is not an Android hardware sensor; it is reported as its own device + // class and maps directly to the base DeviceOrientationSensor layout. + InputSystem.RegisterLayoutMatcher("DeviceOrientationSensor", + new InputDeviceMatcher() + .WithInterface(kAndroidInterface) + .WithDeviceClass("DeviceOrientation")); + InputSystem.onFindLayoutForDevice += OnFindLayoutForDevice; } diff --git a/Packages/com.unity.inputsystem/InputSystem/Runtime/Plugins/iOS/iOSSupport.cs b/Packages/com.unity.inputsystem/InputSystem/Runtime/Plugins/iOS/iOSSupport.cs index 20353ebe9c..e3fa416a49 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Runtime/Plugins/iOS/iOSSupport.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Runtime/Plugins/iOS/iOSSupport.cs @@ -55,6 +55,10 @@ public static void Initialize() new InputDeviceMatcher() .WithInterface("iOS") .WithDeviceClass("LinearAcceleration")); + InputSystem.RegisterLayoutMatcher("DeviceOrientationSensor", + new InputDeviceMatcher() + .WithInterface("iOS") + .WithDeviceClass("DeviceOrientation")); #if UNITY_EDITOR || UNITY_IOS InputSystem.RegisterLayout(); // Don't add devices for InputTestRuntime