FIX: Touch simulation leading to exceptions and assertions in UI module (case 1190150, #1258).

This commit is contained in:
Rene Damm 2021-01-27 00:08:51 +01:00 коммит произвёл GitHub
Родитель e10a07b9a4
Коммит b398919fa2
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
37 изменённых файлов: 1145 добавлений и 556 удалений

Просмотреть файл

@ -710,6 +710,7 @@ class APIVerificationTests
public UnityEngine.InputSystem.InputTestFixture.ActionConstraint Started(UnityEngine.InputSystem.InputAction action, UnityEngine.InputSystem.InputControl control = default(UnityEngine.InputSystem.InputControl), System.Nullable<double> time = default(System.Nullable<double>));
public static UnityEngine.InputSystem.InputActionSetupExtensions.BindingSyntax AddBinding(UnityEngine.InputSystem.InputActionMap actionMap, string path, string interactions = default(string), string groups = default(string), string action = default(string));
public UnityEngine.InputSystem.InputActionSetupExtensions.CompositeSyntax With(string name, string binding, string groups = default(string));
public static void DisableDevice(UnityEngine.InputSystem.InputDevice device);
")]
public void API_MinorVersionsHaveNoBreakingChanges()
{

Просмотреть файл

@ -1,64 +0,0 @@
#if UNITY_EDITOR || DEVELOPMENT_BUILD
using NUnit.Framework;
using UnityEngine.InputSystem;
using UnityEngine.Profiling;
////TODO: write a test that generates a pseudo-random event sequence and runs it through a pseudo-random
//// update pattern and verifies the state and action outcome is as expected
internal class CoreStressTests
{
[Test]
[Category("Stress")]
[Ignore("TODO")]
public void TODO_Stress_512GamepadsAnd10000Events()
{
const int kGamepadCount = 512;
const int kEventCount = 10000;
Profiler.BeginSample("CreateDevices");
var gamepads = new Gamepad[kGamepadCount];
for (var i = 0; i < kGamepadCount; ++i)
gamepads[i] = InputSystem.AddDevice<Gamepad>();
Profiler.EndSample();
Profiler.BeginSample("QueueEvents");
var gamepadIndex = 0;
for (var i = 0; i < kEventCount; ++i)
{
//InputSystem.QueueStateEvent(gamepads[gamepadIndex], );
++gamepadIndex;
gamepadIndex = gamepadIndex % kGamepadCount;
}
Profiler.EndSample();
Profiler.BeginSample("ProcessEvents");
Profiler.EndSample();
//verify
Assert.Fail();
}
[Test]
[Category("Stress")]
[Ignore("TODO")]
public void TODO_Stress_100ActionMapsInAssetWith1000Actions()
{
Profiler.BeginSample("CreateDevices");
Profiler.EndSample();
Profiler.BeginSample("CreateActions");
Profiler.EndSample();
Profiler.BeginSample("EnableActions");
Profiler.EndSample();
Profiler.BeginSample("ProcessEvents");
Profiler.EndSample();
Assert.Fail();
}
}
#endif // UNITY_EDITOR || DEVELOPMENT_BUILD

Просмотреть файл

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

Просмотреть файл

@ -3458,7 +3458,7 @@ partial class CoreTests
[Test]
[Category("Actions")]
public void Actions_ControlsUpdateWhenDeviceIsRemoved()
public void Actions_WhenDeviceIsRemoved_BoundControlsAreUpdated()
{
var gamepad = InputSystem.AddDevice<Gamepad>();
@ -3475,13 +3475,31 @@ partial class CoreTests
[Test]
[Category("Actions")]
public void Actions_ActionListenerWillNotThrowWhenDeviceIsRemoved()
public void Actions_WhenDeviceIsRemoved_OngoingActionsAreCancelled()
{
var gamepad = InputSystem.AddDevice<Gamepad>();
float triggerValue = 0.0f;
bool canceled = false;
bool performed = false;
var action = new InputAction(binding: "<Gamepad>/leftTrigger");
action.Enable();
Set(gamepad.leftTrigger, 0.75f);
Assert.That(action.inProgress, Is.True);
InputSystem.RemoveDevice(gamepad);
Assert.That(action.inProgress, Is.False);
}
[Test]
[Category("Actions")]
public void Actions_WhenDeviceIsRemoved_ReadingValueInActionListenersWillNotThrow()
{
var gamepad = InputSystem.AddDevice<Gamepad>();
var triggerValue = 0.0f;
var canceled = false;
var performed = false;
var action = new InputAction();
action.AddBinding("<Gamepad>/leftTrigger");

Просмотреть файл

@ -1503,45 +1503,37 @@ partial class CoreTests
[Category("Devices")]
public void Devices_WhenCreationFails_SystemRecoversGracefully()
{
// Create an isolated runtime + input manager.
using (var runtime = new InputTestRuntime())
// Create a device layout that will fail to instantiate.
const string layout = @"
{
""name"" : ""TestDevice"",
""controls"" : [
{ ""name"" : ""test"", ""layout"" : ""DoesNotExist"" }
]
}
";
InputSystem.RegisterLayout(layout);
// Report two devices, one that will fail creation and one that shouldn't.
runtime.ReportNewInputDevice(new InputDeviceDescription
{
var manager = new InputManager();
var settings = ScriptableObject.CreateInstance<InputSettings>();
manager.Initialize(runtime, settings);
deviceClass = "TestDevice"
}.ToJson());
runtime.ReportNewInputDevice(new InputDeviceDescription
{
deviceClass = "Gamepad"
}.ToJson());
// Create a device layout that will fail to instantiate.
const string layout = @"
{
""name"" : ""TestDevice"",
""controls"" : [
{ ""name"" : ""test"", ""layout"" : ""DoesNotExist"" }
]
}
";
manager.RegisterControlLayout(layout);
LogAssert.Expect(LogType.Error,
new Regex(".*Could not create a device for 'TestDevice'.*Cannot find layout 'DoesNotExist'.*"));
// Report two devices, one that will fail creation and one that shouldn't.
runtime.ReportNewInputDevice(new InputDeviceDescription
{
deviceClass = "TestDevice"
}.ToJson());
runtime.ReportNewInputDevice(new InputDeviceDescription
{
deviceClass = "Gamepad"
}.ToJson());
Assert.That(() => InputSystem.Update(), Throws.Nothing);
LogAssert.Expect(LogType.Error,
new Regex(".*Could not create a device for 'TestDevice'.*Cannot find layout 'DoesNotExist'.*"));
// Make sure InputManager kept the gamepad.
Assert.That(InputSystem.devices.Count, Is.EqualTo(1));
Assert.That(InputSystem.devices, Has.Exactly(1).TypeOf<Gamepad>());
Assert.That(() => manager.Update(), Throws.Nothing);
// Make sure InputManager kept the gamepad.
Assert.That(manager.devices.Count, Is.EqualTo(1));
Assert.That(manager.devices, Has.Exactly(1).TypeOf<Gamepad>());
LogAssert.NoUnexpectedReceived();
}
LogAssert.NoUnexpectedReceived();
}
[Test]
@ -1551,28 +1543,25 @@ partial class CoreTests
var device = InputSystem.AddDevice<Mouse>();
bool? disabled = null;
unsafe
{
runtime.SetDeviceCommandCallback(device.deviceId,
(id, commandPtr) =>
runtime.SetDeviceCommandCallback(device.deviceId,
(id, commandPtr) =>
{
if (commandPtr->type == DisableDeviceCommand.Type)
{
if (commandPtr->type == DisableDeviceCommand.Type)
{
Assert.That(disabled, Is.Null);
disabled = true;
return InputDeviceCommand.GenericSuccess;
}
Assert.That(disabled, Is.Null);
disabled = true;
return InputDeviceCommand.GenericSuccess;
}
if (commandPtr->type == EnableDeviceCommand.Type)
{
Assert.That(disabled, Is.Null);
disabled = false;
return InputDeviceCommand.GenericSuccess;
}
if (commandPtr->type == EnableDeviceCommand.Type)
{
Assert.That(disabled, Is.Null);
disabled = false;
return InputDeviceCommand.GenericSuccess;
}
return InputDeviceCommand.GenericFailure;
});
}
return InputDeviceCommand.GenericFailure;
});
Assert.That(device.enabled, Is.True);
Assert.That(disabled, Is.Null);
@ -1599,6 +1588,40 @@ partial class CoreTests
Assert.That(disabled.Value, Is.False);
}
[Test]
[Category("Devices")]
public unsafe void Devices_CanBeDisabled_WhileLettingItKeepSendingEvents()
{
var device = InputSystem.AddDevice<Mouse>();
runtime.SetDeviceCommandCallback(device.deviceId,
(id, commandPtr) =>
{
if (commandPtr->type == DisableDeviceCommand.Type)
Assert.Fail("Device should not receive a DisableDeviceCommand");
return InputDeviceCommand.GenericFailure;
});
InputSystem.DisableDevice(device, keepSendingEvents: true);
Assert.That(device.enabled, Is.False);
var receivedEvent = false;
InputSystem.onEvent +=
(eventPtr, d) =>
{
Assert.That(d, Is.SameAs(device));
Assert.That(receivedEvent, Is.False);
receivedEvent = true;
};
InputSystem.QueueStateEvent(device, new MouseState());
InputSystem.Update();
Assert.That(receivedEvent, Is.True);
}
[Test]
[Category("Devices")]
public void Devices_WhenDisabledOrReEnabled_TriggersNotification()

Просмотреть файл

@ -406,11 +406,6 @@ partial class CoreTests
local.StartSending();
}
~FakeRemote()
{
Dispose();
}
public void Dispose()
{
if (runtime != null)
@ -418,6 +413,11 @@ partial class CoreTests
runtime.Dispose();
runtime = null;
}
if (manager != null)
{
Object.Destroy(manager.m_Settings);
manager.Destroy();
}
}
}

Просмотреть файл

@ -1080,4 +1080,32 @@ internal class EnhancedTouchTests : InputTestFixture
Assert.That(TouchSimulation.instance.simulatedTouchscreen, Is.SameAs(device));
}
[Test]
[Category("EnhancedTouch")]
public unsafe void EnhancedTouch_TouchSimulation_DisablesPointerDevicesWithoutDisablingEvents()
{
var mouse = InputSystem.AddDevice<Mouse>();
var pen = InputSystem.AddDevice<Pen>();
runtime.SetDeviceCommandCallback(mouse, (id, command) =>
{
Assert.That(command->type, Is.Not.EqualTo(DisableDeviceCommand.Type));
return InputDeviceCommand.GenericFailure;
});
TouchSimulation.Enable();
Assert.That(mouse.enabled, Is.False);
Assert.That(pen.enabled, Is.False);
InputSystem.QueueStateEvent(mouse, new MouseState
{
position = new Vector2(123, 234),
}.WithButton(MouseButton.Left));
InputSystem.Update();
Assert.That(Touchscreen.current.touches[0].isInProgress, Is.True);
Assert.That(Touchscreen.current.touches[0].position.ReadValue(), Is.EqualTo(new Vector2(123, 234)));
}
}

Просмотреть файл

@ -1,3 +1,4 @@
using System.Collections;
using NUnit.Framework;
using System.Linq;
using UnityEngine;
@ -6,7 +7,10 @@ using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.OnScreen;
using UnityEngine.InputSystem.UI;
using UnityEngine.TestTools;
using UnityEngine.TestTools.Utils;
using UnityEngine.UI;
internal class OnScreenTests : InputTestFixture
{
@ -157,4 +161,118 @@ internal class OnScreenTests : InputTestFixture
Assert.That(InputSystem.devices, Has.None.InstanceOf<Keyboard>());
}
// https://fogbugz.unity3d.com/f/cases/1271942
[UnityTest]
[Category("Devices")]
public IEnumerator Devices_CanHaveOnScreenJoystickControls()
{
foreach (var c in Camera.allCameras)
Object.Destroy(c.gameObject);
yield return null;
InputSystem.AddDevice<Touchscreen>();
// Set up a full UI scene with an on-screen stick and button.
var eventSystemGO = new GameObject("EventSystem");
var eventSystem = eventSystemGO.AddComponent<TestEventSystem>();
var uiModule = eventSystemGO.AddComponent<InputSystemUIInputModule>();
eventSystem.OnApplicationFocus(true);
var uiActions = new DefaultInputActions();
uiModule.actionsAsset = uiActions.asset;
uiModule.leftClick = InputActionReference.Create(uiActions.UI.Click);
uiModule.point = InputActionReference.Create(uiActions.UI.Point);
var canvasGO = new GameObject("Canvas");
var canvasTransform = canvasGO.AddComponent<RectTransform>();
var canvas = canvasGO.AddComponent<Canvas>();
canvasGO.AddComponent<GraphicRaycaster>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
var stickGO = new GameObject("Stick");
stickGO.SetActive(false);
var stickTransform = stickGO.AddComponent<RectTransform>();
var stick = stickGO.AddComponent<OnScreenStick>();
stickGO.AddComponent<Image>();
stickTransform.SetParent(canvasTransform);
stickTransform.anchorMin = new Vector2(0, 0);
stickTransform.anchorMax = new Vector2(0, 0);
stickTransform.anchoredPosition = new Vector2(100, 100);
stickTransform.sizeDelta = new Vector2(100, 100);
stick.controlPath = "<Gamepad>/leftStick";
stickGO.SetActive(true);
var buttonGO = new GameObject("Button");
buttonGO.SetActive(false);
var buttonTransform = buttonGO.AddComponent<RectTransform>();
var button = buttonGO.AddComponent<OnScreenButton>();
buttonGO.AddComponent<Button>();
buttonGO.AddComponent<Image>(); // Give it a Graphic so the raycaster sees it.
buttonTransform.SetParent(canvasTransform);
buttonTransform.anchorMin = new Vector2(0, 0);
buttonTransform.anchorMax = new Vector2(0, 0);
buttonTransform.anchoredPosition = new Vector2(300, 100);
buttonTransform.sizeDelta = new Vector2(100, 100);
button.controlPath = "<Gamepad>/buttonSouth";
buttonGO.SetActive(true);
// Add player and hook it up to the gamepad.
var playerActions = new DefaultInputActions();
var playerGO = new GameObject("Player");
playerGO.SetActive(false);
var player = playerGO.AddComponent<PlayerInput>();
player.actions = playerActions.asset;
player.defaultControlScheme = "Gamepad";
player.neverAutoSwitchControlSchemes = true;
playerGO.SetActive(true);
yield return null;
eventSystem.Update();
Assert.That(player.devices, Is.EquivalentTo(new[] { Gamepad.all[0] }));
// Touch the stick and drag it upwards.
BeginTouch(1, new Vector2(150, 150));
yield return null;
eventSystem.Update();
Assert.That(eventSystem.IsPointerOverGameObject(), Is.True);
MoveTouch(1, new Vector2(150, 200));
yield return null;
eventSystem.Update();
InputSystem.Update(); // Stick is feeding events when responding to UI events.
Assert.That(Gamepad.all[0].leftStick.ReadValue(), Is.EqualTo(new Vector2(0, 1)).Using(Vector2EqualityComparer.Instance));
// Press the button.
BeginTouch(2, new Vector2(350, 150));
yield return null;
eventSystem.Update();
InputSystem.Update(); // Button is feeding events when responding to UI events.
Assert.That(Gamepad.all[0].buttonSouth.isPressed, Is.True);
// Release the button.
EndTouch(2, new Vector2(351, 151));
yield return null;
eventSystem.Update();
InputSystem.Update(); // Button is feeding events when responding to UI events.
Assert.That(Gamepad.all[0].buttonSouth.isPressed, Is.False);
}
private class TestEventSystem : EventSystem
{
public new void OnApplicationFocus(bool hasFocus)
{
base.OnApplicationFocus(hasFocus);
}
public new void Update()
{
base.Update();
}
}
}

Просмотреть файл

