How to detect if InputSystem.EnhancedTouch.Finger is over UI Toolkit Element? - unity3d

I'm using InputSystem.EnhancedTouch for my game and I have a few buttons that are available through UI Elements. When I click on the UI Elements buttons it activates the InputSystem thus creating a bad experience. I want to filter out all the InputSystem.EnhancedTouch events that come through UI.
TL;DR - I want UI Elements to block InputSystem.EnhancedTouch events from triggering
I have found quite some resources but nothing really works. Unity and some other people say to use EventSystem.current.IsPointerOverGameObject but it doesn't work and throws the following warning(I think this is meant to work with normal input only, not enhanced one)
I have tried a few other solutions such as UIDocument.rootVisualElement.panel.Pick or EventSystem.current.RaycastAll but nothing seems to work, or return any consistent data that can be used.
InputSystem.EnhancedTouch binding
private void Awake()
{
EnhancedTouchSupport.Enable();
Touch.onFingerDown += OnFingerDown;
Touch.onFingerMove += OnFingerMove;
Touch.onFingerUp += OnFingerUp;
}

Related

MRTK V2 - Enable/Disable Spatial Mapping at Runtime

I know that this question has already been asked here twice, but the answers did not fix my problem. I need to enable spatial mapping on runtime. After scanning my environment I want to disable it, or hide at least the visualization of polygons, so I can save some fps. But by disabling spatial mapping I still want to have the colliders of my environment.
What I tried:
1. This example from this post did nothing.
if (disable){
// disable
MixedRealityToolkit.SpatialAwarenessSystem.Disable();
}
else
{
// enable
MixedRealityToolkit.SpatialAwarenessSystem.Enable()
}
2. Trying to disable the visualization gives me every time a nullreference. I guess GetObservers is giving null back or maybe meshOserver is null:
foreach(var observer in MixedRealityToolkit.SpatialAwarenessSystem.GetObservers())
{
var meshObserver = observer as IMixedRealitySpatialAwarenessMeshObserver;
if (meshObserver != null)
{
meshObserver.DisplayOption = SpatialAwarenessMeshDisplayOptions.None;
}
}
3. The example given by mrtk in there SpatialAwarenessMeshDemo scene, shows how to start and stop the observer. By starting everything starts fine but after suspending and clearing the observers the whole spatial map disappears, so my cursor does not align to my environment. So this is not what I need.
SpatialAwarenessSystem.ResumeObservers(); //start
SpatialAwarenessSystem.SuspendObservers();//stop
SpatialAwarenessSystem.ClearObservations();
What I have right now:
My Spatial Awareness Profile looks like this:
My code starts the spatial mapping with ResumeObservers, the foreach-loop gives me a nullreference and SuspendObserver is comment out, because it disables the whole spatial map thing:
if (_isObserverRunning)
{
foreach (var observer in SpatialAwarenessSystem.GetObservers())
{
var meshObserver = observer as IMixedRealitySpatialAwarenessMeshObserver;
if (meshObserver != null)
{
meshObserver.DisplayOption = SpatialAwarenessMeshDisplayOptions.None;
}
}
//SpatialAwarenessSystem.SuspendObservers();
//SpatialAwarenessSystem.ClearObservations();
_isObserverRunning = false;
}
else
{
SpatialAwarenessSystem.ResumeObservers();
_isObserverRunning = true;
}
Question: How do I start and stop spatial mapping the right way, so that I can save some performance and still have the colliders of the spatial map to interact with.
My specs:
MRTK v2.0.0
Unity 2019.2.0f1
Visual Studio 2017
!--Edit--inlcuding-Solution--!
1. With option #1 I was wrong. It does what its meant for, but I used it the wrong way. If you disable for example SpatialAwarenessSystem while running the spatial mapping process, it disables the whole process including the created spatial map. So after that you cant interact with the invironment.
2. What worked for me was using for the start ResumeObservers() in combination with setting display option to visible and for stopping spatial mapping the method SuspendObservers() in combination with display option none.
3. The Nullreference if fixed by rewritting and casting to IMixedRealityDataProviderAccess:
if (CoreServices.SpatialAwarenessSystem is IMixedRealityDataProviderAccess provider)
{
foreach (var observer in provider.GetDataProviders())
{
if (observer is IMixedRealitySpatialAwarenessMeshObserver meshObs)
{
meshObs.DisplayOption = option;
}
}
}
4. Performance: To get your fps back after starting an observer, you really need to disable the system via MixedRealityToolkit.SpatialAwarenessSystem.Disable();, but this will of course disable also the spatial map, so you cant interactive with it anymore.
#Perazim,
The recommendation is based on your option #3. Call ResumeObservers() to start and SuspendObservers() to stop. There is no need to call ClearObservations() unless you wish to have them removed from your scene.
The example calls ClearObservations() to illustrate what was, at the time, a new feature added to the Spatial Awareness system.
Please file an issue on GitHub (https://github.com/microsoft/MixedRealityToolkit-Unity/issues) for #1 (failure of Enable() and Disable() to impact the system). Those methods should behave as advertised.
Thank you!
David

MVVM, wait cursor while opening "slow" page

I have MVVM application with multiple pages. My all pages have ReadCommand() bound with:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding ReadCommand}" CommandParameter="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
This is my Read() command in ViewModel:
private void Read(object parameter)
{
HwDevice Device = new HwDevice();
this.Alarms = Device.Alarms; // this is slow (reading data from RS232 device)
Device.Dispose();
}
One page has slow data source and my application is frozen when this page is being loaded (about 5 seconds).
I want to set wait cursor on whole window, but I don't know how to do it in MVVM (Im MVVM newbie). Do I have to pass window reference by command parameter and set Wait cursor in command? If I should - how can I do it in XAML?
The problem is that the thread where you are going to execute your operation, is the same where your UI live. It's here that the BackgroundWorker come in handy.
BackgroundWorker bw = new BackgroundWorker();
bw.RunWorkerAsync += bw_DoWork;
bw.ProgressChanged += bw_ProgressChanged;
bw.RunWorkerCompleted += bw_WorkDone
The previous part was the declaration. Now you need to implement the events, but first modify Read method
private void Read(object parameter)
{
bw.RunWorkerAsync(parameter);
// put your logic here
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
object parameter = e.Argument;
}
Now that the logic is placed on another thread, you can use the progressChanged method to do various stuff. Like showing a progress bar to report the status, or simply enable or disable your wait cursor.
EDIT: You don't need to pass the bw to the UI. If you are using MVVM (like you should and you are doing) you can use bindings and event at your advantage, or implement an interface like this one. The point of the whole thing is that the UI is just "informed" that something on the background is going on, avoiding to freeze the main thread. You just need to decide how to display it. So it can be either using a wait cursor, or implementing a progress bar.

