Hello. How to listen for keyboard click events in UIElements. I try to use UnityEngine.Event.current to capture keyboard click events but the UnityEngine.Event.current.keyCode I get is always None.
UIElements is retained mode so the UI is made of objects that persist. As such, you can't (or ideally shouldn't) have code that constantly checks for keyboard states anymore. Instead, you can use UIElements' own event system to register for the KeyDownEvent (or KeyUpEvent) on a specific element, using myElement.RegisterCallback<KeyDownEvent>((evt) => Debug.Log(evt.keyCode))
@uDamian surely this just makes things messy when you want your UI to controller inputs from the new input system?
We haven't added full support in UIElements for the new Input System yet, but when we do, it will still be based on UI events (converted from the input system). It may not be KeyDownEvent, but the pattern should be similar.
Any idea when UIElements & InputSystem will work together? Is it in scope for UIElements project or InputSystem project?
Hello, The goal for the initial release is to use the new input system for touch detection. We are also looking into a good way for users to interface the two systems with custom logic. If there are specific things you're looking for do let us know in this thread or a new thread.
If by touch do you mean pen support too? It'd be really useful to be able to use pen tablet inputs on things as well.
I just want to completely disable old input events and use only new input system, but currently many things do not work in UI Elements. I don't need anything fancy, just basic text input and buttons.
Here's a class I wrote based off the migration documentation: https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/manual/Migration.html Code (CSharp): using System.Collections.Generic; using UnityEngine.InputSystem; namespace UnityEngine.UIElements { [RequireComponent(typeof(EventSystem))] public class InputSystemInputWrapper : InputWrapper { //Note - I created an input system asset named GameInput. Yours may be different. private GameInput m_input; private string _compositionString = ""; private Vector2 _lastImeCompositionCursorPos = Vector2.zero; private IMECompositionMode _compositionMode = IMECompositionMode.Off; private void Awake() { m_input = new GameInput(); //Note - I created an input system asset named GameInput. Yours may be different. GetComponent<EventSystem>().inputOverride = this; } private void Current_onIMECompositionChange(InputSystem.LowLevel.IMECompositionString composition) { if (_compositionMode == IMECompositionMode.Off) { _compositionString = ""; return; } //On/auto? _compositionString = composition.ToString(); } private void OnEnable() { m_input.Enable(); Keyboard.current.onIMECompositionChange += Current_onIMECompositionChange; } private void OnDisable() { m_input.Disable(); Keyboard.current.onIMECompositionChange -= Current_onIMECompositionChange; } public override string compositionString { get { return _compositionString; } } //Not supported by input system, but we can store the value I guess? public override IMECompositionMode imeCompositionMode { get { return _compositionMode; } set { _compositionMode = value; } } //Read is not supported, but set is. public override Vector2 compositionCursorPos { get { return _lastImeCompositionCursorPos; } set { _lastImeCompositionCursorPos = value; Keyboard.current.SetIMECursorPosition(value); } } public override bool mousePresent { get { return Mouse.current != null; } } public override bool GetMouseButtonDown(int button) { if (button == 0) return Mouse.current.leftButton.wasPressedThisFrame; if (button == 1) return Mouse.current.rightButton.wasPressedThisFrame; if (button == 2) return Mouse.current.middleButton.wasPressedThisFrame; return false; } public override bool GetMouseButtonUp(int button) { if (button == 0) return Mouse.current.leftButton.wasReleasedThisFrame; if (button == 1) return Mouse.current.rightButton.wasReleasedThisFrame; if (button == 2) return Mouse.current.middleButton.wasReleasedThisFrame; return false; } public override bool GetMouseButton(int button) { if (button == 0) return Mouse.current.leftButton.isPressed; if (button == 1) return Mouse.current.rightButton.isPressed; if (button == 2) return Mouse.current.middleButton.isPressed; return false; } public override Vector2 mousePosition { get { return Mouse.current.position.ReadValue(); } } //Note - Mapping not provided by https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/manual/Migration.html, guessed it instead. public override Vector2 mouseScrollDelta { get { return Mouse.current.scroll.ReadValue(); } } public override bool touchSupported { get { return Touchscreen.current != null; } } public override int touchCount { get { if(!InputSystem.EnhancedTouch.EnhancedTouchSupport.enabled) InputSystem.EnhancedTouch.EnhancedTouchSupport.Enable(); return InputSystem.EnhancedTouch.Touch.activeTouches.Count; } } //Ugly. Might need some work? public override Touch GetTouch(int index) { if (!InputSystem.EnhancedTouch.EnhancedTouchSupport.enabled) InputSystem.EnhancedTouch.EnhancedTouchSupport.Enable(); var result = new UnityEngine.Touch(); var t = InputSystem.EnhancedTouch.Touch.activeTouches[index]; result.position = t.screenPosition; switch(t.phase) { case InputSystem.TouchPhase.Began: result.phase = TouchPhase.Began; break; case InputSystem.TouchPhase.Canceled: result.phase = TouchPhase.Canceled; break; case InputSystem.TouchPhase.Ended: result.phase = TouchPhase.Ended; break; case InputSystem.TouchPhase.Moved: result.phase = TouchPhase.Moved; break; case InputSystem.TouchPhase.None: case InputSystem.TouchPhase.Stationary: default: result.phase = TouchPhase.Stationary; break; } result.deltaPosition = t.delta; result.fingerId = t.finger.index; result.deltaTime = (float)(t.time - t.startTime); result.maximumPossiblePressure = 1f; //?? result.pressure = t.pressure; result.radius = t.radius.magnitude; //?? result.radiusVariance = 0f; //?? result.rawPosition = t.screenPosition; //?? result.tapCount = t.tapCount; result.type = TouchType.Direct; //?? return result; } public float GetGamePadHorizontalAxis() { //Missing this? /* In GameInput UI * - Add "UIGamePadHorizontal" * - Action Type : Pass Through * - Control Type: Axis * - Add Binding "D-Pad/X [Gamepad] */ return m_input.Player.UIGamePadHorizontal.ReadValue<float>(); } public float GetGamePadVerticalAxis() { //Missing this? /* In GameInput UI * - Add "UIGamePadVertical" * - Action Type : Pass Through * - Control Type: Axis * - Add Binding "D-Pad/Y [Gamepad] */ return m_input.Player.UIGamePadVertical.ReadValue<float>(); } public float GetGamePadSubmitButton() { //Missing this? /* In GameInput UI * - Add "UIGamePadSubmit" * - Action Type : Pass Through * - Control Type: Button * - Add Binding "Button South [Gamepad] */ return m_input.Player.UIGamePadSubmit.ReadValue<float>(); } public float GetGamePadCancelButton() { //Missing this? /* In GameInput UI * - Add "UIGamePadCancel" * - Action Type : Pass Through * - Control Type: Button * - Add Binding "Button East [Gamepad] */ return m_input.Player.UIGamePadCancel.ReadValue<float>(); } public override float GetAxisRaw(string axisName) { //No direct axis to GetAxisRaw results, but passthrough without filtering may provide what is needed. //Have to manually bind them in the input designer UI. switch(axisName) { case "Vertical": return GetGamePadVerticalAxis(); case "Horizontal": return GetGamePadHorizontalAxis(); default: if (!LoggedAxis.Contains(axisName)) { LoggedAxis.Add(axisName); Debug.Log($"InputSystemInputWrapper.GetAxisRaw(axisName:'{axisName}') - Not handled. Must bind to something in the input builder ui then use ReadUnprocessedValue() method."); } return 0; } } private List<string> LoggedAxis = new List<string>(); private List<string> LoggedButtonDown = new List<string>(); public override bool GetButtonDown(string buttonName) { //No direct axis to GetAxisRaw results, but passthrough without filtering may provide what is needed. //Have to manually bind them in the input designer UI. //This binding might not be correct... May need to bind to actual buttons for left/right etc or something idk. switch (buttonName) { case "Vertical": return GetGamePadVerticalAxis() != 0; case "Horizontal": return GetGamePadHorizontalAxis() != 0; case "Submit": return GetGamePadSubmitButton() != 0; case "Cancel": return GetGamePadCancelButton() != 0; default: if (!LoggedButtonDown.Contains(buttonName)) { LoggedButtonDown.Add(buttonName); Debug.Log($"InputSystemInputWrapper.GetButtonDown(buttonName:'{buttonName}') - Not handled. Must bind to something in the input builder ui then use ReadUnprocessedValue() method."); } return false; } } } }
For now I will try to use the public InputWrapper inputOverride { get; set; } or edit the *.cs file from Event System UI Toolkit manually and directly (Didn't start yet, but think thats the approach that could work) -> However, the Use Case that belongs to that approach: When you have an always visible UI but also a FPS "kindof" Steering in a RTS related genre, when you move your character or unit, the UI Visual Elements > Components (e.g. Button that have the Property "focusable" set to true) are creating nice Disco-Light Effects because while moving your Unit (or in the specific case a Building Element for an engineer project) they change focus according to their tab order as intended, of course that should only happen while in UI Mode. So that is more ment to be a suggestion: Can you implement a Baseline option for the Unity to be able to deactivate A, W, S, D etc. Keys for set focus while UI is not in focus itself? Maybe something, methodologically, same as: Code (CSharp): UnityEngine.Cursor.lockState = UnityEngine.CursorLockMode.Confined; UnityEngine.Cursor.visible = true; That I use to switch forth and back between UI Mode and FPS Mode and it works for the Mouse perfectly, just add something like this for the other Input Methods if you agree ^o^ - Please give advice if there is already something like this and I missed it, thanks!
But if I need to check the keyboard without binding to a specific element. And I don't want to use MonoBehaviour Update
You could monitor for keyboard events on some root element. Events bubble up so even if another element is focused, if it doesn't use up the key event it should bubble up to the root. Otherwise, if you're just looking to know when keys are pressed outside the context of the UI, then you're not really in UI Toolkit territory anymore and should be able to use the new Input System directly.
Hello @uDamian, I have watched your Ui toolkit demo and switch to UiElements, but i am stuck on a problem. How can i use EventSystem.current.IsPointerOverGameObject() with ui elements (Ui document) to understand the touch is on Ui or on my game area? Or a better advice? Thanks