@ -6,6 +6,7 @@ using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.EnhancedTouch;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.UI;
using UnityEngine.Profiling;
@ -22,6 +23,7 @@ internal class UITests : InputTestFixture
{
private struct TestObjects
{
public Camera camera;
public InputSystemUIInputModule uiModule;
public TestEventSystem eventSystem;
public GameObject parentGameObject;
@ -30,60 +32,72 @@ internal class UITests : InputTestFixture
public UICallbackReceiver parentReceiver;
public UICallbackReceiver leftChildReceiver;
public UICallbackReceiver rightChildReceiver;
// Assume a 640x480 resolution and translate the given coordinates from a resolution
// in that space to coordinates in the current camera screen space.
public Vector2 From640x480ToScreen(float x, float y)
{
var cameraRect = camera.rect;
var cameraPixelRect = camera.pixelRect;
var result = new Vector2(cameraPixelRect.x + x / 640f * cameraRect.width * cameraPixelRect.width,
cameraPixelRect.y + y / 480f * cameraRect.height * cameraPixelRect.height);
// Pixel-snap. Not sure where this is coming from but Mac tests are failing without this.
return new Vector2(Mathf.Floor(result.x), Mathf.Floor(result.y));
}
public bool IsWithinRect(Vector2 screenPoint, GameObject gameObject)
{
var transform = gameObject.GetComponent<RectTransform>();
return RectTransformUtility.RectangleContainsScreenPoint(transform, screenPoint, camera, default);
}
}
// Set up a InputSystemUIInputModule with a full roster of actions and inputs
// and then see if we can generate all the various events expected by the UI
// from activity on input devices.
private static TestObjects CreateScene(int minY = 0 , int maxY = 480, bool noFirstSelected = false)
private static TestObjects CreateUIAndPlayer(Rect viewport = default, bool noFirstSelected = false, string namePrefix = "")
{
var objects = new TestObjects();
// Set up GameObject with EventSystem.
var systemObject = new GameObject("System");
var systemObject = new GameObject(namePrefix + "System");
objects.eventSystem = systemObject.AddComponent<TestEventSystem>();
var uiModule = systemObject.AddComponent<InputSystemUIInputModule>();
objects.uiModule = uiModule;
objects.eventSystem.UpdateModules();
var cameraObject = GameObject.Find("Camera");
Camera camera;
if (cameraObject == null)
{
// Set up camera and canvas on which we can perform raycasts.
cameraObject = new GameObject("Camera");
camera = cameraObject.AddComponent<Camera>();
camera.stereoTargetEye = StereoTargetEyeMask.None;
camera.pixelRect = new Rect(0, 0, 640, 480);
}
else
camera = cameraObject.GetComponent<Camera>();
var cameraObject = new GameObject(namePrefix + "Camera");
objects.camera = cameraObject.AddComponent<Camera>();
objects.camera.stereoTargetEye = StereoTargetEyeMask.None;
objects.camera.rect = viewport == default ? new Rect(0, 0, 1, 1) : viewport;
var canvasObject = GameObject.Find("Canvas");
if (canvasObject == null)
{
canvasObject = new GameObject("Canvas");
var canvas = canvasObject.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceCamera;
canvasObject.AddComponent<GraphicRaycaster>();
canvasObject.AddComponent<TrackedDeviceRaycaster>();
canvas.worldCamera = camera;
}
var canvasObject = new GameObject(namePrefix + "Canvas");
canvasObject.SetActive(false);
var canvas = canvasObject.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceCamera;
canvasObject.AddComponent<GraphicRaycaster>();
canvasObject.AddComponent<TrackedDeviceRaycaster>();
canvas.worldCamera = objects.camera;
// Set up a GameObject hierarchy that we send events to. In a real setup,
// this would be a hierarchy involving UI components.
var parentGameObject = new GameObject("Parent");
var parentGameObject = new GameObject(namePrefix + "Parent");
parentGameObject.SetActive(false);
var parentTransform = parentGameObject.AddComponent<RectTransform>();
objects.parentGameObject = parentGameObject;
objects.parentReceiver = parentGameObject.AddComponent<UICallbackReceiver>();
var leftChildGameObject = new GameObject("Left Child");
var leftChildGameObject = new GameObject(namePrefix + "Left Child");
leftChildGameObject.SetActive(false);
var leftChildTransform = leftChildGameObject.AddComponent<RectTransform>();
leftChildGameObject.AddComponent<Image>();
objects.leftChildReceiver = leftChildGameObject.AddComponent<UICallbackReceiver>();
objects.leftGameObject = leftChildGameObject;
var rightChildGameObject = new GameObject("Right Child");
var rightChildGameObject = new GameObject(namePrefix + "Right Child");
rightChildGameObject.SetActive(false);
var rightChildTransform = rightChildGameObject.AddComponent<RectTransform>();
rightChildGameObject.AddComponent<Image>();
objects.rightChildReceiver = rightChildGameObject.AddComponent<UICallbackReceiver>();
@ -94,16 +108,27 @@ internal class UITests : InputTestFixture
rightChildTransform.SetParent(parentTransform, worldPositionStays: false);
// Parent occupies full space of canvas.
parentTransform.sizeDelta = new Vector2(640, maxY - minY);
parentTransform.anchoredPosition = new Vector2(0, (maxY + minY) / 2 - 240);
parentTransform.anchoredPosition = default;
parentTransform.anchorMin = Vector2.zero;
parentTransform.anchorMax = Vector2.one;
parentTransform.sizeDelta = default;
// Left child occupies left half of parent.
leftChildTransform.anchoredPosition = new Vector2(-(640 / 4), 0); //(maxY + minY)/2 - 240);
leftChildTransform.sizeDelta = new Vector2(320, maxY - minY);
leftChildTransform.anchoredPosition = default;
leftChildTransform.anchorMin = default;
leftChildTransform.anchorMax = new Vector2(0.5f, 1);
leftChildTransform.sizeDelta = default;
// Right child occupies right half of parent.
rightChildTransform.anchoredPosition = new Vector2(640 / 4, 0); //(maxY + minY) / 2 - 240);
rightChildTransform.sizeDelta = new Vector2(320, maxY - minY);
rightChildTransform.anchoredPosition = default;
rightChildTransform.anchorMin = new Vector2(0.5f, 0);
rightChildTransform.anchorMax = new Vector2(1, 1);
rightChildTransform.sizeDelta = default;
canvasObject.SetActive(true);
parentGameObject.SetActive(true);
leftChildGameObject.SetActive(true);
rightChildGameObject.SetActive(true);
objects.eventSystem.playerRoot = parentGameObject;
if (!noFirstSelected)
@ -113,6 +138,23 @@ internal class UITests : InputTestFixture
return objects;
}
private static void AssignDefaultActions(ref TestObjects setup)
{
var defaultActions = new DefaultInputActions();
setup.uiModule.actionsAsset = defaultActions.asset;
setup.uiModule.cancel = InputActionReference.Create(defaultActions.UI.Cancel);
setup.uiModule.submit = InputActionReference.Create(defaultActions.UI.Submit);
setup.uiModule.move = InputActionReference.Create(defaultActions.UI.Navigate);
setup.uiModule.leftClick = InputActionReference.Create(defaultActions.UI.Click);
setup.uiModule.rightClick = InputActionReference.Create(defaultActions.UI.RightClick);
setup.uiModule.middleClick = InputActionReference.Create(defaultActions.UI.MiddleClick);
setup.uiModule.point = InputActionReference.Create(defaultActions.UI.Point);
setup.uiModule.scrollWheel = InputActionReference.Create(defaultActions.UI.ScrollWheel);
defaultActions.Enable();
}
// Comprehensive test for general pointer input behaviors.
// NOTE: The behavior we test for here is slightly *DIFFERENT* than what you get with StandaloneInputModule. The reason is that
// StandaloneInputModule has both lots of inconsistencies between touch and mouse input (example: touch press handling goes
@ -123,6 +165,9 @@ internal class UITests : InputTestFixture
// of to the previous click).
[UnityTest]
[Category("UI")]
#if UNITY_IOS || UNITY_TVOS
[Ignore("Failing on iOS https://jira.unity3d.com/browse/ISX-448")]
#endif
// All pointer input goes through a single code path. Goes for Pointer-derived devices as well as for TrackedDevice input but
// also any other input that can deliver point and click functionality.
//
@ -148,7 +193,7 @@ internal class UITests : InputTestFixture
var trackedOrientation = isTracked ? Quaternion.Euler(0, -90, 0) : default;
var trackedPosition = isTracked ? new Vector3(0.001f, 0.001f, 0.001f) : default;
var scene = CreateScene();
var scene = CreateUIAndPlayer();
const string kActions = @"
{
@ -228,16 +273,9 @@ internal class UITests : InputTestFixture
Assert.That(scene.eventSystem.IsPointerOverGameObject(), Is.False);
var firstScreenPosition = new Vector2(100, 100);
var secondScreenPosition = new Vector2(100, 200);
var thirdScreenPosition = new Vector2(350, 200);
if (isTracked)
{
firstScreenPosition = new Vector2(28.936f, 240);
secondScreenPosition = new Vector2(80.006f, 240);
thirdScreenPosition = new Vector2(560, 240);
}
var firstScreenPosition = scene.From640x480ToScreen(100, 100);
var secondScreenPosition = scene.From640x480ToScreen(100, 200);
var thirdScreenPosition = scene.From640x480ToScreen(350, 200);
// Move pointer over left child.
currentTime = 1;
@ -263,6 +301,15 @@ internal class UITests : InputTestFixture
Assert.That(scene.eventSystem.IsPointerOverGameObject(pointerId), Is.True);
if (isTracked)
{
// Different screen geometries will lead to different ray intersection points from tracked devices.
// Only check whether we reported a position inside of leftGameObject.
Assert.That(scene.IsWithinRect(scene.leftChildReceiver.events[0].pointerData.position, scene.leftGameObject), Is.True);
firstScreenPosition = scene.leftChildReceiver.events[0].pointerData.position;
}
// For both regular pointers and touch, pointer enter is the first event.
// NOTE: This is different to StandaloneInputModule where for mouse, click comes before pointer enter.
Assert.That(scene.leftChildReceiver.events[0].type, Is.EqualTo(EventType.PointerEnter));
@ -595,8 +642,17 @@ internal class UITests : InputTestFixture
scene.eventSystem.InvokeUpdate();
Assert.That(scene.eventSystem.IsPointerOverGameObject(pointerId), Is.True);
Assert.That(scene.leftChildReceiver.events, Has.Count.EqualTo(2));
if (isTracked)
{
// Different screen geometries will lead to different ray intersection points from tracked devices.
// Only check whether we reported a position inside of leftGameObject.
Assert.That(scene.IsWithinRect(scene.leftChildReceiver.events[0].pointerData.position, scene.leftGameObject), Is.True);
secondScreenPosition = scene.leftChildReceiver.events[0].pointerData.position;
}
Assert.That(scene.leftChildReceiver.events[0].type, Is.EqualTo(EventType.BeginDrag));
Assert.That(scene.leftChildReceiver.events[0].pointerData.device, Is.SameAs(device));
Assert.That(scene.leftChildReceiver.events[0].pointerData.button, Is.EqualTo(clickButton));
@ -677,11 +733,20 @@ internal class UITests : InputTestFixture
Assert.That(scene.eventSystem.IsPointerOverGameObject(pointerId), Is.True);
Assert.That(scene.parentReceiver.events, Is.Empty); // Should not have seen pointer enter/exit on parent.
Assert.That(scene.leftChildReceiver.events, Has.Count.EqualTo(2));
if (isTracked)
{
// Different screen geometries will lead to different ray intersection points from tracked devices.
// Only check whether we reported a position inside of rightGameObject.
Assert.That(scene.IsWithinRect(scene.leftChildReceiver.events[0].pointerData.position, scene.rightGameObject), Is.True);
thirdScreenPosition = scene.leftChildReceiver.events[0].pointerData.position;
}
// Input module (like StandaloneInputModule on mouse path) processes move first which is why
// we get an exit *before* a drag even though it would make more sense the other way round.
Assert.That(scene.leftChildReceiver.events, Has.Count.EqualTo(2));
Assert.That(scene.leftChildReceiver.events[0].type, Is.EqualTo(EventType.PointerExit));
Assert.That(scene.leftChildReceiver.events[0].pointerData.button, Is.EqualTo(PointerEventData.InputButton.Left));
Assert.That(scene.leftChildReceiver.events[0].pointerData.pointerId, Is.EqualTo(pointerId));
@ -1014,6 +1079,9 @@ internal class UITests : InputTestFixture
[TestCase(UIPointerBehavior.SingleUnifiedPointer, ExpectedResult = -1)]
[TestCase(UIPointerBehavior.AllPointersAsIs, ExpectedResult = -1)]
[TestCase(UIPointerBehavior.SingleMouseOrPenButMultiTouchAndTrack, ExpectedResult = -1)]
#if UNITY_IOS || UNITY_TVOS
[Ignore("Failing on iOS https://jira.unity3d.com/browse/ISX-448")]
#endif
public IEnumerator UI_CanDriveUIFromMultiplePointers(UIPointerBehavior pointerBehavior)
{
InputSystem.RegisterLayout(kTrackedDeviceWithButton);
@ -1027,7 +1095,7 @@ internal class UITests : InputTestFixture
var trackedDevice1 = (TrackedDevice)InputSystem.AddDevice("TrackedDeviceWithButton");
var trackedDevice2 = (TrackedDevice)InputSystem.AddDevice("TrackedDeviceWithButton");
var scene = CreateScene();
var scene = CreateUIAndPlayer();
scene.uiModule.pointerBehavior = pointerBehavior;
var actions = ScriptableObject.CreateInstance<InputActionAsset>();
@ -1063,20 +1131,22 @@ internal class UITests : InputTestFixture
scene.leftChildReceiver.events.Clear();
// Put mouse1 over left object.
Set(mouse1.position, new Vector2(100, 100));
var firstPosition = scene.From640x480ToScreen(100, 100);
Set(mouse1.position, firstPosition);
scene.eventSystem.InvokeUpdate();
Assert.That(scene.leftChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerEnter).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == mouse1).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(100, 100)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == firstPosition));
Assert.That(scene.rightChildReceiver.events, Is.Empty);
scene.leftChildReceiver.events.Clear();
scene.rightChildReceiver.events.Clear();
// Put mouse2 over right object.
Set(mouse2.position, new Vector2(350, 200));
var secondPosition = scene.From640x480ToScreen(350, 200);
Set(mouse2.position, secondPosition);
scene.eventSystem.InvokeUpdate();
switch (pointerBehavior)
@ -1087,11 +1157,11 @@ internal class UITests : InputTestFixture
Assert.That(scene.leftChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerExit).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == mouse2).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(350, 200)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == secondPosition));
Assert.That(scene.rightChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerEnter).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == mouse2).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(350, 200)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == secondPosition));
break;
case UIPointerBehavior.AllPointersAsIs:
@ -1100,7 +1170,7 @@ internal class UITests : InputTestFixture
Assert.That(scene.rightChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerEnter).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == mouse2).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(350, 200)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == secondPosition));
break;
}
@ -1115,6 +1185,9 @@ internal class UITests : InputTestFixture
Set(trackedDevice2, "deviceRotation", Quaternion.Euler(0, 30, 0));
scene.eventSystem.InvokeUpdate();
var leftPosition = scene.From640x480ToScreen(80, 240);
var rightPosition = scene.From640x480ToScreen(560, 240);
switch (pointerBehavior)
{
case UIPointerBehavior.SingleUnifiedPointer:
@ -1123,22 +1196,22 @@ internal class UITests : InputTestFixture
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerExit).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == trackedDevice1).And // System transparently switched from mouse2 to trackedDevice1.
.Matches((UICallbackReceiver.Event e) =>
Mathf.Approximately(e.pointerData.position.x, 80f) && Mathf.Approximately(e.pointerData.position.y, 240))); // Exits at position of trackedDevice1.
scene.IsWithinRect(e.pointerData.position, scene.leftGameObject))); // Exits at position of trackedDevice1.
Assert.That(scene.leftChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerEnter).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == trackedDevice1).And
.Matches((UICallbackReceiver.Event e) =>
Mathf.Approximately(e.pointerData.position.x, 80f) && Mathf.Approximately(e.pointerData.position.y, 240)));
scene.IsWithinRect(e.pointerData.position, scene.leftGameObject)));
Assert.That(scene.leftChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerExit).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == trackedDevice2).And
.Matches((UICallbackReceiver.Event e) =>
Mathf.Approximately(e.pointerData.position.x, 560f) && Mathf.Approximately(e.pointerData.position.y, 240)));
scene.IsWithinRect(e.pointerData.position, scene.rightGameObject)));
Assert.That(scene.rightChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerEnter).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == trackedDevice2).And
.Matches((UICallbackReceiver.Event e) =>
Mathf.Approximately(e.pointerData.position.x, 560) && Mathf.Approximately(e.pointerData.position.y, 240)));
scene.IsWithinRect(e.pointerData.position, scene.rightGameObject)));
break;
case UIPointerBehavior.AllPointersAsIs:
@ -1149,7 +1222,7 @@ internal class UITests : InputTestFixture
Assert.That(scene.rightChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerExit).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == mouse2).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(350, 200)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == secondPosition));
}
// Pointer-enter on left, pointer-enter on right.
@ -1157,12 +1230,12 @@ internal class UITests : InputTestFixture
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerEnter).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == trackedDevice1).And
.Matches((UICallbackReceiver.Event e) =>
Mathf.Approximately(e.pointerData.position.x, 80f) && Mathf.Approximately(e.pointerData.position.y, 240)));
scene.IsWithinRect(e.pointerData.position, scene.leftGameObject)));
Assert.That(scene.rightChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerEnter).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == trackedDevice2).And
.Matches((UICallbackReceiver.Event e) =>
Mathf.Approximately(e.pointerData.position.x, 560) && Mathf.Approximately(e.pointerData.position.y, 240)));
scene.IsWithinRect(e.pointerData.position, scene.rightGameObject)));
break;
}
@ -1170,9 +1243,9 @@ internal class UITests : InputTestFixture
scene.rightChildReceiver.events.Clear();
// Touch right object on first touchscreen and left object on second touchscreen.
BeginTouch(1, new Vector2(350, 200), screen: touch1);
BeginTouch(1, secondPosition, screen: touch1);
scene.eventSystem.InvokeUpdate();
BeginTouch(1, new Vector2(100, 100), screen: touch2);
BeginTouch(1, firstPosition, screen: touch2);
scene.eventSystem.InvokeUpdate();
switch (pointerBehavior)
@ -1184,22 +1257,22 @@ internal class UITests : InputTestFixture
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerDown).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == touch1).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.touchId == 1).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(350, 200)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == secondPosition));
Assert.That(scene.rightChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerExit).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == touch2).And // Transparently switched.
.Matches((UICallbackReceiver.Event e) => e.pointerData.touchId == 1).And // Transparently switched.
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(100, 100)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == firstPosition));
Assert.That(scene.rightChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.BeginDrag).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == touch2).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.touchId == 1).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(100, 100)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == firstPosition));
Assert.That(scene.leftChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerEnter).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == touch2).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.touchId == 1).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(100, 100)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == firstPosition));
break;
case UIPointerBehavior.SingleMouseOrPenButMultiTouchAndTrack:
@ -1208,11 +1281,11 @@ internal class UITests : InputTestFixture
Assert.That(scene.rightChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerEnter).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == touch1).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(350, 200)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == secondPosition));
Assert.That(scene.leftChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerEnter).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == touch2).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(100, 100)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == firstPosition));
Assert.That(scene.leftChildReceiver.events, Has.None.With.Property("type").EqualTo(EventType.Dragging));
Assert.That(scene.rightChildReceiver.events, Has.None.With.Property("type").EqualTo(EventType.Dragging));
break;
@ -1227,7 +1300,7 @@ internal class UITests : InputTestFixture
// Prevent default selection of left object. This means that we will not have to contend with selections at all
// in this test as they are driven from UI objects and not by the input module itself.
var scene = CreateScene(noFirstSelected: true);
var scene = CreateUIAndPlayer(noFirstSelected: true);
var asset = ScriptableObject.CreateInstance<InputActionAsset>();
var map = asset.AddActionMap("map");
@ -1250,7 +1323,8 @@ internal class UITests : InputTestFixture
Assert.That(scene.eventSystem.IsPointerOverGameObject(3), Is.False);
// Touch left object.
BeginTouch(1, new Vector2(100, 100));
var firstPosition = scene.From640x480ToScreen(100, 100);
BeginTouch(1, firstPosition);
scene.eventSystem.InvokeUpdate();
Assert.That(scene.eventSystem.IsPointerOverGameObject(), Is.True);
@ -1264,20 +1338,21 @@ internal class UITests : InputTestFixture
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == touchScreen).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.touchId == 1).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.pointerType == UIPointerType.Touch).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(100, 100)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == firstPosition));
Assert.That(scene.leftChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerDown).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == touchScreen).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.touchId == 1).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.pointerType == UIPointerType.Touch).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(100, 100)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == firstPosition));
Assert.That(scene.rightChildReceiver.events, Is.Empty);
scene.leftChildReceiver.events.Clear();
scene.rightChildReceiver.events.Clear();
// Touch right object.
BeginTouch(2, new Vector2(350, 200));
var secondPosition = scene.From640x480ToScreen(350, 200);
BeginTouch(2, secondPosition);
scene.eventSystem.InvokeUpdate();
Assert.That(scene.eventSystem.IsPointerOverGameObject(), Is.True);
@ -1291,20 +1366,21 @@ internal class UITests : InputTestFixture
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == touchScreen).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.touchId == 2).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.pointerType == UIPointerType.Touch).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(350, 200)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == secondPosition));
Assert.That(scene.rightChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerDown).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == touchScreen).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.touchId == 2).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.pointerType == UIPointerType.Touch).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(350, 200)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == secondPosition));
Assert.That(scene.leftChildReceiver.events, Is.Empty);
scene.leftChildReceiver.events.Clear();
scene.rightChildReceiver.events.Clear();
// Drag left object over right object.
MoveTouch(1, new Vector2(355, 210));
var thirdPosition = scene.From640x480ToScreen(355, 210);
MoveTouch(1, thirdPosition);
scene.eventSystem.InvokeUpdate();
Assert.That(scene.eventSystem.IsPointerOverGameObject(), Is.True);
@ -1318,25 +1394,26 @@ internal class UITests : InputTestFixture
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == touchScreen).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.touchId == 1).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.pointerType == UIPointerType.Touch).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(355, 210)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == thirdPosition));
Assert.That(scene.leftChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerExit).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == touchScreen).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.touchId == 1).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.pointerType == UIPointerType.Touch).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(355, 210)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == thirdPosition));
Assert.That(scene.rightChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerEnter).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == touchScreen).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.touchId == 1).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.pointerType == UIPointerType.Touch).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(355, 210)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == thirdPosition));
scene.leftChildReceiver.events.Clear();
scene.rightChildReceiver.events.Clear();
// Touch left object again.
BeginTouch(3, new Vector2(123, 123));
var fourthPosition = scene.From640x480ToScreen(123, 123);
BeginTouch(3, fourthPosition);
scene.eventSystem.InvokeUpdate();
Assert.That(scene.eventSystem.IsPointerOverGameObject(), Is.True);
@ -1350,14 +1427,15 @@ internal class UITests : InputTestFixture
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == touchScreen).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.touchId == 3).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.pointerType == UIPointerType.Touch).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(123, 123)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == fourthPosition));
Assert.That(scene.rightChildReceiver.events, Is.Empty);
scene.leftChildReceiver.events.Clear();
scene.rightChildReceiver.events.Clear();
// End second touch.
EndTouch(2, new Vector2(355, 205));
var fifthPosition = scene.From640x480ToScreen(355, 205);
EndTouch(2, fifthPosition);
scene.eventSystem.InvokeUpdate();
Assert.That(scene.eventSystem.IsPointerOverGameObject(), Is.True);
@ -1371,20 +1449,21 @@ internal class UITests : InputTestFixture
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == touchScreen).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.touchId == 2).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.pointerType == UIPointerType.Touch).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(355, 205)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == fifthPosition));
Assert.That(scene.rightChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerUp).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == touchScreen).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.touchId == 2).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.pointerType == UIPointerType.Touch).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(355, 205)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == fifthPosition));
Assert.That(scene.leftChildReceiver.events, Is.Empty);
scene.leftChildReceiver.events.Clear();
scene.rightChildReceiver.events.Clear();
// Begin second touch again.
BeginTouch(2, new Vector2(345, 195));
var sixthPosition = scene.From640x480ToScreen(345, 195);
BeginTouch(2, sixthPosition);
scene.eventSystem.InvokeUpdate();
Assert.That(scene.eventSystem.IsPointerOverGameObject(), Is.True);
@ -1398,16 +1477,70 @@ internal class UITests : InputTestFixture
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == touchScreen).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.touchId == 2).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.pointerType == UIPointerType.Touch).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(345, 195)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == sixthPosition));
Assert.That(scene.rightChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerDown).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == touchScreen).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.touchId == 2).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.pointerType == UIPointerType.Touch).And
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == new Vector2(345, 195)));
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == sixthPosition));
Assert.That(scene.leftChildReceiver.events, Is.Empty);
}
// https://fogbugz.unity3d.com/f/cases/1190150/
[UnityTest]
[Category("UI")]
public IEnumerator UI_CanUseTouchSimulationWithUI()
{
var mouse = InputSystem.AddDevice<Mouse>();
var scene = CreateUIAndPlayer();
AssignDefaultActions(ref scene);
TouchSimulation.Enable();
try
{
yield return null;
scene.leftChildReceiver.events.Clear();
InputSystem.QueueStateEvent(mouse, new MouseState
{
position = scene.From640x480ToScreen(123, 123)
}.WithButton(MouseButton.Left));
InputSystem.Update();
yield return null;
Assert.That(scene.uiModule.m_CurrentPointerType, Is.EqualTo(UIPointerType.Touch));
Assert.That(scene.uiModule.m_PointerIds.length, Is.EqualTo(1));
Assert.That(scene.uiModule.m_PointerTouchControls.length, Is.EqualTo(1));
Assert.That(scene.uiModule.m_PointerTouchControls[0], Is.SameAs(Touchscreen.current.touches[0]));
Assert.That(scene.leftChildReceiver.events, Has.Count.EqualTo(3));
Assert.That(scene.leftChildReceiver.events[0].type, Is.EqualTo(EventType.PointerEnter));
Assert.That(scene.leftChildReceiver.events[0].pointerData.pointerType, Is.EqualTo(UIPointerType.Touch));
Assert.That(scene.leftChildReceiver.events[0].pointerData.touchId, Is.EqualTo(1));
Assert.That(scene.leftChildReceiver.events[1].type, Is.EqualTo(EventType.PointerDown));
Assert.That(scene.leftChildReceiver.events[1].pointerData.pointerType, Is.EqualTo(UIPointerType.Touch));
Assert.That(scene.leftChildReceiver.events[1].pointerData.touchId, Is.EqualTo(1));
Assert.That(scene.leftChildReceiver.events[2].type, Is.EqualTo(EventType.InitializePotentialDrag));
Assert.That(scene.leftChildReceiver.events[2].pointerData.pointerType, Is.EqualTo(UIPointerType.Touch));
Assert.That(scene.leftChildReceiver.events[2].pointerData.touchId, Is.EqualTo(1));
// Release the mouse button so the touch ends. TouchSimulation.Disable() will remove
// the touchscreen and thus cancel ongoing actions (like Point). This should not result
// in exceptions from the input module trying to read data from the already removed touchscreen.
Release(mouse.leftButton);
yield return null;
}
finally
{
TouchSimulation.Disable();
}
}
#if UNITY_IOS || UNITY_TVOS
[Ignore("Failing on iOS https://jira.unity3d.com/browse/ISX-448")]
#endif
[UnityTest]
[Category("UI")]
public IEnumerator UI_CanDriveUIFromMultipleTrackedDevices()
@ -1417,7 +1550,7 @@ internal class UITests : InputTestFixture
var trackedDevice1 = (TrackedDevice)InputSystem.AddDevice("TrackedDeviceWithButton");
var trackedDevice2 = (TrackedDevice)InputSystem.AddDevice("TrackedDeviceWithButton");
var scene = CreateScene();
var scene = CreateUIAndPlayer();
var asset = ScriptableObject.CreateInstance<InputActionAsset>();
var map = new InputActionMap("map");
@ -1577,7 +1710,7 @@ internal class UITests : InputTestFixture
var mouse = InputSystem.AddDevice<Mouse>();
var keyboard = InputSystem.AddDevice<Keyboard>();
var scene = CreateScene();
var scene = CreateUIAndPlayer();
var actions = ScriptableObject.CreateInstance<InputActionAsset>();
var uiActions = actions.AddActionMap("UI");
@ -1593,7 +1726,7 @@ internal class UITests : InputTestFixture
yield return null;
// Move mouse over right object.
Set(mouse.position, new Vector2(350, 200));
Set(mouse.position, scene.From640x480ToScreen(350, 200));
scene.eventSystem.InvokeUpdate();
scene.rightChildReceiver.events.Clear();
@ -1623,7 +1756,7 @@ internal class UITests : InputTestFixture
{
var mouse = InputSystem.AddDevice<Mouse>();
var scene = CreateScene();
var scene = CreateUIAndPlayer();
var actions = ScriptableObject.CreateInstance<InputActionAsset>();
var uiActions = actions.AddActionMap("UI");
@ -1647,11 +1780,11 @@ internal class UITests : InputTestFixture
//
// NOTE: We also need to do this because we can't use Retry(2) -- as we normally do to warm up the JIT -- in combination
// with UnityTest. Doing so will flat out lead to InvalidCastExceptions in the test runner :(
Set(mouse.position, new Vector2(100, 100));
Set(mouse.position, scene.From640x480ToScreen(100, 100));
scene.eventSystem.InvokeUpdate();
Press(mouse.leftButton);
scene.eventSystem.InvokeUpdate();
Set(mouse.position, new Vector2(200, 200));
Set(mouse.position, scene.From640x480ToScreen(200, 200));
scene.eventSystem.InvokeUpdate();
Release(mouse.leftButton);
scene.eventSystem.InvokeUpdate();
@ -1662,21 +1795,21 @@ internal class UITests : InputTestFixture
Assert.That(() =>
{
Profiler.BeginSample(kProfilerRegion);
Set(mouse.position, new Vector2(100, 100));
Set(mouse.position, scene.From640x480ToScreen(100, 100));
scene.eventSystem.InvokeUpdate();
Press(mouse.leftButton);
scene.eventSystem.InvokeUpdate();
Set(mouse.position, new Vector2(200, 200));
Set(mouse.position, scene.From640x480ToScreen(200, 200));
scene.eventSystem.InvokeUpdate();
Release(mouse.leftButton);
scene.eventSystem.InvokeUpdate();
// And just for kicks, do it the opposite way, too.
Set(mouse.position, new Vector2(200, 200));
Set(mouse.position, scene.From640x480ToScreen(200, 200));
scene.eventSystem.InvokeUpdate();
Press(mouse.leftButton);
scene.eventSystem.InvokeUpdate();
Set(mouse.position, new Vector2(100, 100));
Set(mouse.position, scene.From640x480ToScreen(100, 100));
scene.eventSystem.InvokeUpdate();
Release(mouse.leftButton);
scene.eventSystem.InvokeUpdate();
@ -1692,7 +1825,13 @@ internal class UITests : InputTestFixture
{
var mouse = InputSystem.AddDevice<Mouse>();
var players = new[] { CreateScene(0, 240), CreateScene(240, 480) };
// Create two players each with their own UI scene and split
// it vertically across the screen.
var players = new[]
{
CreateUIAndPlayer(new Rect(0, 0, 1, 0.5f), namePrefix: "Player1 "),
CreateUIAndPlayer(new Rect(0, 0.5f, 1, 0.5f), namePrefix: "Player2 ")
};
// Create asset
var asset = ScriptableObject.CreateInstance<InputActionAsset>();
@ -1731,9 +1870,9 @@ internal class UITests : InputTestFixture
// We need to wait a frame to let the underlying canvas update and properly order the graphics images for raycasting.
yield return null;
// Click left gameObject of player 0
InputSystem.QueueStateEvent(mouse, new MouseState { position = new Vector2(100, 100), buttons = 1 << (int)MouseButton.Left });
InputSystem.QueueStateEvent(mouse, new MouseState { position = new Vector2(100, 100), buttons = 0 });
// Click left gameObject of player 0.
InputSystem.QueueStateEvent(mouse, new MouseState { position = players[0].From640x480ToScreen(100, 100), buttons = 1 << (int)MouseButton.Left });
InputSystem.QueueStateEvent(mouse, new MouseState { position = players[0].From640x480ToScreen(100, 100), buttons = 0 });
InputSystem.Update();
foreach (var player in players)
@ -1742,9 +1881,9 @@ internal class UITests : InputTestFixture
Assert.That(players[0].eventSystem.currentSelectedGameObject, Is.SameAs(players[0].leftGameObject));
Assert.That(players[1].eventSystem.currentSelectedGameObject, Is.Null);
// Click right gameObject of player 1
InputSystem.QueueStateEvent(mouse, new MouseState { position = new Vector2(400, 300), buttons = 1 << (int)MouseButton.Left });
InputSystem.QueueStateEvent(mouse, new MouseState { position = new Vector2(400, 300), buttons = 0 });
// Click right gameObject of player 1.
InputSystem.QueueStateEvent(mouse, new MouseState { position = players[1].From640x480ToScreen(400, 100), buttons = 1 << (int)MouseButton.Left });
InputSystem.QueueStateEvent(mouse, new MouseState { position = players[1].From640x480ToScreen(400, 100), buttons = 0 });
InputSystem.Update();
@ -1754,9 +1893,9 @@ internal class UITests : InputTestFixture
Assert.That(players[0].eventSystem.currentSelectedGameObject, Is.SameAs(players[0].leftGameObject));
Assert.That(players[1].eventSystem.currentSelectedGameObject, Is.SameAs(players[1].rightGameObject));
// Click right gameObject of player 0
InputSystem.QueueStateEvent(mouse, new MouseState { position = new Vector2(400, 100), buttons = 1 << (int)MouseButton.Left });
InputSystem.QueueStateEvent(mouse, new MouseState { position = new Vector2(400, 100), buttons = 0 });
// Click right gameObject of player 0.
InputSystem.QueueStateEvent(mouse, new MouseState { position = players[0].From640x480ToScreen(400, 100), buttons = 1 << (int)MouseButton.Left });
InputSystem.QueueStateEvent(mouse, new MouseState { position = players[0].From640x480ToScreen(400, 100), buttons = 0 });
InputSystem.Update();
foreach (var player in players)
@ -1774,7 +1913,7 @@ internal class UITests : InputTestFixture
{
// Create devices.
var gamepads = new[] { InputSystem.AddDevice<Gamepad>(), InputSystem.AddDevice<Gamepad>() };
var players = new[] { CreateScene(0, 240), CreateScene(240, 480) };
var players = new[] { CreateUIAndPlayer(new Rect(0, 0, 0.5f, 1)), CreateUIAndPlayer(new Rect(0.5f, 0, 0.5f, 1)) };
for (var i = 0; i < 2; i++)
{
@ -1889,7 +2028,7 @@ internal class UITests : InputTestFixture
InputSystem.settings.defaultDeadzoneMax = 1;
InputSystem.settings.defaultDeadzoneMin = 0;
var scene = CreateScene();
var scene = CreateUIAndPlayer();
var asset = ScriptableObject.CreateInstance<InputActionAsset>();
var map = asset.AddActionMap("map");
@ -2079,7 +2218,7 @@ internal class UITests : InputTestFixture
{
var mouse = InputSystem.AddDevice<Mouse>();
var scene = CreateScene();
var scene = CreateUIAndPlayer();
var asset = ScriptableObject.CreateInstance<InputActionAsset>();
var map = asset.AddActionMap("map");
@ -2094,7 +2233,7 @@ internal class UITests : InputTestFixture
yield return null;
// Put mouse over left object.
Set(mouse.position, new Vector2(100, 100));
Set(mouse.position, scene.From640x480ToScreen(100, 100));
scene.eventSystem.InvokeUpdate();
Assert.That(scene.leftChildReceiver.events, Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerEnter));
@ -2109,6 +2248,7 @@ internal class UITests : InputTestFixture
// Remove mouse. Should result in pointer-exit event.
InputSystem.RemoveDevice(mouse);
yield return null;
Assert.That(scene.leftChildReceiver.events,
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerExit).And
@ -2121,11 +2261,10 @@ internal class UITests : InputTestFixture
public IEnumerator TODO_UI_WhenEnabled_InitialPointerPositionIsPickedUp()
{
var mouse = InputSystem.AddDevice<Mouse>();
var scene = CreateUIAndPlayer();
// Put mouse over left object.
Set(mouse.position, new Vector2(100, 100));
var scene = CreateScene();
Set(mouse.position, scene.From640x480ToScreen(100, 100));
var asset = ScriptableObject.CreateInstance<InputActionAsset>();
var map = asset.AddActionMap("map");
@ -2235,7 +2374,10 @@ internal class UITests : InputTestFixture
actions.Enable();
var scene = CreateScene(0, 200);
var scene = CreateUIAndPlayer();
// Resize parent to span only half the screen.
((RectTransform)scene.parentGameObject.transform).anchorMax = new Vector2(0.5f, 1);
scene.uiModule.actionsAsset = actions;
scene.uiModule.point = InputActionReference.Create(pointAction);
@ -2249,14 +2391,14 @@ internal class UITests : InputTestFixture
yield return null;
// Click on left GO and make sure it gets selected.
Set(mouse.position, new Vector2(10, 10));
Set(mouse.position, scene.From640x480ToScreen(10, 10));
PressAndRelease(mouse.leftButton);
scene.eventSystem.InvokeUpdate();
Assert.That(scene.eventSystem.currentSelectedGameObject, Is.SameAs(scene.leftGameObject));
// Click outside of GOs and make sure the selection gets cleared.
Set(mouse.position, new Vector2(50, 250));
// Click on empty right side and make sure the selection gets cleared.
Set(mouse.position, scene.From640x480ToScreen(400, 10));
PressAndRelease(mouse.leftButton);
scene.eventSystem.InvokeUpdate();
@ -2265,14 +2407,14 @@ internal class UITests : InputTestFixture
scene.uiModule.deselectOnBackgroundClick = false;
// Click on left GO and make sure it gets selected.
Set(mouse.position, new Vector2(10, 10));
Set(mouse.position, scene.From640x480ToScreen(10, 10));
PressAndRelease(mouse.leftButton);
scene.eventSystem.InvokeUpdate();
Assert.That(scene.eventSystem.currentSelectedGameObject, Is.SameAs(scene.leftGameObject));
// Click outside of GOs and make sure our selection does NOT get cleared.
Set(mouse.position, new Vector2(50, 250));
// Click on empty right side and make sure our selection does NOT get cleared.
Set(mouse.position, scene.From640x480ToScreen(400, 10));
PressAndRelease(mouse.leftButton);
scene.eventSystem.InvokeUpdate();
@ -2300,38 +2442,38 @@ internal class UITests : InputTestFixture
actions.Enable();
var testObjects = CreateScene(0, 200);
var scene = CreateUIAndPlayer();
testObjects.uiModule.actionsAsset = actions;
testObjects.uiModule.point = InputActionReference.Create(pointAction);
testObjects.uiModule.leftClick = InputActionReference.Create(clickAction);
testObjects.uiModule.move = InputActionReference.Create(navigateAction);
scene.uiModule.actionsAsset = actions;
scene.uiModule.point = InputActionReference.Create(pointAction);
scene.uiModule.leftClick = InputActionReference.Create(clickAction);
scene.uiModule.move = InputActionReference.Create(navigateAction);
// Give canvas a chance to set itself up.
yield return null;
// Select left GO.
Set(mouse.position, new Vector2(10, 10));
Set(mouse.position, scene.From640x480ToScreen(10, 10));
Press(mouse.leftButton);
yield return null;
Release(mouse.leftButton);
Assert.That(testObjects.eventSystem.currentSelectedGameObject, Is.SameAs(testObjects.leftGameObject));
Assert.That(scene.eventSystem.currentSelectedGameObject, Is.SameAs(scene.leftGameObject));
// Click on background and make sure we deselect.
Set(mouse.position, new Vector2(50, 250));
Set(mouse.position, scene.From640x480ToScreen(50, 250));
Press(mouse.leftButton);
yield return null;
Release(mouse.leftButton);
Assert.That(testObjects.eventSystem.currentSelectedGameObject, Is.Null);
Assert.That(scene.eventSystem.currentSelectedGameObject, Is.Null);
// Now perform a navigate-right action. Given we have no current selection, this should
// cause the right GO to be selected based on the fact that the left GO was selected last.
Press(gamepad.dpad.right);
yield return null;
Assert.That(testObjects.eventSystem.currentSelectedGameObject, Is.SameAs(testObjects.rightGameObject));
Assert.That(scene.eventSystem.currentSelectedGameObject, Is.SameAs(scene.rightGameObject));
// Just to make extra sure, navigate left and make sure that results in the expected selection
// change over to the left GO.
@ -2339,7 +2481,7 @@ internal class UITests : InputTestFixture
Press(gamepad.dpad.left);
yield return null;
Assert.That(testObjects.eventSystem.currentSelectedGameObject, Is.SameAs(testObjects.leftGameObject));
Assert.That(scene.eventSystem.currentSelectedGameObject, Is.SameAs(scene.leftGameObject));
}
private const string kTrackedDeviceWithButton = @"

Просмотреть файл

@ -15,6 +15,9 @@ however, it has to be formatted properly to pass verification tests.
* This allows for certain optimizations.
* Should the limits prove too tight, they can be raised in the future.
* The most complex device we have at the moment (`Touchscreen`) has 242 controls and 616 bytes of state.
- `TouchSimulation` now __disables__ the `Pointer` devices it reads input from.
* This is to address the problem of mouse input leading to __both__ mouse and touch input happening concurrently. Instead, enabling touch simulation will now effectively __replace__ mouse and pen input with touch input.
* Devices such `Mouse` and `Pen` will remain in place but will not get updated. Events received for them will be consumed by `TouchSimulation`.
- Enabled XR device support on Switch.
### Fixed
@ -24,6 +27,10 @@ however, it has to be formatted properly to pass verification tests.
- Fixed `InputUser.OnEvent` and `RebindingOperation.OnEvent` exhibiting bad performance profiles and leading to multi-millisecond input update times (case 1253371).
* In our own measurements, `InputUser.OnEvent` is >9 times faster than before and `RebindingOperation.OnEvent` is ~2.5 times faster.
- Fixed PS4 controller not recognized on Mac when connected over Bluetooth ([case 1286449](https://issuetracker.unity3d.com/issues/input-system-dualshock-4-zct1e-dualshock-2-v1-devices-are-not-fully-recognised-over-bluetooth)).
- Fixed `EnhancedTouch` leaking `NativeArray` memory on domain reloads ([case 1190150](https://issuetracker.unity3d.com/issues/new-input-system-simulated-touch-in-editor-doesnt-work)).
- Fixed `TouchSimulation` leading to `"Pointer should have exited all objects before being removed"` errors ([case 1190150](https://issuetracker.unity3d.com/issues/new-input-system-simulated-touch-in-editor-doesnt-work)).
- Fixed multi-touch not working with `InputSystemUIInputModule` ([case 1271942](https://issuetracker.unity3d.com/issues/android-onenddrag-not-being-called-when-there-are-at-least-2-touches-on-the-screen)).
* This also manifested itself when using On-Screen Controls and not being able to use multiple controls at the same time (for example, in the [Warriors demo](https://github.com/UnityTechnologies/InputSystem_Warriors)).
- Fixed restart prompt after package installation not appearing on Unity 2020.2+ ([case 1292513](https://issuetracker.unity3d.com/issues/input-system-after-package-install-the-update-slash-switch-and-restart-prompt-does-not-appear)).
- Fixed action with multiple bindings getting stuck in `Performed` state when two or more controls are pressed at the same time ([case 1295535](https://issuetracker.unity3d.com/issues/input-system-not-registering-multiple-inputs)).
* Regression introduced in 1.1-preview.2.

Просмотреть файл

@ -8,7 +8,7 @@ Unlike other devices, sensors are disabled by default. To enable a sensor, call
InputSystem.EnableDevice(Gyroscope.current);
```
To disable a sensor, call [`InputSystem.DisableDevice()`](../api/UnityEngine.InputSystem.InputSystem.html#UnityEngine_InputSystem_InputSystem_DisableDevice_UnityEngine_InputSystem_InputDevice_).
To disable a sensor, call [`InputSystem.DisableDevice()`](../api/UnityEngine.InputSystem.InputSystem.html#UnityEngine_InputSystem_InputSystem_DisableDevice_UnityEngine_InputSystem_InputDevice_System_Boolean_).
```
InputSystem.DisableDevice(Gyroscope.current);

Просмотреть файл

@ -26,16 +26,16 @@ You can use the following properties to map Actions from the chosen [__Actions A
|Property|Description|
|--------|-----------|
|[`Point`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_point)|An Action that delivers a 2D screen position. Use as a cursor for pointing at UI elements to implement mouse-style UI interactions. See [pointer-type input](#pointer-type-input).|
|[`Left Click`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_leftClick)|An Action that maps to the primary cursor button used to interact with UI. See [pointer-type input](#pointer-type-input).|
|[`Middle Click`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_middleClick)|An Action that maps to the middle cursor button used to interact with UI. See [pointer-type input](#pointer-type-input).|
|[`Right Click`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_rightClick)|An Action that maps to the secondary cursor button used to interact with UI. See [pointer-type input](#pointer-type-input).|
|[`Scroll Wheel`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_scrollWheel)|An Action that delivers gesture input to allow scrolling in the UI. See [pointer-type input](#pointer-type-input).|
|[`Move`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_move)|An Action that delivers a 2D vector used to select the currently active UI [selectable](https://docs.unity3d.com/Manual/script-Selectable.html). This allows a gamepad or arrow-key style navigation of the UI. See [navigation-type input](#navigation-type-input).|
|[`Submit`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_submit)|An Action to engage with or "click" the currently selected UI [selectable](https://docs.unity3d.com/Manual/script-Selectable.html). See [navigation-type input](#navigation-type-input).|
|[`Cancel`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_cancel)|An Action to exit any interaction with the currently selected UI [selectable](https://docs.unity3d.com/Manual/script-Selectable.html). See [navigation-type input](#navigation-type-input).|
|[`Tracked Device Position`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_trackedDevicePosition)|An Action that delivers a 3D position of one or multiple spatial tracking devices, such as XR hand controllers. In combination with [`Tracked Device Orientation`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_trackedDeviceOrientation), this allows XR-style UI interactions by pointing at UI [selectables](https://docs.unity3d.com/Manual/script-Selectable.html) in space. See [tracked-type input](#tracked-type-input).|
|[`Tracked Device Orientation`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_trackedDeviceOrientation)|An Action that delivers a `Quaternion` representing the rotation of one or multiple spatial tracking devices, such as XR hand controllers. In combination with [`Tracked Device Position`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_trackedDevicePosition), this allows XR-style UI interactions by pointing at UI [selectables](https://docs.unity3d.com/Manual/script-Selectable.html) in space. See [tracked-type input](#tracked-type-input).|
|[`Point`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_point)|An Action that delivers a 2D screen position. Use as a cursor for pointing at UI elements to implement mouse-style UI interactions. See [pointer-type input](#pointer-type-input).<br><br>Set to [`PassThrough`](../api/UnityEngine.InputSystem.InputActionType.html#UnityEngine_InputSystem_InputActionType_PassThrough) Action type and `Vector2` value type.|
|[`Left Click`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_leftClick)|An Action that maps to the primary cursor button used to interact with UI. See [pointer-type input](#pointer-type-input).<br><br>Set to [`PassThrough`](../api/UnityEngine.InputSystem.InputActionType.html#UnityEngine_InputSystem_InputActionType_PassThrough) Action type and `Button` value type.|
|[`Middle Click`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_middleClick)|An Action that maps to the middle cursor button used to interact with UI. See [pointer-type input](#pointer-type-input).<br><br>Set to [`PassThrough`](../api/UnityEngine.InputSystem.InputActionType.html#UnityEngine_InputSystem_InputActionType_PassThrough) Action type and `Button` value type.|
|[`Right Click`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_rightClick)|An Action that maps to the secondary cursor button used to interact with UI. See [pointer-type input](#pointer-type-input).<br><br>Set to [`PassThrough`](../api/UnityEngine.InputSystem.InputActionType.html#UnityEngine_InputSystem_InputActionType_PassThrough) Action type and `Button` value type.|
|[`Scroll Wheel`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_scrollWheel)|An Action that delivers gesture input to allow scrolling in the UI. See [pointer-type input](#pointer-type-input).<br><br>Set to [`PassThrough`](../api/UnityEngine.InputSystem.InputActionType.html#UnityEngine_InputSystem_InputActionType_PassThrough) Action type and `Vector2` value type.|
|[`Move`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_move)|An Action that delivers a 2D vector used to select the currently active UI [selectable](https://docs.unity3d.com/Manual/script-Selectable.html). This allows a gamepad or arrow-key style navigation of the UI. See [navigation-type input](#navigation-type-input).<br><br>Set to [`PassThrough`](../api/UnityEngine.InputSystem.InputActionType.html#UnityEngine_InputSystem_InputActionType_PassThrough) Action type and `Vector2` value type.|
|[`Submit`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_submit)|An Action to engage with or "click" the currently selected UI [selectable](https://docs.unity3d.com/Manual/script-Selectable.html). See [navigation-type input](#navigation-type-input).<br><br>Set to [`Button`](../api/UnityEngine.InputSystem.InputActionType.html#UnityEngine_InputSystem_InputActionType_Button) Action type.|
|[`Cancel`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_cancel)|An Action to exit any interaction with the currently selected UI [selectable](https://docs.unity3d.com/Manual/script-Selectable.html). See [navigation-type input](#navigation-type-input).<br><br>Set to [`Button`](../api/UnityEngine.InputSystem.InputActionType.html#UnityEngine_InputSystem_InputActionType_Button) Action type.|
|[`Tracked Device Position`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_trackedDevicePosition)|An Action that delivers a 3D position of one or multiple spatial tracking devices, such as XR hand controllers. In combination with [`Tracked Device Orientation`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_trackedDeviceOrientation), this allows XR-style UI interactions by pointing at UI [selectables](https://docs.unity3d.com/Manual/script-Selectable.html) in space. See [tracked-type input](#tracked-type-input).<br><br>Set to [`PassThrough`](../api/UnityEngine.InputSystem.InputActionType.html#UnityEngine_InputSystem_InputActionType_PassThrough) Action type and `Vector3` value type.|
|[`Tracked Device Orientation`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_trackedDeviceOrientation)|An Action that delivers a `Quaternion` representing the rotation of one or multiple spatial tracking devices, such as XR hand controllers. In combination with [`Tracked Device Position`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_trackedDevicePosition), this allows XR-style UI interactions by pointing at UI [selectables](https://docs.unity3d.com/Manual/script-Selectable.html) in space. See [tracked-type input](#tracked-type-input).<br><br>Set to [`PassThrough`](../api/UnityEngine.InputSystem.InputActionType.html#UnityEngine_InputSystem_InputActionType_PassThrough) Action type and `Quaternion` value type.|
### How the bindings work
@ -53,7 +53,9 @@ To the UI, a pointer is a position from which clicks and scrolls can be triggere
>NOTE: The UI input module does not have an association between pointers and cursors. In general, the UI is oblivious to whether a cursor exists for a particular pointer. However, for mouse and pen input, the UI input module will respect [`Cusor.lockState`](https://docs.unity3d.com/ScriptReference/Cursor-lockState.html) and pin the pointer position at `(-1,-1)` whenever the cursor is locked.
Multiple pointer devices may feed input into a single UI input module. Also, in the case of [`Touchscreen`](../api/UnityEngine.InputSystem.Touchscreen.html), a single device can have the ability to have multiple concurrent pointers (each finger contact is one pointer).
Multiple pointer Devices may feed input into a single UI input module. Also, in the case of [`Touchscreen`](../api/UnityEngine.InputSystem.Touchscreen.html), a single Device can have the ability to have multiple concurrent pointers (each finger contact is one pointer).
>IMPORTANT: Because multiple pointer Devices can feed into the same set of Actions, it is important to set the [action type](./Actions.md#action-types) to [`PassThrough`](../api/UnityEngine.InputSystem.InputActionType.html#UnityEngine_InputSystem_InputActionType_PassThrough). This ensures that no filtering is applied to input on these actions and that instead every input is relayed as is.
From the perspective of [`InputSystemUIInputModule`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html), each [`InputDevice`](../api/UnityEngine.InputSystem.InputDevice.html) that has one or more controls bound to one of the pointer-type actions is considered a unique pointer. Also, for each [`Touchscreen`](../api/UnityEngine.InputSystem.Touchscreen.html) devices, each separate [`TouchControl`](../api/UnityEngine.InputSystem.Controls.TouchControl.html) that has one or more of its controls bound to the those actions is considered its own unique pointer as well. Each pointer receives a unique [`pointerId`](https://docs.unity3d.com/Packages/com.unity.ugui@1.0/api/UnityEngine.EventSystems.PointerEventData.html#UnityEngine_EventSystems_PointerEventData_pointerId) which generally corresponds to the [`deviceId`](../api/UnityEngine.InputSystem.InputDevice.html#UnityEngine_InputSystem_InputDevice_deviceId) of the pointer. However, for touch, this will be a combination of [`deviceId`](../api/UnityEngine.InputSystem.InputDevice.html#UnityEngine_InputSystem_InputDevice_deviceId) and [`touchId`](../api/UnityEngine.InputSystem.Controls.TouchControl.html#UnityEngine_InputSystem_Controls_TouchControl_touchId). Use [`ExtendedPointerEventData.touchId`](../api/UnityEngine.InputSystem.UI.ExtendedPointerEventData.html#UnityEngine_InputSystem_UI_ExtendedPointerEventData_touchId) to find the ID for a touch event.
@ -73,9 +75,12 @@ For pointer-type input (as well as for [tracked-type input](#tracked-type input)
Navigation-type input controls the current selection based on motion read from the [`move`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_move) action. Additionally, input from
[`submit`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_submit) will trigger `ISubmitHandler` on the currently selected object and
[`Cancel`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_cancel) will trigger `ICancelHandler` on it.
[`cancel`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_cancel) will trigger `ICancelHandler` on it.
Inputs received on these actions are independent of each other. Thus, an arbitrary set of devices can be bound to these actions and feed input in arbitrary order.
Unlike with [pointer-type](#pointer-type input), where multiple pointer inputs may exist concurrently (think two touches or left- and right-hand tracked input), navigation-type input does not have multiple concurrent instances. In other words, only a single [`move`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_move) vector and a single [`submit`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_submit) and [`cancel`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_cancel) input will be processed by the UI module each frame. However, these inputs need not necessarily come from one single Device always. Arbitrary many inputs can be bound to the respective actions.
>IMPORTANT: While, [`move`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_move) should be set to [`PassThrough`](../api/UnityEngine.InputSystem.InputActionType.html#UnityEngine_InputSystem_InputActionType_PassThrough) Action type, it is important that [`submit`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_submit) and
[`cancel`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_cancel) be set to the [`Button`](../api/UnityEngine.InputSystem.InputActionType.html#UnityEngine_InputSystem_InputActionType_Button) Action type.
Navigation input is non-positional, that is, unlike with pointer-type input, there is no screen position associcated with these actions. Rather, navigation actions always operate on the current selection.
@ -83,6 +88,8 @@ Navigation input is non-positional, that is, unlike with pointer-type input, the
Input from [tracked devices](../api/UnityEngine.InputSystem.TrackedDevice.html) such as [XR controllers](../api/UnityEngine.InputSystem.XR.XRController.html) and [HMDs](../api/UnityEngine.InputSystem.XR.XRHMD.html) essentially behaves like [pointer-type input](#pointer-type-input). The main difference is that the world-space device position and orientation sourced from [`trackedDevicePosition`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_trackedDevicePosition) and [`trackedDeviceOrientation`](../api/UnityEngine.InputSystem.UI.InputSystemUIInputModule.html#UnityEngine_InputSystem_UI_InputSystemUIInputModule_trackedDeviceOrientation) is translated into a screen-space position via raycasting.
>IMPORTANT: Because multiple tracked Devices can feed into the same set of Actions, it is important to set the [action type](./Actions.md#action-types) to [`PassThrough`](../api/UnityEngine.InputSystem.InputActionType.html#UnityEngine_InputSystem_InputActionType_PassThrough). This ensures that no filtering is applied to input on these actions and that instead every input is relayed as is.
For this raycasting to work, you need to add [`TrackedDeviceRaycaster`](../api/UnityEngine.InputSystem.UI.TrackedDeviceRaycaster.html) to the `GameObject` that has the UI's `Canvas` component. This `GameObject` will usually have a `GraphicRaycaster` component which, however, only works for 2D screen-space raycasting. You can put [`TrackedDeviceRaycaster`](../api/UnityEngine.InputSystem.UI.TrackedDeviceRaycaster.html) alongside `GraphicRaycaster` and both can be enabled at the same time without advserse effect.
![TrackedDeviceRayster Add Component](Images/TrackedDeviceRaycasterComponentMenu.png)

Просмотреть файл

@ -1023,7 +1023,7 @@ namespace UnityEngine.InputSystem
// Check actuation level.
var actuation = ComputeMagnitude(ref trigger);
var actionState = &actionStates[actionIndex];
var pressPoint = controls[controlIndex] is ButtonControl button ? button.pressPointOrDefault : ButtonControl.s_GlobalDefaultButtonPressPoint;
var pressPoint = controls[trigger.controlIndex] is ButtonControl button ? button.pressPointOrDefault : ButtonControl.s_GlobalDefaultButtonPressPoint;
if (!actionState->isPressed && actuation >= pressPoint)
{
actionState->pressedInUpdate = InputUpdate.s_UpdateStepCount;

Просмотреть файл

@ -786,9 +786,9 @@ namespace UnityEngine.InputSystem
protected internal InputStateBlock m_StateBlock;
////REVIEW: shouldn't these sit on the device?
protected internal unsafe void* currentStatePtr => InputStateBuffers.GetFrontBufferForDevice(ResolveDeviceIndex());
protected internal unsafe void* currentStatePtr => InputStateBuffers.GetFrontBufferForDevice(GetDeviceIndex());
protected internal unsafe void* previousFrameStatePtr => InputStateBuffers.GetBackBufferForDevice(ResolveDeviceIndex());
protected internal unsafe void* previousFrameStatePtr => InputStateBuffers.GetBackBufferForDevice(GetDeviceIndex());
protected internal unsafe void* defaultStatePtr => InputStateBuffers.s_DefaultStateBuffer;
@ -857,6 +857,7 @@ namespace UnityEngine.InputSystem
ConfigUpToDate = 1 << 0,
IsNoisy = 1 << 1,
IsSynthetic = 1 << 2,
IsButton = 1 << 3,
SetupFinished = 1 << 5, // Can't be modified once this is set.
UsesStateFromOtherControl = 1 << 6,
}
@ -873,6 +874,18 @@ namespace UnityEngine.InputSystem
}
}
internal bool isButton
{
get => (m_ControlFlags & ControlFlags.IsButton) == ControlFlags.IsButton;
set
{
if (value)
m_ControlFlags |= ControlFlags.IsButton;
else
m_ControlFlags &= ~ControlFlags.IsButton;
}
}
internal bool isConfigUpToDate
{
get => (m_ControlFlags & ControlFlags.ConfigUpToDate) == ControlFlags.ConfigUpToDate;
@ -925,7 +938,7 @@ namespace UnityEngine.InputSystem
list[i].BakeOffsetIntoStateBlockRecursive(offset);
}
internal int ResolveDeviceIndex()
internal int GetDeviceIndex()
{
var deviceIndex = m_Device.m_DeviceIndex;
if (deviceIndex == InputDevice.kInvalidDeviceIndex)

Просмотреть файл

@ -1655,6 +1655,13 @@ namespace UnityEngine.InputSystem
return this;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ControlBuilder IsButton(bool value)
{
control.isButton = value;
return this;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Finish()
{

Просмотреть файл

@ -497,7 +497,7 @@ namespace UnityEngine.InputSystem
var device = control.device;
var deviceIndex = device.m_DeviceIndex;
var controlIndex = !ReferenceEquals(device, control)
? ArrayHelpers.IndexOfReference(device.m_ChildrenForEachControl, control) + 1
? device.m_ChildrenForEachControl.IndexOfReference<InputControl, InputControl>(control) + 1
: 0;
// There is a known documented bug with the new Rosyln

Просмотреть файл

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.Utilities;
@ -393,6 +394,7 @@ namespace UnityEngine.InputSystem.Layouts
control.synthetic = controlItem.isSynthetic;
if (control.noisy)
m_Device.noisy = true;
control.isButton = control is ButtonControl;
// Remember the display names from the layout. We later do a proper pass once we have
// the full hierarchy to set final names.

Просмотреть файл

@ -555,6 +555,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Any Key")
.WithLayout(kAnyKeyLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -577,6 +578,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Escape")
.WithLayout(kKeyLayout)
.WithUsages(0, 2)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -599,6 +601,7 @@ namespace UnityEngine.InputSystem
.WithName("space")
.WithDisplayName("Space")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -622,6 +625,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Enter")
.WithLayout(kKeyLayout)
.WithUsages(2, 1)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -644,6 +648,7 @@ namespace UnityEngine.InputSystem
.WithName("tab")
.WithDisplayName("Tab")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -666,6 +671,7 @@ namespace UnityEngine.InputSystem
.WithName("backquote")
.WithDisplayName("`")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -688,6 +694,7 @@ namespace UnityEngine.InputSystem
.WithName("quote")
.WithDisplayName("'")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -710,6 +717,7 @@ namespace UnityEngine.InputSystem
.WithName("semicolon")
.WithDisplayName(";")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -732,6 +740,7 @@ namespace UnityEngine.InputSystem
.WithName("comma")
.WithDisplayName(",")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -754,6 +763,7 @@ namespace UnityEngine.InputSystem
.WithName("period")
.WithDisplayName(".")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -776,6 +786,7 @@ namespace UnityEngine.InputSystem
.WithName("slash")
.WithDisplayName("/")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -798,6 +809,7 @@ namespace UnityEngine.InputSystem
.WithName("backslash")
.WithDisplayName("\\")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -820,6 +832,7 @@ namespace UnityEngine.InputSystem
.WithName("leftBracket")
.WithDisplayName("[")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -842,6 +855,7 @@ namespace UnityEngine.InputSystem
.WithName("rightBracket")
.WithDisplayName("]")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -864,6 +878,7 @@ namespace UnityEngine.InputSystem
.WithName("minus")
.WithDisplayName("-")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -886,6 +901,7 @@ namespace UnityEngine.InputSystem
.WithName("equals")
.WithDisplayName("=")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -908,6 +924,7 @@ namespace UnityEngine.InputSystem
.WithName("upArrow")
.WithDisplayName("Up Arrow")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -930,6 +947,7 @@ namespace UnityEngine.InputSystem
.WithName("downArrow")
.WithDisplayName("Down Arrow")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -952,6 +970,7 @@ namespace UnityEngine.InputSystem
.WithName("leftArrow")
.WithDisplayName("Left Arrow")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -974,6 +993,7 @@ namespace UnityEngine.InputSystem
.WithName("rightArrow")
.WithDisplayName("Right Arrow")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -996,6 +1016,7 @@ namespace UnityEngine.InputSystem
.WithName("a")
.WithDisplayName("A")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1018,6 +1039,7 @@ namespace UnityEngine.InputSystem
.WithName("b")
.WithDisplayName("B")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1040,6 +1062,7 @@ namespace UnityEngine.InputSystem
.WithName("c")
.WithDisplayName("C")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1062,6 +1085,7 @@ namespace UnityEngine.InputSystem
.WithName("d")
.WithDisplayName("D")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1084,6 +1108,7 @@ namespace UnityEngine.InputSystem
.WithName("e")
.WithDisplayName("E")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1106,6 +1131,7 @@ namespace UnityEngine.InputSystem
.WithName("f")
.WithDisplayName("F")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1128,6 +1154,7 @@ namespace UnityEngine.InputSystem
.WithName("g")
.WithDisplayName("G")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1150,6 +1177,7 @@ namespace UnityEngine.InputSystem
.WithName("h")
.WithDisplayName("H")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1172,6 +1200,7 @@ namespace UnityEngine.InputSystem
.WithName("i")
.WithDisplayName("I")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1194,6 +1223,7 @@ namespace UnityEngine.InputSystem
.WithName("j")
.WithDisplayName("J")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1216,6 +1246,7 @@ namespace UnityEngine.InputSystem
.WithName("k")
.WithDisplayName("K")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1238,6 +1269,7 @@ namespace UnityEngine.InputSystem
.WithName("l")
.WithDisplayName("L")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1260,6 +1292,7 @@ namespace UnityEngine.InputSystem
.WithName("m")
.WithDisplayName("M")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1282,6 +1315,7 @@ namespace UnityEngine.InputSystem
.WithName("n")
.WithDisplayName("N")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1304,6 +1338,7 @@ namespace UnityEngine.InputSystem
.WithName("o")
.WithDisplayName("O")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1326,6 +1361,7 @@ namespace UnityEngine.InputSystem
.WithName("p")
.WithDisplayName("P")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1348,6 +1384,7 @@ namespace UnityEngine.InputSystem
.WithName("q")
.WithDisplayName("Q")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1370,6 +1407,7 @@ namespace UnityEngine.InputSystem
.WithName("r")
.WithDisplayName("R")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1392,6 +1430,7 @@ namespace UnityEngine.InputSystem
.WithName("s")
.WithDisplayName("S")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1414,6 +1453,7 @@ namespace UnityEngine.InputSystem
.WithName("t")
.WithDisplayName("T")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1436,6 +1476,7 @@ namespace UnityEngine.InputSystem
.WithName("u")
.WithDisplayName("U")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1458,6 +1499,7 @@ namespace UnityEngine.InputSystem
.WithName("v")
.WithDisplayName("V")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1480,6 +1522,7 @@ namespace UnityEngine.InputSystem
.WithName("w")
.WithDisplayName("W")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1502,6 +1545,7 @@ namespace UnityEngine.InputSystem
.WithName("x")
.WithDisplayName("X")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1524,6 +1568,7 @@ namespace UnityEngine.InputSystem
.WithName("y")
.WithDisplayName("Y")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1546,6 +1591,7 @@ namespace UnityEngine.InputSystem
.WithName("z")
.WithDisplayName("Z")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1568,6 +1614,7 @@ namespace UnityEngine.InputSystem
.WithName("1")
.WithDisplayName("1")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1590,6 +1637,7 @@ namespace UnityEngine.InputSystem
.WithName("2")
.WithDisplayName("2")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1612,6 +1660,7 @@ namespace UnityEngine.InputSystem
.WithName("3")
.WithDisplayName("3")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1634,6 +1683,7 @@ namespace UnityEngine.InputSystem
.WithName("4")
.WithDisplayName("4")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1656,6 +1706,7 @@ namespace UnityEngine.InputSystem
.WithName("5")
.WithDisplayName("5")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1678,6 +1729,7 @@ namespace UnityEngine.InputSystem
.WithName("6")
.WithDisplayName("6")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1700,6 +1752,7 @@ namespace UnityEngine.InputSystem
.WithName("7")
.WithDisplayName("7")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1722,6 +1775,7 @@ namespace UnityEngine.InputSystem
.WithName("8")
.WithDisplayName("8")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1744,6 +1798,7 @@ namespace UnityEngine.InputSystem
.WithName("9")
.WithDisplayName("9")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1766,6 +1821,7 @@ namespace UnityEngine.InputSystem
.WithName("0")
.WithDisplayName("0")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1789,6 +1845,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Left Shift")
.WithLayout(kKeyLayout)
.WithUsages(3, 1)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1812,6 +1869,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Right Shift")
.WithLayout(kKeyLayout)
.WithUsages(4, 1)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1836,6 +1894,7 @@ namespace UnityEngine.InputSystem
.WithLayout(kDiscreteButtonLayout)
.WithUsages(5, 1)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1858,6 +1917,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Left Alt")
.WithLayout(kKeyLayout)
.WithUsages(6, 1)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1882,6 +1942,7 @@ namespace UnityEngine.InputSystem
.WithLayout(kKeyLayout)
.WithUsages(7, 1)
.WithAliases(0, 1)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1906,6 +1967,7 @@ namespace UnityEngine.InputSystem
.WithLayout(kDiscreteButtonLayout)
.WithUsages(8, 1)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1928,6 +1990,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Left Control")
.WithLayout(kKeyLayout)
.WithUsages(9, 1)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1951,6 +2014,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Right Control")
.WithLayout(kKeyLayout)
.WithUsages(10, 1)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1975,6 +2039,7 @@ namespace UnityEngine.InputSystem
.WithLayout(kDiscreteButtonLayout)
.WithUsages(11, 1)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1998,6 +2063,7 @@ namespace UnityEngine.InputSystem
.WithLayout(kKeyLayout)
.WithUsages(12, 1)
.WithAliases(1, 3)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2022,6 +2088,7 @@ namespace UnityEngine.InputSystem
.WithLayout(kKeyLayout)
.WithUsages(13, 1)
.WithAliases(4, 3)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2045,6 +2112,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Context Menu")
.WithLayout(kKeyLayout)
.WithUsages(14, 1)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2067,6 +2135,7 @@ namespace UnityEngine.InputSystem
.WithName("backspace")
.WithDisplayName("Backspace")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2089,6 +2158,7 @@ namespace UnityEngine.InputSystem
.WithName("pageDown")
.WithDisplayName("Page Down")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2111,6 +2181,7 @@ namespace UnityEngine.InputSystem
.WithName("pageUp")
.WithDisplayName("Page Up")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2133,6 +2204,7 @@ namespace UnityEngine.InputSystem
.WithName("home")
.WithDisplayName("Home")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2155,6 +2227,7 @@ namespace UnityEngine.InputSystem
.WithName("end")
.WithDisplayName("End")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2177,6 +2250,7 @@ namespace UnityEngine.InputSystem
.WithName("insert")
.WithDisplayName("Insert")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2199,6 +2273,7 @@ namespace UnityEngine.InputSystem
.WithName("delete")
.WithDisplayName("Delete")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2221,6 +2296,7 @@ namespace UnityEngine.InputSystem
.WithName("capsLock")
.WithDisplayName("Caps Lock")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2243,6 +2319,7 @@ namespace UnityEngine.InputSystem
.WithName("numLock")
.WithDisplayName("Num Lock")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2265,6 +2342,7 @@ namespace UnityEngine.InputSystem
.WithName("printScreen")
.WithDisplayName("Print Screen")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2287,6 +2365,7 @@ namespace UnityEngine.InputSystem
.WithName("scrollLock")
.WithDisplayName("Scroll Lock")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2309,6 +2388,7 @@ namespace UnityEngine.InputSystem
.WithName("pause")
.WithDisplayName("Pause/Break")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2331,6 +2411,7 @@ namespace UnityEngine.InputSystem
.WithName("numpadEnter")
.WithDisplayName("Numpad Enter")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2353,6 +2434,7 @@ namespace UnityEngine.InputSystem
.WithName("numpadDivide")
.WithDisplayName("Numpad /")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2375,6 +2457,7 @@ namespace UnityEngine.InputSystem
.WithName("numpadMultiply")
.WithDisplayName("Numpad *")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2397,6 +2480,7 @@ namespace UnityEngine.InputSystem
.WithName("numpadPlus")
.WithDisplayName("Numpad +")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2419,6 +2503,7 @@ namespace UnityEngine.InputSystem
.WithName("numpadMinus")
.WithDisplayName("Numpad -")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2441,6 +2526,7 @@ namespace UnityEngine.InputSystem
.WithName("numpadPeriod")
.WithDisplayName("Numpad .")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2463,6 +2549,7 @@ namespace UnityEngine.InputSystem
.WithName("numpadEquals")
.WithDisplayName("Numpad =")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2485,6 +2572,7 @@ namespace UnityEngine.InputSystem
.WithName("numpad1")
.WithDisplayName("Numpad 1")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2507,6 +2595,7 @@ namespace UnityEngine.InputSystem
.WithName("numpad2")
.WithDisplayName("Numpad 2")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2529,6 +2618,7 @@ namespace UnityEngine.InputSystem
.WithName("numpad3")
.WithDisplayName("Numpad 3")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2551,6 +2641,7 @@ namespace UnityEngine.InputSystem
.WithName("numpad4")
.WithDisplayName("Numpad 4")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2573,6 +2664,7 @@ namespace UnityEngine.InputSystem
.WithName("numpad5")
.WithDisplayName("Numpad 5")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2595,6 +2687,7 @@ namespace UnityEngine.InputSystem
.WithName("numpad6")
.WithDisplayName("Numpad 6")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2617,6 +2710,7 @@ namespace UnityEngine.InputSystem
.WithName("numpad7")
.WithDisplayName("Numpad 7")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2639,6 +2733,7 @@ namespace UnityEngine.InputSystem
.WithName("numpad8")
.WithDisplayName("Numpad 8")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2661,6 +2756,7 @@ namespace UnityEngine.InputSystem
.WithName("numpad9")
.WithDisplayName("Numpad 9")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2683,6 +2779,7 @@ namespace UnityEngine.InputSystem
.WithName("numpad0")
.WithDisplayName("Numpad 0")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2705,6 +2802,7 @@ namespace UnityEngine.InputSystem
.WithName("f1")
.WithDisplayName("F1")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2727,6 +2825,7 @@ namespace UnityEngine.InputSystem
.WithName("f2")
.WithDisplayName("F2")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2749,6 +2848,7 @@ namespace UnityEngine.InputSystem
.WithName("f3")
.WithDisplayName("F3")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2771,6 +2871,7 @@ namespace UnityEngine.InputSystem
.WithName("f4")
.WithDisplayName("F4")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2793,6 +2894,7 @@ namespace UnityEngine.InputSystem
.WithName("f5")
.WithDisplayName("F5")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2815,6 +2917,7 @@ namespace UnityEngine.InputSystem
.WithName("f6")
.WithDisplayName("F6")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2837,6 +2940,7 @@ namespace UnityEngine.InputSystem
.WithName("f7")
.WithDisplayName("F7")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2859,6 +2963,7 @@ namespace UnityEngine.InputSystem
.WithName("f8")
.WithDisplayName("F8")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2881,6 +2986,7 @@ namespace UnityEngine.InputSystem
.WithName("f9")
.WithDisplayName("F9")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2903,6 +3009,7 @@ namespace UnityEngine.InputSystem
.WithName("f10")
.WithDisplayName("F10")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2925,6 +3032,7 @@ namespace UnityEngine.InputSystem
.WithName("f11")
.WithDisplayName("F11")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2947,6 +3055,7 @@ namespace UnityEngine.InputSystem
.WithName("f12")
.WithDisplayName("F12")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2969,6 +3078,7 @@ namespace UnityEngine.InputSystem
.WithName("OEM1")
.WithDisplayName("OEM1")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2991,6 +3101,7 @@ namespace UnityEngine.InputSystem
.WithName("OEM2")
.WithDisplayName("OEM2")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -3013,6 +3124,7 @@ namespace UnityEngine.InputSystem
.WithName("OEM3")
.WithDisplayName("OEM3")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -3035,6 +3147,7 @@ namespace UnityEngine.InputSystem
.WithName("OEM4")
.WithDisplayName("OEM4")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -3057,6 +3170,7 @@ namespace UnityEngine.InputSystem
.WithName("OEM5")
.WithDisplayName("OEM5")
.WithLayout(kKeyLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -3080,6 +3194,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("IMESelected")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),

Просмотреть файл

@ -226,6 +226,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Press")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -249,6 +250,7 @@ namespace UnityEngine.InputSystem
.WithShortDisplayName("LMB")
.WithLayout(kButtonLayout)
.WithUsages(4, 1)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -272,6 +274,7 @@ namespace UnityEngine.InputSystem
.WithShortDisplayName("RMB")
.WithLayout(kButtonLayout)
.WithUsages(5, 1)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -294,6 +297,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Middle Button")
.WithShortDisplayName("MMB")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -316,6 +320,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Forward")
.WithLayout(kButtonLayout)
.WithUsages(6, 1)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -338,6 +343,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Back")
.WithLayout(kButtonLayout)
.WithUsages(7, 1)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),

Просмотреть файл

@ -1169,6 +1169,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Press")
.WithLayout(kTouchPressLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -1532,6 +1533,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Primary Touch Touch Contact?")
.WithShortDisplayName("Primary Touch Touch Contact?")
.WithLayout(kTouchPressLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -1576,6 +1578,7 @@ namespace UnityEngine.InputSystem
.WithShortDisplayName("Primary Touch Indirect Touch?")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1599,6 +1602,7 @@ namespace UnityEngine.InputSystem
.WithShortDisplayName("Primary Touch Tap")
.WithLayout(kButtonLayout)
.WithUsages(0, 1)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2091,6 +2095,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Touch Contact?")
.WithShortDisplayName("Touch Touch Contact?")
.WithLayout(kTouchPressLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -2135,6 +2140,7 @@ namespace UnityEngine.InputSystem
.WithShortDisplayName("Touch Indirect Touch?")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2157,6 +2163,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Tap")
.WithShortDisplayName("Touch Tap")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2523,6 +2530,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Touch Contact?")
.WithShortDisplayName("Touch Touch Contact?")
.WithLayout(kTouchPressLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -2567,6 +2575,7 @@ namespace UnityEngine.InputSystem
.WithShortDisplayName("Touch Indirect Touch?")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2589,6 +2598,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Tap")
.WithShortDisplayName("Touch Tap")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -2955,6 +2965,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Touch Contact?")
.WithShortDisplayName("Touch Touch Contact?")
.WithLayout(kTouchPressLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -2999,6 +3010,7 @@ namespace UnityEngine.InputSystem
.WithShortDisplayName("Touch Indirect Touch?")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -3021,6 +3033,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Tap")
.WithShortDisplayName("Touch Tap")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -3387,6 +3400,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Touch Contact?")
.WithShortDisplayName("Touch Touch Contact?")
.WithLayout(kTouchPressLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -3431,6 +3445,7 @@ namespace UnityEngine.InputSystem
.WithShortDisplayName("Touch Indirect Touch?")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -3453,6 +3468,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Tap")
.WithShortDisplayName("Touch Tap")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -3819,6 +3835,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Touch Contact?")
.WithShortDisplayName("Touch Touch Contact?")
.WithLayout(kTouchPressLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -3863,6 +3880,7 @@ namespace UnityEngine.InputSystem
.WithShortDisplayName("Touch Indirect Touch?")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -3885,6 +3903,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Tap")
.WithShortDisplayName("Touch Tap")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -4251,6 +4270,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Touch Contact?")
.WithShortDisplayName("Touch Touch Contact?")
.WithLayout(kTouchPressLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -4295,6 +4315,7 @@ namespace UnityEngine.InputSystem
.WithShortDisplayName("Touch Indirect Touch?")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -4317,6 +4338,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Tap")
.WithShortDisplayName("Touch Tap")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -4683,6 +4705,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Touch Contact?")
.WithShortDisplayName("Touch Touch Contact?")
.WithLayout(kTouchPressLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -4727,6 +4750,7 @@ namespace UnityEngine.InputSystem
.WithShortDisplayName("Touch Indirect Touch?")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -4749,6 +4773,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Tap")
.WithShortDisplayName("Touch Tap")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -5115,6 +5140,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Touch Contact?")
.WithShortDisplayName("Touch Touch Contact?")
.WithLayout(kTouchPressLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -5159,6 +5185,7 @@ namespace UnityEngine.InputSystem
.WithShortDisplayName("Touch Indirect Touch?")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -5181,6 +5208,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Tap")
.WithShortDisplayName("Touch Tap")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -5547,6 +5575,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Touch Contact?")
.WithShortDisplayName("Touch Touch Contact?")
.WithLayout(kTouchPressLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -5591,6 +5620,7 @@ namespace UnityEngine.InputSystem
.WithShortDisplayName("Touch Indirect Touch?")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -5613,6 +5643,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Tap")
.WithShortDisplayName("Touch Tap")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -5979,6 +6010,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Touch Contact?")
.WithShortDisplayName("Touch Touch Contact?")
.WithLayout(kTouchPressLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -6023,6 +6055,7 @@ namespace UnityEngine.InputSystem
.WithShortDisplayName("Touch Indirect Touch?")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -6045,6 +6078,7 @@ namespace UnityEngine.InputSystem
.WithDisplayName("Touch Tap")
.WithShortDisplayName("Touch Tap")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),

Просмотреть файл

@ -286,6 +286,8 @@ namespace UnityEngine.InputSystem.Editor
writer.WriteLine(" .IsNoisy(true)");
if (control.synthetic)
writer.WriteLine(" .IsSynthetic(true)");
if (control is ButtonControl)
writer.WriteLine(" .IsButton(true)");
writer.WriteLine(" .WithStateBlock(new InputStateBlock");
writer.WriteLine(" {");
writer.WriteLine($" format = new FourCC({(int) control.stateBlock.format}),");

Просмотреть файл

@ -43,7 +43,7 @@ namespace UnityEngine.InputSystem.Editor
// Revert back to original state.
var preloadedAssets = PlayerSettings.GetPreloadedAssets();
var index = preloadedAssets.IndexOfReference(m_SettingsAddedToPreloadedAssets);
var index = preloadedAssets.IndexOfReference<Object, Object>(m_SettingsAddedToPreloadedAssets);
if (index != -1)
{
ArrayHelpers.EraseAt(ref preloadedAssets, index);

Просмотреть файл

@ -1378,7 +1378,7 @@ namespace UnityEngine.InputSystem
}
////TODO: this should reset the device to its default state
public void EnableOrDisableDevice(InputDevice device, bool enable)
public void EnableOrDisableDevice(InputDevice device, bool enable, bool keepSendingEvents = false)
{
if (device == null)
throw new ArgumentNullException(nameof(device));
@ -1399,7 +1399,7 @@ namespace UnityEngine.InputSystem
var command = EnableDeviceCommand.Create();
device.ExecuteCommand(ref command);
}
else
else if (!keepSendingEvents)
{
var command = DisableDeviceCommand.Create();
device.ExecuteCommand(ref command);

Просмотреть файл

@ -1708,6 +1708,11 @@ namespace UnityEngine.InputSystem
/// Disable the given device, i.e. "mute" it.
/// </summary>
/// <param name="device">Device to disable. If already disabled, the method will do nothing.</param>
/// <param name="keepSendingEvents">If true, no <see cref="LowLevel.DisableDeviceCommand"/> will be sent
/// for the device. This means that the backend sending input events will not be notified about the device
/// being disabled and will thus keep sending events. This can be useful when input is being rerouted from
/// one device to another. For example, <see cref="TouchSimulation"/> uses this to disable the <see cref="Mouse"/>
/// while redirecting its events to input on a <see cref="Touchscreen"/>.<br/><br/>This parameter is false by default.</param>
/// <exception cref="ArgumentNullException"><paramref name="device"/> is <c>null</c>.</exception>
/// <remarks>
/// A disabled device will not receive input and will remain in its default state. It will remain
@ -1727,9 +1732,9 @@ namespace UnityEngine.InputSystem
/// </remarks>
/// <seealso cref="EnableDevice"/>
/// <seealso cref="InputDevice.enabled"/>
public static void DisableDevice(InputDevice device)
public static void DisableDevice(InputDevice device, bool keepSendingEvents = false)
{
s_Manager.EnableOrDisableDevice(device, false);
s_Manager.EnableOrDisableDevice(device, false, keepSendingEvents: keepSendingEvents);
}
public static bool TrySyncDevice(InputDevice device)
@ -2148,6 +2153,11 @@ namespace UnityEngine.InputSystem
/// to process the event. However, if any of the callbacks sets <see cref="InputEvent.handled"/>
/// to true, the event will be skipped and ignored.
///
/// Note that a device that is disabled (see <see cref="InputDevice.enabled"/>) may still get
/// this event signalled for it. A <see cref="DisableDeviceCommand"/> will usually be sent to
/// backends when a device is disabled but a backend may or may not respond to the command and
/// thus may or may not keep sending events for the device.
///
/// Note that the input system does NOT sort events by timestamps (<see cref="InputEvent.time"/>).
/// Instead, they are consumed in the order they are produced. This means that they
/// will also surface on this callback in that order.

Просмотреть файл

@ -316,6 +316,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithLayout(kButtonLayout)
.WithUsages(3, 1)
.WithAliases(0, 2)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -340,6 +341,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithLayout(kButtonLayout)
.WithUsages(4, 2)
.WithAliases(2, 2)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -364,6 +366,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithLayout(kButtonLayout)
.WithUsages(6, 2)
.WithAliases(4, 2)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -387,6 +390,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithShortDisplayName("Triangle")
.WithLayout(kButtonLayout)
.WithAliases(6, 2)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -409,6 +413,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithDisplayName("L1")
.WithShortDisplayName("L1")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -431,6 +436,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithDisplayName("R1")
.WithShortDisplayName("R1")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -452,6 +458,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithName("leftTriggerButton")
.WithDisplayName("leftTriggerButton")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -473,6 +480,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithName("rightTriggerButton")
.WithDisplayName("rightTriggerButton")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -494,6 +502,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithName("select")
.WithDisplayName("Share")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -516,6 +525,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithDisplayName("Options")
.WithLayout(kButtonLayout)
.WithUsages(8, 1)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -538,6 +548,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithDisplayName("L3")
.WithShortDisplayName("L3")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -560,6 +571,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithDisplayName("R3")
.WithShortDisplayName("R3")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -581,6 +593,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithName("systemButton")
.WithDisplayName("System")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -602,6 +615,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithName("touchpadButton")
.WithDisplayName("Touchpad Press")
.WithLayout(kButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -625,6 +639,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithShortDisplayName("L2")
.WithLayout(kButtonLayout)
.WithUsages(9, 1)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -648,6 +663,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithShortDisplayName("R2")
.WithLayout(kButtonLayout)
.WithUsages(10, 1)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -671,6 +687,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithShortDisplayName("LS Up")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -744,6 +761,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithShortDisplayName("LS Down")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -769,6 +787,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithShortDisplayName("LS Left")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -794,6 +813,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithShortDisplayName("LS Right")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -819,6 +839,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithShortDisplayName("RS Up")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -892,6 +913,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithShortDisplayName("RS Down")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -917,6 +939,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithShortDisplayName("RS Left")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -942,6 +965,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithShortDisplayName("RS Right")
.WithLayout(kButtonLayout)
.IsSynthetic(true)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1113150533),
@ -1012,6 +1036,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithDisplayName("D-Pad Up")
.WithShortDisplayName("D-Pad Up")
.WithLayout(kDiscreteButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1034,6 +1059,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithDisplayName("D-Pad Down")
.WithShortDisplayName("D-Pad Down")
.WithLayout(kDiscreteButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1056,6 +1082,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithDisplayName("D-Pad Left")
.WithShortDisplayName("D-Pad Left")
.WithLayout(kDiscreteButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),
@ -1078,6 +1105,7 @@ namespace UnityEngine.InputSystem.DualShock
.WithDisplayName("D-Pad Right")
.WithShortDisplayName("D-Pad Right")
.WithLayout(kDiscreteButtonLayout)
.IsButton(true)
.WithStateBlock(new InputStateBlock
{
format = new FourCC(1112101920),

Просмотреть файл

@ -1,6 +1,9 @@
using System;
using System.Diagnostics;
using UnityEngine.InputSystem.LowLevel;
#if UNITY_EDITOR
using UnityEditor;
#endif
////REVIEW: this *really* should be renamed to TouchPolling or something like that
@ -14,6 +17,8 @@ using UnityEngine.InputSystem.LowLevel;
////TODO: as soon as we can break the API, remove the EnhancedTouchSupport class altogether and rename UnityEngine.InputSystem.EnhancedTouch to TouchPolling
////FIXME: does not survive domain reloads
namespace UnityEngine.InputSystem.EnhancedTouch
{
/// <summary>
@ -85,6 +90,10 @@ namespace UnityEngine.InputSystem.EnhancedTouch
InputSystem.onBeforeUpdate += Touch.BeginUpdate;
InputSystem.onSettingsChange += OnSettingsChange;
#if UNITY_EDITOR
AssemblyReloadEvents.beforeAssemblyReload += OnBeforeDomainReload;
#endif
SetUpState();
}
@ -106,6 +115,10 @@ namespace UnityEngine.InputSystem.EnhancedTouch
InputSystem.onBeforeUpdate -= Touch.BeginUpdate;
InputSystem.onSettingsChange -= OnSettingsChange;
#if UNITY_EDITOR
AssemblyReloadEvents.beforeAssemblyReload -= OnBeforeDomainReload;
#endif
TearDownState();
}
@ -179,6 +192,16 @@ namespace UnityEngine.InputSystem.EnhancedTouch
SetUpState();
}
#if UNITY_EDITOR
private static void OnBeforeDomainReload()
{
// We need to release NativeArrays we're holding before losing track of them during domain reloads.
Touch.s_PlayerState.Destroy();
Touch.s_EditorState.Destroy();
}
#endif
[Conditional("DEVELOPMENT_BUILD")]
[Conditional("UNITY_EDITOR")]
internal static void CheckEnabled()

Просмотреть файл

@ -1,4 +1,5 @@
using System;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.Utilities;
@ -77,14 +78,15 @@ namespace UnityEngine.InputSystem.EnhancedTouch
throw new ArgumentNullException(nameof(pointer));
// Ignore if already added.
if (ArrayHelpers.ContainsReference(m_Sources, m_NumSources, pointer))
if (ArrayHelpers.ContainsReference(m_Pointers, m_NumPointers, pointer))
return;
var numPositions = m_NumSources;
ArrayHelpers.AppendWithCapacity(ref m_CurrentPositions, ref numPositions, Vector2.zero);
var index = ArrayHelpers.AppendWithCapacity(ref m_Sources, ref m_NumSources, pointer);
// Add to list.
var numPointers = m_NumPointers;
ArrayHelpers.AppendWithCapacity(ref m_Pointers, ref m_NumPointers, pointer);
ArrayHelpers.AppendWithCapacity(ref m_CurrentPositions, ref numPointers, default);
InstallStateChangeMonitors(index);
InputSystem.DisableDevice(pointer, keepSendingEvents: true);
}
protected void RemovePointer(Pointer pointer)
@ -93,229 +95,103 @@ namespace UnityEngine.InputSystem.EnhancedTouch
throw new ArgumentNullException(nameof(pointer));
// Ignore if not added.
var index = m_Sources.IndexOfReference(pointer, m_NumSources);
if (index == -1)
var pointerIndex = m_Pointers.IndexOfReference(pointer, m_NumPointers);
if (pointerIndex == -1)
return;
// Removing the pointer will shift indices of all pointers coming after it. So we uninstall all
// monitors starting with the device we're about to remove and then re-install whatever is left
// starting at the same index.
UninstallStateChangeMonitors(index);
// Cancel all ongoing touches from the pointer.
for (var i = 0; i < m_Touches.Length; ++i)
{
if (m_Touches[i].touchId == 0 || m_Touches[i].sourceIndex != index)
var button = m_Touches[i];
if (button != null && button.device != pointer)
continue;
var isPrimary = m_PrimaryTouchIndex == i;
var touch = new TouchState
{
phase = TouchPhase.Canceled,
position = m_CurrentPositions[index],
touchId = m_Touches[i].touchId,
};
if (isPrimary)
{
InputState.Change(simulatedTouchscreen.primaryTouch, touch);
m_PrimaryTouchIndex = -1;
}
InputState.Change(simulatedTouchscreen.touches[i], touch);
m_Touches[i].touchId = 0;
m_Touches[i].sourceIndex = 0;
UpdateTouch(i, pointerIndex, TouchPhase.Canceled);
}
// Remove from arrays.
var numPositions = m_NumSources;
ArrayHelpers.EraseAtWithCapacity(m_CurrentPositions, ref numPositions, index);
ArrayHelpers.EraseAtWithCapacity(m_Sources, ref m_NumSources, index);
// Remove from list.
var numPointers = m_NumPointers;
ArrayHelpers.EraseAtWithCapacity(m_Pointers, ref m_NumPointers, pointerIndex);
ArrayHelpers.EraseAtWithCapacity(m_CurrentPositions, ref numPointers, pointerIndex);
if (index != m_NumSources)
InstallStateChangeMonitors(index);
// Re-enable the device.
InputSystem.EnableDevice(pointer);
}
protected void InstallStateChangeMonitors(int startIndex = 0)
private unsafe void OnEvent(InputEventPtr eventPtr, InputDevice device)
{
////REVIEW: just bind to the entire pointer state instead of to individual controls?
for (var i = startIndex; i < m_NumSources; ++i)
{
var pointer = m_Sources[i];
var pointerIndex = m_Pointers.IndexOfReference(device, m_NumPointers);
if (pointerIndex < 0)
return;
// Monitor position.
InputState.AddChangeMonitor(pointer.position, this, i);
var eventType = eventPtr.type;
if (eventType != StateEvent.Type && eventType != DeltaStateEvent.Type)
return;
// Monitor any button that isn't synthetic.
var buttonIndex = 0;
foreach (var control in pointer.allControls)
if (control is ButtonControl button && !button.synthetic)
{
InputState.AddChangeMonitor(button, this, ((long)(uint)buttonIndex << 32) | (uint)i);
++buttonIndex;
}
}
}
protected void UninstallStateChangeMonitors(int startIndex = 0)
{
for (var i = startIndex; i < m_NumSources; ++i)
{
var pointer = m_Sources[i];
InputState.RemoveChangeMonitor(pointer.position, this, i);
var buttonIndex = 0;
foreach (var control in pointer.allControls)
if (control is ButtonControl button && !button.synthetic)
{
InputState.RemoveChangeMonitor(button, this, ((long)(uint)buttonIndex << 32) | (uint)i);
++buttonIndex;
}
}
}
protected void OnSourceControlChangedValue(InputControl control, double time, InputEventPtr eventPtr, long sourceDeviceAndButtonIndex)
{
var sourceDeviceIndex = sourceDeviceAndButtonIndex & 0xffffffff;
if (sourceDeviceIndex < 0 && sourceDeviceIndex >= m_NumSources)
throw new ArgumentOutOfRangeException(nameof(sourceDeviceIndex), $"Index {sourceDeviceIndex} out of range; have {m_NumSources} sources");
////TODO: this can be simplified a lot if we use events instead of InputState.Change() but doing so requires work on buffering events while processing; also
////TODO: this can be simplified if we use events instead of InputState.Change() but doing so requires work on buffering events while processing; also
//// needs extra handling to not lag into the next frame
if (control is ButtonControl button)
////REVIEW: should we have specialized paths for MouseState and PenState here? (probably can only use for StateEvents)
// Read pointer position.
var positionControl = m_Pointers[pointerIndex].position;
var positionStatePtr = positionControl.GetStatePtrFromStateEventUnchecked(eventPtr, eventType);
if (positionStatePtr != null)
m_CurrentPositions[pointerIndex] = positionControl.ReadValueFromState(positionStatePtr);
// End touches for which buttons are no longer pressed.
////REVIEW: There must be a better way to do this
for (var i = 0; i < m_Touches.Length; ++i)
{
var buttonIndex = (int)(sourceDeviceAndButtonIndex >> 32);
var isPressed = button.isPressed;
if (isPressed)
var button = m_Touches[i];
if (button == null || button.device != device)
continue;
var buttonStatePtr = button.GetStatePtrFromStateEventUnchecked(eventPtr, eventType);
if (buttonStatePtr == null)
{
// Start new touch.
for (var i = 0; i < m_Touches.Length; ++i)
{
// Find unused touch.
if (m_Touches[i].touchId != 0)
continue;
// Button is not contained in event. If we do have a position update, issue
// a move on the button's corresponding touch. This makes us deal with delta
// events that only update pointer positions.
if (positionStatePtr != null)
UpdateTouch(i, pointerIndex, TouchPhase.Moved, eventPtr);
}
else if (button.ReadValueFromState(buttonStatePtr) < (ButtonControl.s_GlobalDefaultButtonPressPoint * ButtonControl.s_GlobalDefaultButtonReleaseThreshold))
UpdateTouch(i, pointerIndex, TouchPhase.Ended, eventPtr);
}
var touchId = ++m_LastTouchId;
m_Touches[i] = new SimulatedTouch
{
touchId = touchId,
buttonIndex = buttonIndex,
sourceIndex = (int)sourceDeviceIndex,
};
// Add/update touches for buttons that are pressed.
foreach (var control in eventPtr.EnumerateControls(InputControlExtensions.Enumerate.IgnoreControlsInDefaultState, device))
{
if (!control.isButton)
continue;
var isPrimary = m_PrimaryTouchIndex == -1;
var position = m_CurrentPositions[sourceDeviceIndex];
var oldTouch = simulatedTouchscreen.touches[i].ReadValue();
// Check if it's pressed.
var buttonStatePtr = control.GetStatePtrFromStateEventUnchecked(eventPtr, eventType);
Debug.Assert(buttonStatePtr != null, "Button returned from EnumerateControls() must be found in event");
var value = 0f;
control.ReadValueFromStateIntoBuffer(buttonStatePtr, UnsafeUtility.AddressOf(ref value), 4);
if (value <= ButtonControl.s_GlobalDefaultButtonPressPoint)
continue; // Not in default state but also not pressed.
var touch = new TouchState
{
touchId = touchId,
position = position,
phase = TouchPhase.Began,
startTime = time,
startPosition = position,
isPrimaryTouch = isPrimary,
tapCount = oldTouch.tapCount,
};
if (isPrimary)
{
InputState.Change(simulatedTouchscreen.primaryTouch, touch, eventPtr: eventPtr);
m_PrimaryTouchIndex = i;
}
InputState.Change(simulatedTouchscreen.touches[i], touch, eventPtr: eventPtr);
break;
}
// See if we have an ongoing touch for the button.
var touchIndex = m_Touches.IndexOfReference(control);
if (touchIndex < 0)
{
// No, so add it.
touchIndex = m_Touches.IndexOfReference((ButtonControl)null);
m_Touches[touchIndex] = (ButtonControl)control;
if (touchIndex >= 0) // If negative, we're at max touch count and can't add more.
UpdateTouch(touchIndex, pointerIndex, TouchPhase.Began, eventPtr);
}
else
{
// End ongoing touch.
for (var i = 0; i < m_Touches.Length; ++i)
{
if (m_Touches[i].buttonIndex != buttonIndex || m_Touches[i].sourceIndex != sourceDeviceIndex ||
m_Touches[i].touchId == 0)
continue;
// Detect taps.
var position = m_CurrentPositions[sourceDeviceIndex];
var oldTouch = simulatedTouchscreen.touches[i].ReadValue();
var isTap = time - oldTouch.startTime <= Touchscreen.s_TapTime &&
(position - oldTouch.startPosition).sqrMagnitude <= Touchscreen.s_TapRadiusSquared;
var touch = new TouchState
{
touchId = m_Touches[i].touchId,
phase = TouchPhase.Ended,
position = position,
tapCount = (byte)(oldTouch.tapCount + (isTap ? 1 : 0)),
isTap = isTap,
startPosition = oldTouch.startPosition,
startTime = oldTouch.startTime,
};
if (m_PrimaryTouchIndex == i)
{
InputState.Change(simulatedTouchscreen.primaryTouch, touch, eventPtr: eventPtr);
////TODO: check if there's an ongoing touch that can take over
m_PrimaryTouchIndex = -1;
}
InputState.Change(simulatedTouchscreen.touches[i], touch, eventPtr: eventPtr);
m_Touches[i].touchId = 0;
break;
}
// Yes, so update it.
UpdateTouch(touchIndex, pointerIndex, TouchPhase.Moved, eventPtr);
}
}
else
{
Debug.Assert(control is InputControl<Vector2>, "Expecting control to be either a button or a position");
var positionControl = (InputControl<Vector2>)control;
// Update recorded position.
var position = positionControl.ReadValue();
var delta = position - m_CurrentPositions[sourceDeviceIndex];
m_CurrentPositions[sourceDeviceIndex] = position;
// Update position of ongoing touches from this pointer.
for (var i = 0; i < m_Touches.Length; ++i)
{
if (m_Touches[i].sourceIndex != sourceDeviceIndex || m_Touches[i].touchId == 0)
continue;
var oldTouch = simulatedTouchscreen.touches[i].ReadValue();
var isPrimary = m_PrimaryTouchIndex == i;
var touch = new TouchState
{
touchId = m_Touches[i].touchId,
phase = TouchPhase.Moved,
position = position,
delta = delta,
isPrimaryTouch = isPrimary,
tapCount = oldTouch.tapCount,
isTap = false, // Can't be tap as it's a move.
startPosition = oldTouch.startPosition,
startTime = oldTouch.startTime,
};
if (isPrimary)
InputState.Change(simulatedTouchscreen.primaryTouch, touch, eventPtr: eventPtr);
InputState.Change(simulatedTouchscreen.touches[i], touch, eventPtr: eventPtr);
}
}
}
void IInputStateChangeMonitor.NotifyControlStateChanged(InputControl control, double time, InputEventPtr eventPtr, long monitorIndex)
{
OnSourceControlChangedValue(control, time, eventPtr, monitorIndex);
}
void IInputStateChangeMonitor.NotifyTimerExpired(InputControl control, double time, long monitorIndex, int timerIndex)
{
// We don't use timers on our monitors.
eventPtr.handled = true;
}
private void OnDeviceChange(InputDevice device, InputDeviceChange change)
@ -365,12 +241,18 @@ namespace UnityEngine.InputSystem.EnhancedTouch
}
if (m_Touches == null)
m_Touches = new SimulatedTouch[simulatedTouchscreen.touches.Count];
m_Touches = new ButtonControl[simulatedTouchscreen.touches.Count];
foreach (var device in InputSystem.devices)
OnDeviceChange(device, InputDeviceChange.Added);
InputSystem.onDeviceChange += OnDeviceChange;
if (m_OnDeviceChange == null)
m_OnDeviceChange = OnDeviceChange;
if (m_OnEvent == null)
m_OnEvent = OnEvent;
InputSystem.onDeviceChange += m_OnDeviceChange;
InputSystem.onEvent += m_OnEvent;
}
protected void OnDisable()
@ -378,25 +260,84 @@ namespace UnityEngine.InputSystem.EnhancedTouch
if (simulatedTouchscreen != null && simulatedTouchscreen.added)
InputSystem.RemoveDevice(simulatedTouchscreen);
UninstallStateChangeMonitors();
// Re-enable all pointers we disabled.
for (var i = 0; i < m_NumPointers; ++i)
InputSystem.EnableDevice(m_Pointers[i]);
m_Sources.Clear(m_NumSources);
m_CurrentPositions.Clear(m_NumSources);
m_Pointers.Clear(m_NumPointers);
m_Touches.Clear();
m_NumSources = 0;
m_NumPointers = 0;
m_LastTouchId = 0;
m_PrimaryTouchIndex = -1;
InputSystem.onDeviceChange -= OnDeviceChange;
InputSystem.onDeviceChange -= m_OnDeviceChange;
InputSystem.onEvent -= m_OnEvent;
}
[NonSerialized] private int m_NumSources;
[NonSerialized] private Pointer[] m_Sources;
private unsafe void UpdateTouch(int touchIndex, int pointerIndex, TouchPhase phase, InputEventPtr eventPtr = default)
{
var position = m_CurrentPositions[pointerIndex];
var touch = new TouchState
{
phase = phase,
position = position
};
var time = eventPtr.valid ? eventPtr.time : InputState.currentTime;
var oldTouchState = (TouchState*)((byte*)simulatedTouchscreen.currentStatePtr +
simulatedTouchscreen.touches[touchIndex].stateBlock.byteOffset);
if (phase == TouchPhase.Began)
{
touch.isPrimaryTouch = m_PrimaryTouchIndex < 0;
touch.startTime = time;
touch.startPosition = position;
touch.touchId = ++m_LastTouchId;
touch.tapCount = oldTouchState->tapCount; // Get reset automatically by Touchscreen.
if (touch.isPrimaryTouch)
m_PrimaryTouchIndex = touchIndex;
}
else
{
touch.touchId = oldTouchState->touchId;
touch.isPrimaryTouch = m_PrimaryTouchIndex == touchIndex;
touch.delta = position - oldTouchState->position;
touch.startPosition = oldTouchState->startPosition;
touch.startTime = oldTouchState->startTime;
touch.tapCount = oldTouchState->tapCount;
if (phase == TouchPhase.Ended)
{
touch.isTap = time - oldTouchState->startTime <= Touchscreen.s_TapTime &&
(position - oldTouchState->startPosition).sqrMagnitude <= Touchscreen.s_TapRadiusSquared;
if (touch.isTap)
++touch.tapCount;
}
}
if (touch.isPrimaryTouch)
InputState.Change(simulatedTouchscreen.primaryTouch, touch, eventPtr: eventPtr);
InputState.Change(simulatedTouchscreen.touches[touchIndex], touch, eventPtr: eventPtr);
if (phase.IsEndedOrCanceled())
{
m_Touches[touchIndex] = null;
if (m_PrimaryTouchIndex == touchIndex)
m_PrimaryTouchIndex = -1;
}
}
[NonSerialized] private int m_NumPointers;
[NonSerialized] private Pointer[] m_Pointers;
[NonSerialized] private Vector2[] m_CurrentPositions;
[NonSerialized] private SimulatedTouch[] m_Touches;
[NonSerialized] private ButtonControl[] m_Touches;
[NonSerialized] private int m_LastTouchId;
[NonSerialized] private int m_PrimaryTouchIndex = -1;
[NonSerialized] private Action<InputDevice, InputDeviceChange> m_OnDeviceChange;
[NonSerialized] private Action<InputEventPtr, InputDevice> m_OnEvent;
internal static TouchSimulation s_Instance;
@ -430,14 +371,32 @@ namespace UnityEngine.InputSystem.EnhancedTouch
#endif
/// <summary>
/// An ongoing simulated touch.
/// </summary>
private struct SimulatedTouch
////TODO: Remove IInputStateChangeMonitor from this class when we can break the API
void IInputStateChangeMonitor.NotifyControlStateChanged(InputControl control, double time, InputEventPtr eventPtr, long monitorIndex)
{
}
void IInputStateChangeMonitor.NotifyTimerExpired(InputControl control, double time, long monitorIndex, int timerIndex)
{
}
// Disable warnings about unused parameters.
#pragma warning disable CA1801
////TODO: [Obsolete]
protected void InstallStateChangeMonitors(int startIndex = 0)
{
}
////TODO: [Obsolete]
protected void OnSourceControlChangedValue(InputControl control, double time, InputEventPtr eventPtr,
long sourceDeviceAndButtonIndex)
{
}
////TODO: [Obsolete]
protected void UninstallStateChangeMonitors(int startIndex = 0)
{
public int sourceIndex;
public int buttonIndex;
public int touchId;
}
}
}

Просмотреть файл

@ -61,7 +61,7 @@ namespace UnityEngine.InputSystem.OnScreen
set
{
controlPathInternal = value;
if (enabled)
if (isActiveAndEnabled)
SetupInputControl();
}
}
@ -91,9 +91,9 @@ namespace UnityEngine.InputSystem.OnScreen
private void SetupInputControl()
{
Debug.Assert(m_Control == null);
Debug.Assert(m_NextControlOnDevice == null);
Debug.Assert(!m_InputEventPtr.valid);
Debug.Assert(m_Control == null, "InputControl already initialized");
Debug.Assert(m_NextControlOnDevice == null, "Previous InputControl has not been properly uninitialized (m_NextControlOnDevice still set)");
Debug.Assert(!m_InputEventPtr.valid, "Previous InputControl has not been properly uninitialized (m_InputEventPtr still set)");
// Nothing to do if we don't have a control path.
var path = controlPathInternal;

Просмотреть файл

@ -302,7 +302,7 @@ namespace UnityEngine.InputSystem
""actions"": [
{
""name"": ""Navigate"",
""type"": ""Value"",
""type"": ""PassThrough"",
""id"": ""c95b2375-e6d9-4b88-9c4c-c5e76515df4b"",
""expectedControlType"": ""Vector2"",
""processors"": """",
@ -334,7 +334,7 @@ namespace UnityEngine.InputSystem
},
{
""name"": ""Click"",
""type"": ""Button"",
""type"": ""PassThrough"",
""id"": ""3c7022bf-7922-4f7c-a998-c437916075ad"",
""expectedControlType"": ""Button"",
""processors"": """",
@ -350,7 +350,7 @@ namespace UnityEngine.InputSystem
},
{
""name"": ""MiddleClick"",
""type"": ""Button"",
""type"": ""PassThrough"",
""id"": ""dad70c86-b58c-4b17-88ad-f5e53adf419e"",
""expectedControlType"": ""Button"",
""processors"": """",
@ -358,7 +358,7 @@ namespace UnityEngine.InputSystem
},
{
""name"": ""RightClick"",
""type"": ""Button"",
""type"": ""PassThrough"",
""id"": ""44b200b1-1557-4083-816c-b22cbdf77ddf"",
""expectedControlType"": ""Button"",
""processors"": """",

Просмотреть файл

@ -259,7 +259,7 @@
"actions": [
{
"name": "Navigate",
"type": "Value",
"type": "PassThrough",
"id": "c95b2375-e6d9-4b88-9c4c-c5e76515df4b",
"expectedControlType": "Vector2",
"processors": "",
@ -291,7 +291,7 @@
},
{
"name": "Click",
"type": "Button",
"type": "PassThrough",
"id": "3c7022bf-7922-4f7c-a998-c437916075ad",
"expectedControlType": "Button",
"processors": "",
@ -307,7 +307,7 @@
},
{
"name": "MiddleClick",
"type": "Button",
"type": "PassThrough",
"id": "dad70c86-b58c-4b17-88ad-f5e53adf419e",
"expectedControlType": "Button",
"processors": "",
@ -315,7 +315,7 @@
},
{
"name": "RightClick",
"type": "Button",
"type": "PassThrough",
"id": "44b200b1-1557-4083-816c-b22cbdf77ddf",
"expectedControlType": "Button",
"processors": "",

Просмотреть файл

@ -415,7 +415,7 @@ namespace UnityEngine.InputSystem
if (m_NeverAutoSwitchControlSchemes == value)
return;
m_NeverAutoSwitchControlSchemes = value;
if (enabled)
if (m_Enabled)
{
if (!value && !m_OnUnpairedDeviceUsedHooked)
StartListeningForUnpairedDeviceActivity();

Просмотреть файл

@ -86,6 +86,11 @@ namespace UnityEngine.InputSystem.UI
}
}
internal static int TouchIdFromPointerId(int pointerId)
{
return pointerId & 0xff;
}
////TODO: adder pressure and tile support (probably add after 1.0; probably should have separate actions)
/*
/// <summary>

Просмотреть файл

@ -13,6 +13,9 @@ using UnityEngine.Serialization;
////REVIEW: I think this would be much better served by having a composite type input for each of the three basic types of input (pointer, navigation, tracked)
//// I.e. there'd be a PointerInput, a NavigationInput, and a TrackedInput composite. This would solve several problems in one go and make
//// it much more obvious which inputs go together.
//// NOTE: This does not actually solve the problem. Even if, for example, we have a PointerInput value struct and a PointerInputComposite
//// that binds the individual inputs to controls, and then we use it to bind touch0 as a pointer input source, there may still be multiple
//// touchscreens and thus multiple touches coming in through the same composite. This leads back to the same situation.
////REVIEW: The current input model has too much complexity for pointer input; find a way to simplify this.
@ -635,23 +638,44 @@ namespace UnityEngine.InputSystem.UI
property.action.canceled -= actionCallback;
}
var oldActionNull = property?.action == null;
var oldActionEnabled = property?.action != null && property.action.enabled;
property = newValue;
#if DEBUG
if (newValue != null && newValue.action != null && newValue.action.type != InputActionType.PassThrough && newValue.action.m_Type != InputActionType.Button)
// We source inputs from arbitrary pointers through a set of pointer-related actions (point, click, etc). This means that in any frame,
// multiple pointers may pipe input through to the same action and we do not want the disambiguation code in InputActionState.ShouldIgnoreControlStateChange()
// to prevent input from getting to us. Thus, these actions should generally be set to InputActionType.PassThrough.
//
// We treat navigation actions differently as there is only a single NavigationModel for the UI that all navigation input feeds into.
// Thus, those actions should be configured with disambiguation active (i.e. Move should be a Value action and Submit and Cancel should
// be Button actions). This is especially important for Submit and Cancel as we get proper press and release action this way.
if (newValue != null && newValue.action != null && newValue.action.type != InputActionType.PassThrough && !IsNavigationAction(newValue))
{
Debug.LogWarning("Non-button actions used with the UI input module should generally be set to Pass-Through type so that the module can properly distinguish between "
+ $"input from multiple devices (action {newValue.action} is set to {newValue.action.type})", this);
Debug.LogWarning("Pointer-related actions used with the UI input module should generally be set to Pass-Through type so that the module can properly distinguish between "
+ $"input from multiple pointers (action {newValue.action} is set to {newValue.action.type})", this);
}
#endif
if (newValue != null && actionCallback != null && actionsHooked)
if (newValue?.action != null && actionCallback != null && actionsHooked)
{
property.action.performed += actionCallback;
property.action.canceled += actionCallback;
}
if (isActiveAndEnabled && newValue?.action != null && (oldActionEnabled || oldActionNull))
newValue.action.Enable();
}
#if DEBUG
private bool IsNavigationAction(InputActionReference reference)
{
return reference == m_SubmitAction || reference == m_CancelAction || reference == m_MoveAction;
}
#endif
/// <summary>
/// An <see cref="InputAction"/> delivering a <see cref="Vector2"/> 2D screen position
/// used as a cursor for pointing at UI elements.
@ -662,6 +686,9 @@ namespace UnityEngine.InputSystem.UI
/// Together with <see cref="leftClick"/>, <see cref="rightClick"/>, <see cref="middleClick"/>, and
/// <see cref="scrollWheel"/>, this forms the basis for pointer-type UI input.
///
/// This action should have its <see cref="InputAction.type"/> set to <see cref="InputActionType.PassThrough"/> and its
/// <see cref="InputAction.expectedControlType"/> set to <c>"Vector2"</c>.
///
/// <example>
/// <code>
/// var asset = ScriptableObject.Create&lt;InputActionAsset&gt;();
@ -699,6 +726,9 @@ namespace UnityEngine.InputSystem.UI
/// Note that the action is optional. A pointer is fully functional with just <see cref="point"/>
/// and <see cref="leftClick"/> alone.
///
/// This action should have its <see cref="InputAction.type"/> set to <see cref="InputActionType.PassThrough"/> and its
/// <see cref="InputAction.expectedControlType"/> set to <c>"Vector2"</c>.
///
/// <example>
/// <code>
/// var asset = ScriptableObject.Create&lt;InputActionAsset&gt;();
@ -741,6 +771,9 @@ namespace UnityEngine.InputSystem.UI
/// Note that together with <see cref="point"/>, this action is necessary for a pointer to be functional. The other clicks
/// and <see cref="scrollWheel"/> are optional, however.
///
/// This action should have its <see cref="InputAction.type"/> set to <see cref="InputActionType.PassThrough"/> and its
/// <see cref="InputAction.expectedControlType"/> set to <c>"Button"</c>.
///
/// <example>
/// <code>
/// var asset = ScriptableObject.Create&lt;InputActionAsset&gt;();
@ -784,6 +817,9 @@ namespace UnityEngine.InputSystem.UI
/// Note that the action is optional. A pointer is fully functional with just <see cref="point"/>
/// and <see cref="leftClick"/> alone.
///
/// This action should have its <see cref="InputAction.type"/> set to <see cref="InputActionType.PassThrough"/> and its
/// <see cref="InputAction.expectedControlType"/> set to <c>"Button"</c>.
///
/// <example>
/// <code>
/// var asset = ScriptableObject.Create&lt;InputActionAsset&gt;();
@ -832,6 +868,9 @@ namespace UnityEngine.InputSystem.UI
/// Note that the action is optional. A pointer is fully functional with just <see cref="point"/>
/// and <see cref="leftClick"/> alone.
///
/// This action should have its <see cref="InputAction.type"/> set to <see cref="InputActionType.PassThrough"/> and its
/// <see cref="InputAction.expectedControlType"/> set to <c>"Button"</c>.
///
/// <example>
/// <code>
/// var asset = ScriptableObject.Create&lt;InputActionAsset&gt;();
@ -877,6 +916,9 @@ namespace UnityEngine.InputSystem.UI
/// This action together with <see cref="submit"/> and <see cref="cancel"/> form the sources for navigation-style
/// UI input.
///
/// This action should have its <see cref="InputAction.type"/> set to <see cref="InputActionType.PassThrough"/> and its
/// <see cref="InputAction.expectedControlType"/> set to <c>"Vector2"</c>.
///
/// <example>
/// <code>
/// var asset = ScriptableObject.Create&lt;InputActionAsset&gt;();
@ -917,6 +959,8 @@ namespace UnityEngine.InputSystem.UI
/// This action together with <see cref="move"/> and <see cref="cancel"/> form the sources for navigation-style
/// UI input.
///
/// This action should have its <see cref="InputAction.type"/> set to <see cref="InputActionType.Button"/>.
///
/// <example>
/// <code>
/// var asset = ScriptableObject.Create&lt;InputActionAsset&gt;();
@ -957,6 +1001,8 @@ namespace UnityEngine.InputSystem.UI
/// This action together with <see cref="move"/> and <see cref="submit"/> form the sources for navigation-style
/// UI input.
///
/// This action should have its <see cref="InputAction.type"/> set to <see cref="InputActionType.Button"/>.
///
/// <example>
/// <code>
/// var asset = ScriptableObject.Create&lt;InputActionAsset&gt;();
@ -998,6 +1044,9 @@ namespace UnityEngine.InputSystem.UI
/// pointer-type input. This means that <see cref="leftClick"/>, <see cref="rightClick"/>, <see cref="middleClick"/>, and
/// <see cref="scrollWheel"/> can all be used for tracked device input like for regular pointer input.
///
/// This action should have its <see cref="InputAction.type"/> set to <see cref="InputActionType.PassThrough"/> and its
/// <see cref="InputAction.expectedControlType"/> set to <c>"Quaternion"</c>.
///
/// <example>
/// <code>
/// var asset = ScriptableObject.Create&lt;InputActionAsset&gt;();
@ -1037,6 +1086,9 @@ namespace UnityEngine.InputSystem.UI
/// pointer-type input. This means that <see cref="leftClick"/>, <see cref="rightClick"/>, <see cref="middleClick"/>, and
/// <see cref="scrollWheel"/> can all be used for tracked device input like for regular pointer input.
///
/// This action should have its <see cref="InputAction.type"/> set to <see cref="InputActionType.PassThrough"/> and its
/// <see cref="InputAction.expectedControlType"/> set to <c>"Vector3"</c>.
///
/// <example>
/// <code>
/// var asset = ScriptableObject.Create&lt;InputActionAsset&gt;();
@ -1210,9 +1262,23 @@ namespace UnityEngine.InputSystem.UI
//// making that setup explicit in actions may be quite awkward and not nearly as robust.
// Determine the pointer (and touch) ID. We default the pointer ID to the device
// ID of the InputDevice.
var controlParent = control.parent;
var touchControlIndex = m_PointerTouchControls.IndexOfReference(controlParent);
if (touchControlIndex != -1)
{
// For touches, we cache a reference to the control of a pointer so that we don't
// have to continuously do ReadValue() on the touch ID control.
m_CurrentPointerId = m_PointerIds[touchControlIndex];
m_CurrentPointerIndex = touchControlIndex;
m_CurrentPointerType = UIPointerType.Touch;
return touchControlIndex;
}
var pointerId = device.deviceId;
var touchId = 0;
var controlParent = control.parent;
// Need to check if it's a touch so that we get a correct pointerId.
if (controlParent is TouchControl touchControl)
touchId = touchControl.touchId.ReadValue();
// Could be it's a toplevel control on Touchscreen (like "<Touchscreen>/position"). In that case,
@ -1231,15 +1297,18 @@ namespace UnityEngine.InputSystem.UI
// NOTE: This is a linear search but m_PointerIds is only IDs and the number of concurrent pointers
// should be very low at any one point (in fact, we don't generally expect to have more than one
// which is why we are using InlinedArrays).
for (var i = 0; i < m_PointerIds.length; i++)
if (touchId == 0) // Not necessary for touches; see above.
{
if (m_PointerIds[i] == pointerId)
for (var i = 0; i < m_PointerIds.length; i++)
{
// Existing entry found. Make it the current pointer.
m_CurrentPointerId = pointerId;
m_CurrentPointerIndex = i;
m_CurrentPointerType = m_PointerStates[i].pointerType;
return i;
if (m_PointerIds[i] == pointerId)
{
// Existing entry found. Make it the current pointer.
m_CurrentPointerId = pointerId;
m_CurrentPointerIndex = i;
m_CurrentPointerType = m_PointerStates[i].pointerType;
return i;
}
}
}
@ -1289,7 +1358,7 @@ namespace UnityEngine.InputSystem.UI
{
if (m_CurrentPointerIndex == -1)
{
m_CurrentPointerIndex = AllocatePointer(pointerId, touchId, pointerType, device);
m_CurrentPointerIndex = AllocatePointer(pointerId, touchId, pointerType, device, touchId != 0 ? controlParent : null);
}
else
{
@ -1322,7 +1391,7 @@ namespace UnityEngine.InputSystem.UI
if (pointerType != UIPointerType.None)
{
// Device has an associated position input. Create a new pointer record.
index = AllocatePointer(pointerId, touchId, pointerType, device);
index = AllocatePointer(pointerId, touchId, pointerType, device, touchId != 0 ? controlParent : null);
}
else
{
@ -1374,7 +1443,7 @@ namespace UnityEngine.InputSystem.UI
return index;
}
private int AllocatePointer(int pointerId, int touchId, UIPointerType pointerType, InputDevice device)
private int AllocatePointer(int pointerId, int touchId, UIPointerType pointerType, InputDevice device, InputControl touchControl = null)
{
// Recover event instance from previous record.
var eventData = default(ExtendedPointerEventData);
@ -1392,6 +1461,7 @@ namespace UnityEngine.InputSystem.UI
// Allocate state.
m_PointerIds.AppendWithCapacity(pointerId);
m_PointerTouchControls.AppendWithCapacity(touchControl);
return m_PointerStates.AppendWithCapacity(new PointerModel(pointerId, touchId, pointerType, device, eventData));
}
@ -1406,6 +1476,8 @@ namespace UnityEngine.InputSystem.UI
private void RemovePointerAtIndex(int index)
{
if (m_PointerStates[index].eventData.pointerEnter != null)
Debug.Log("Foo");
Debug.Assert(m_PointerStates[index].eventData.pointerEnter == null, "Pointer should have exited all objects before being removed");
// Retain event data so that we can reuse the event the next time we allocate a PointerModel record.
@ -1415,6 +1487,7 @@ namespace UnityEngine.InputSystem.UI
// Remove. Note that we may change the order of pointers here. This can save us needless copying
// and m_CurrentPointerIndex should be the only index we get around for longer.
m_PointerIds.RemoveAtByMovingTailWithCapacity(index);
m_PointerTouchControls.RemoveAtByMovingTailWithCapacity(index);
m_PointerStates.RemoveAtByMovingTailWithCapacity(index);
Debug.Assert(m_PointerIds.length == m_PointerStates.length, "Pointer ID array should match state array in length");
@ -1454,14 +1527,17 @@ namespace UnityEngine.InputSystem.UI
{
ref var state = ref GetPointerStateForIndex(i);
var device = state.eventData.device;
if (!HaveControlForDevice(device, point) &&
!HaveControlForDevice(device, trackedDevicePosition) &&
!HaveControlForDevice(device, trackedDeviceOrientation))
if (!device.added || // Check if device was removed altogether.
(!HaveControlForDevice(device, point) &&
!HaveControlForDevice(device, trackedDevicePosition) &&
!HaveControlForDevice(device, trackedDeviceOrientation)))
{
SendPointerExitEventsAndRemovePointer(i);
--i;
}
}
m_NeedToPurgeStalePointers = false;
}
private static bool HaveControlForDevice(InputDevice device, InputActionReference actionReference)
@ -1480,6 +1556,13 @@ namespace UnityEngine.InputSystem.UI
private void OnPoint(InputAction.CallbackContext context)
{
// When a pointer is removed, there's like a non-zero coordinate on the position control and thus
// we will see cancellations on the "Point" action. Ignore these as they provide no useful values
// and we want to avoid doing a read of touch IDs in GetPointerStateFor() on an already removed
// touchscreen.
if (CheckForRemovedDevice(ref context))
return;
ref var state = ref GetPointerStateFor(ref context);
state.screenPosition = context.ReadValue<Vector2>();
}
@ -1490,6 +1573,9 @@ namespace UnityEngine.InputSystem.UI
private void OnLeftClick(InputAction.CallbackContext context)
{
if (CheckForRemovedDevice(ref context))
return;
ref var state = ref GetPointerStateFor(ref context);
state.leftButton.isPressed = context.ReadValueAsButton();
state.changedThisFrame = true;
@ -1497,6 +1583,9 @@ namespace UnityEngine.InputSystem.UI
private void OnRightClick(InputAction.CallbackContext context)
{
if (CheckForRemovedDevice(ref context))
return;
ref var state = ref GetPointerStateFor(ref context);
state.rightButton.isPressed = context.ReadValueAsButton();
state.changedThisFrame = true;
@ -1504,11 +1593,28 @@ namespace UnityEngine.InputSystem.UI
private void OnMiddleClick(InputAction.CallbackContext context)
{
if (CheckForRemovedDevice(ref context))
return;
ref var state = ref GetPointerStateFor(ref context);
state.middleButton.isPressed = context.ReadValueAsButton();
state.changedThisFrame = true;
}
private bool CheckForRemovedDevice(ref InputAction.CallbackContext context)
{
// When a device is removed, we want to simply cancel ongoing pointer
// operations. Most importantly, we want to prevent GetPointerStateFor()
// doing ReadValue() on touch ID controls when a touchscreen has already
// been removed.
if (context.canceled && !context.control.device.added)
{
m_NeedToPurgeStalePointers = true;
return true;
}
return false;
}
internal const float kPixelPerLine = 20;
private void OnScroll(InputAction.CallbackContext context)
@ -1538,11 +1644,14 @@ namespace UnityEngine.InputSystem.UI
private void OnControlsChanged(object obj)
{
PurgeStalePointers();
m_NeedToPurgeStalePointers = true;
}
public override void Process()
{
if (m_NeedToPurgeStalePointers)
PurgeStalePointers();
// Reset devices of changes since we don't want to spool up changes once we gain focus.
if (!eventSystem.isFocused)
{
@ -1701,6 +1810,7 @@ namespace UnityEngine.InputSystem.UI
private bool m_OwnsEnabledState;
private bool m_ActionsHooked;
private bool m_NeedToPurgeStalePointers;
private Action<InputAction.CallbackContext> m_OnPointDelegate;
private Action<InputAction.CallbackContext> m_OnMoveDelegate;
@ -1715,9 +1825,10 @@ namespace UnityEngine.InputSystem.UI
// Pointer-type input (also tracking-type).
private int m_CurrentPointerId = -1; // Keeping track of the current pointer avoids searches in most cases.
private int m_CurrentPointerIndex = -1;
private UIPointerType m_CurrentPointerType = UIPointerType.None;
private InlinedArray<int> m_PointerIds; // Index in this array maps to index in m_PointerStates. Separated out to make searching more efficient (we do a linear search).
private InlinedArray<PointerModel> m_PointerStates;
internal UIPointerType m_CurrentPointerType = UIPointerType.None;
internal InlinedArray<int> m_PointerIds; // Index in this array maps to index in m_PointerStates. Separated out to make searching more efficient (we do a linear search).
internal InlinedArray<InputControl> m_PointerTouchControls;
internal InlinedArray<PointerModel> m_PointerStates;
// Navigation-type input.
private NavigationModel m_NavigationState;

Просмотреть файл

@ -1,5 +1,7 @@
using UnityEngine.InputSystem;
////FIXME: This should be UnityEngine.InputSystem.UI
#if UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION
public
#else

Просмотреть файл

@ -100,8 +100,9 @@ namespace UnityEngine.InputSystem.Utilities
return ContainsReference(array, array.Length, value);
}
public static bool ContainsReference<TValue>(TValue[] array, int count, TValue value)
where TValue : class
public static bool ContainsReference<TFirst, TSecond>(TFirst[] array, int count, TSecond value)
where TSecond : class
where TFirst : TSecond
{
return IndexOfReference(array, value, count) != -1;
}
@ -154,14 +155,16 @@ namespace UnityEngine.InputSystem.Utilities
return -1;
}
public static int IndexOfReference<TValue>(this TValue[] array, TValue value, int count = -1)
where TValue : class
public static int IndexOfReference<TFirst, TSecond>(this TFirst[] array, TSecond value, int count = -1)
where TSecond : class
where TFirst : TSecond
{
return IndexOfReference(array, value, 0, count);
}
public static int IndexOfReference<TValue>(this TValue[] array, TValue value, int startIndex, int count)
where TValue : class
public static int IndexOfReference<TFirst, TSecond>(this TFirst[] array, TSecond value, int startIndex, int count)
where TSecond : class
where TFirst : TSecond
{
if (array == null)
return -1;

Просмотреть файл

@ -4,11 +4,11 @@
"com.unity.cinemachine": "2.3.4",
"com.unity.coding": "0.1.0-preview.20",
"com.unity.ide.rider": "1.1.4",
"com.unity.ide.vscode": "1.2.1",
"com.unity.ide.vscode": "1.2.3",
"com.unity.package-manager-doctools": "1.1.1-preview.5",
"com.unity.probuilder": "4.2.3",
"com.unity.settings-manager": "1.0.3",
"com.unity.test-framework": "1.1.19",
"com.unity.test-framework": "1.1.20",
"com.unity.test-framework.build": "0.0.1-preview.12",
"com.unity.test-framework.performance": "2.3.1-preview",
"com.unity.test-framework.utp-reporter": "1.0.2-preview",

Просмотреть файл

@ -1,2 +1,2 @@
m_EditorVersion: 2019.4.8f1
m_EditorVersionWithRevision: 2019.4.8f1 (60781d942082)
m_EditorVersion: 2019.4.18f1
m_EditorVersionWithRevision: 2019.4.18f1 (3310a4d4f880)