Coverting between javascript keycodes and libgdx keycodes

I am porting a existing webgame to libgdx. The game is controlled by a script I would rather not change.
The script specifies actions to do on certain keypress events using their javascript keycode value.
ie. "OnKeyPress=32:" would define actions to run when space is pressed.
"OnKeyPress=40:" would define actions to run when down is pressed, etc.
Now, as LibGDX uses different keycode system I need some way to fire my existing events when the correct key is pressed.
#Override
public boolean keyDown(int keycode) {
//convert input processors keycode to javascripts?
}
I can only think I have to create some sort of large static hashmap mapping between
GDXs
Input.Keys
and
com.google.gwt.event.dom.client.KeyCodes
But before going to this bother I thought Id ask around in case theres a better way?
Thanks,
In the end I just created a class to associate one keycode (com.badlogic.gdx.Input.Keys) with another. (com.google.gwt.event.dom.client.KeyCodes)
Fairly easy, but anyone doing the same should pay careful attention that not everything has a 1:1 mapping.
For example;
ALT_LEFT and ALT_RIGHT both map to ALT in gwt/javascript.
Likewise CNTR left and right and SHIFT left and right.
GDX meanwhile treats BACKSPACE and DELETE as the same key.
This is, naturally, as GDX needs to be cross platform so has different requirements.
Other then that just a ImmutableMap seemed to do the job....
/**
* static mapping between javascript/gwt keycodes and gdxs
**/
static ImmutableMap<Integer,Integer> keyCodeConversion = ImmutableMap.<Integer, Integer>builder()
.put(Input.Keys.UP, GwtKeyCodeCopy.UP)
.put(Input.Keys.DOWN, GwtKeyCodeCopy.DOWN)
.put(Input.Keys.LEFT, GwtKeyCodeCopy.LEFT)
.put(Input.Keys.RIGHT, GwtKeyCodeCopy.RIGHT)
.....
...(lots more)
.bind();
static public int getJavascriptKeyCode(int GdxKeycode){
return keyCodeConversion.get(GdxKeycode);
}

Detect mouse clicked on GUI

I got a problem in my project. I want to know that mouse cliked happend on GUI or on any game object.
I have tried this but it is showing null reference exception
EventSystem eventSystem = EventSystem.current;
if (eventSystem.IsPointerOverGameObject())
Debug.Log("left click over a gui element");
how to detect?? Is there any event available or else?
IsPointerOverGameObject() is fairly broken on mobile and some corner cases. We rolled our own for our project and it works like a champ on all platforms we've thrown it at.
private bool IsPointerOverUIObject() {
PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current);
eventDataCurrentPosition.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(eventDataCurrentPosition, results);
return results.Count > 0;
}
Source:
http://forum.unity3d.com/threads/ispointerovereventsystemobject-always-returns-false-on-mobile.265372/
There is some approaches that you can use to detect if your mouse is over a legacy GUI element here I'll show you one that I hope will work fine for you, if not research a little about "mouse over GUI" and you'll find a lot of different ways to do it (this one is what I use on my legacy GUI projects and usually works fine with touch):
Create an easily accessible behaviour (usually a singleton) to hold your MouseOverGUI "status":
if you are using GUILayout.Button you need to catch the last drawn rect, if you are GUI.Button just use the same rect you passed as button's param like this:
// GUILayout
( Event.current.type == EventType.Repaint &&
GUILayoutUtility.GetLastRect().Contains( Event.current.mousePosition ) ) {
mouseOverGUI = true;
}
} // you need call it in your OnGUI right after the call to the element that you want to control
// GUI
( Event.current.isMouse &&
yourRect.Contains( Event.current.mousePosition ) ) {
mouseOverGUI = true;
}
}
after that you just need to test if your mouseOverGUI is true or false to allow or not your desired click actions before execute them. (a good understanding of unity loops will help you to catch and test the flag in correct timing to avoid problems expecting to get something that already changed)
edited: also remember to reset mouseOverGUI to false when it is not over GUI ;)
Finally got my answer here:
There are three ways to do this, as demonstrated in this video tutorial. this video save me:).
Use EventSystem.current.IsPointerOverGameObject
Convert your OnMouseXXX and Raycasts to an EventSystem trigger. Use a physics raycaster on the camera
Implement the various handler interfaces from the EventSystems namespace. Use a physics raycaster on the camera.

pause viewmodel process for user input

I've been looking at a view examples of the typical "raise dialog from viewmodel" problem, noting 3 main solutions:
use attached behaviors
use a mediator pattern
use a service
I'm getting a bit bogged down though and struggling to find a solution that easily fits into my problem space - which is a very simple file copy problem:
My viewmodel is processing a loop (copying a list of files)
When a file already exists at the destination I need to raise a modal dialog to get confirmation to replace
The vm needs to wait for and receive confirmation before continuing
The "modal dialog" is actually not a new window but a hidden overlay in my MainWindow, as per http://www.codeproject.com/KB/WPF/wpfmodaldialog.aspx (thanks Ronald!)
I'm mostly there but the biggest struggles I have are:
- how to pause the loop in the viewmodel while it waits for input
- how to get input back to the viewmodel within the loop so it can carry on
So far I'm leaning towards the service solution because it seems a direct method call with a return that the vm must wait for. However, it does mean the service needs to tie directly to the view in order to make an element visible?
If anyone can post some simple code that deals directly with this problem I (and the net) would be very happy! Thanks!
For example, you have a service called IDialogService with the following interface:
public interface IDialogService
{
bool ConfirmAction(string title, string confirmationText);
}
As you mentioned, in order for the service to be able to show the actual dialog it needs to have a reference to the view that will show the actual overlay element. But instead of directly referencing the view I prefer to reference it via an interface. Lets call it ICanShowDialog and it will have the following members:
public interface ICanShowDialog
{
void ShowDialog(object dialogContent);
void HideDialog();
}
This interface will be implemented by your view that owns the dialog overlay (e.g. your main window).
Now the interesting part: suspending the code execution while the dialog is shown. First of all, I would recommend you not to use overlay elements but use usual windows if possible. Then you will not have that problem. You can style the dialog window so it will look just like the overlay element.
Anyway, if you still want to use overlay elements then you can do the following trick to suspend the code execution:
Here is pseudo code of the ConfirmAction method of the IDialogService inteface:
public bool ConfirmAction(string title, string confirmationText)
{
ConfirmationDialogView dialogView = new ConfirmationDialogView(title, confirmationText);
DialogShower.ShowDialog(dialogView); // DialogShower is of type ICanShowDialog
while (!dialogView.ResultAvailable)
{
DispatcherUtils.DoEvents();
}
DialogShower.HideDialog();
return dialogView.Result;
}
Here is the code of DispatcherUtils.DoEvents() (that was taken from here: http://dedjo.blogspot.com/2007/08/how-to-doevents-in-wpf.html):
public static class DispatcherUtils
{
public static void DoEvents()
{
DispatcherFrame f = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(
DispatcherPriority.Background,
(SendOrPostCallback)delegate(object arg) {
DispatcherFrame fr = arg as DispatcherFrame;
fr.Continue=True;
}, f);
Dispatcher.PushFrame(frame);
}
}
But I must warn you. Using DoEvents can result in some subtle bugs caused by inner dispatcher loops.
As an alternative to suspending the code execution while a dialog is shown you can use callbacks:
public interface IDialogService
{
void ConfirmAction(string title, string confirmationText, Action<bool> dialogResultCallback);
}
But it will not be so convenient to use.