Detecting the mouse releasing focus from Inspector in Unity - c#

Do you see how, after editing a value on -say- an image asset and then clicking anywhere outside without first clicking on apply, makes a window appear to ask you if you want to save or discard changes?
So, I wanted to do pretty much that from an OnGUI() or OnSceneGUI(), and right after starting to write the MyClassEditor : Editor realized that I not only knew how to accomplish such a thing; but apparently I didn't even know where to start searching on how to detect the mouse "entering" or "leaving" anything in the UI... if even possible.
This is what i found first when googling "detect mouse leaving inspector unity", and well as far as i understood, it is about detecting the edge of the screen boundaries, and the game window boundaries. So I went over the next result, which now looks promising at the beggining since it seems to describe my issue, but that is misleading since that solution applies to detect the mouse inside the scene view without loosing focus of the UI.. and i want to detect the focus lost so i head back to google only to stumble with a couple (more like several tbh) more of similar cases (like the unity reference to Monobehaviour.OnMouseEnter/Exit).
Which probably shows how lost I am, and I don't mean to ask for anything solved, but maybe a little push in the right direction will do? I appreciate every little help.
Edit:
So I tried this inside a [CustomEditor(typeof(MyClass))] public class MyClassEditor : Editor:
public override void OnInspectorGUI()
{
Event e = Event.current;
switch (e.type)
{
case EventType.MouseDown:
Debug.Log("mouse down");
break;
case EventType.MouseEnterWindow:
Debug.Log("mouse left a window");
break;
case EventType.MouseLeaveWindow:
Debug.Log("mouse entered a window");
break;
default:
break;
}
base.OnInspectorGUI();
var click = GUILayout.Button("Quick Fill");
if (click)
{
MyClassEditorWindow.Open((MyClass)target);
}
}
and even when the button works and the [mouse down] fires (only when clicking on top of what i think would be UIElements(?), tho -but not outside them, on the empty inspector area-),the other two ones don't seem to be firing... pretty sure im doing more than one thing wrong, not a clue of what of all.

There is a property in EditowWindow called wantsMouseEnterLeaveWindow.
If you set it to true, you will receive EventType.MouseLeaveWindow/EventType.MouseEnterWindow events.
Normally, I enable it in OnEnable function like this:
private void OnEnable() { wantsMouseEnterLeaveWindow = true; }

Related

C# Monogame - handling mouse

I'm trying to understand what is the proper way of handling mouse input when there are multiple objects on the screen that can be clicked. I have a custom Mouse class and many "game objects" on the screen that can be clicked (say like buttons of a gui). In many simple tutorials I see people check for mouse-pressed or released inside the Update method of each game object instance present on the scene and then the particular clicked object invokes an event that it has been clicked. But having say 20 or 30 or more of these objects - each with a different function means that the "top layer" (the manager of these objects) has to subscribe to all the OnClick events of all the buttons and call the respective function. Is this the correct way? (I'm not asking for an opinion - I know that each can program it's own way) Is there a less cumbersome way of programming this?
The "solution" that I was thinking is putting the click event ivocation in the Mouse class. So the mouse will notify the game/state/ui-manager that a click occured, passing the mouse coordinates and buttin clicked in the eventArgs. Then (and not at each game loop as before) the "manager" would find the game-object that has been clicked over. This might work for click events but is it possible to implement the mouse hover functionality in this way too?
Thanks for your 2 cents.
I prefer to implement a static InputManager class to provide a common input across platforms and input devices.
Here are snippets of that class relevant to your question:
public static class InputManager
{
public static bool LeftClicked = false;
private static MouseState ms = new MouseState(), oms;
public static void Update()
{
oms = ms;
ms = Mouse.GetState();
LeftClicked = ms.LeftButton != ButtonState.Pressed && oms.LeftButton == ButtonState.Pressed;
// true On left release like Windows buttons
}
public static bool Hover(Rectangle r)
{
return r.Contains(new Vector2(ms.X,ms.Y));
}
}
The first line of Update in game1.cs:
InputManager.Update();
In any of your game objects' Update method with a collision or draw rectangle named rect:
if (InputManager.Hover(rect))
{
// do hover code here
if(InputManager.LeftClicked)
{
//Do click action here
}
}
else
{
//undo hover code here
}
The only time I used a native input event callback with MonoGame was in an Android App to register/process swipes that could occur faster than 16.66ms, 60 fps.(There were no Threading issues involved in that case)

How to determine if the users gaze is on a GUI element? (HoloLens Unity.EventSystem code equivalent)

bool clickingGuiElement = false;
if (UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject())
{
if (UnityEngine.EventSystems.EventSystem.current.currentSelectedGameObject != null)
{
if (UnityEngine.EventSystems.EventSystem.current.currentSelectedGameObject
.GetComponent<CanvasRenderer>() != null)
{
// user is clicking on a UI element (button)
clickingGuiElement = true;
}
else
clickingGuiElement = false;
}
else
clickingGuiElement = false;
}
else
clickingGuiElement = false;
The code above is utilizing Unity's EventSystem. This small control structure serves the purpose of handling objects being currently clicked on. It accomplishes the task of setting a boolean to inform the program whether the user is clicking on an UI element or not. In my case it is primarily utilized on buttons. Using this boolean output one can block UI clicks from propagating through the UI and interacting with any gameObjects that may be behind the UI.
Running through the code above on the HoloLens with breakpoints and/or printouts the result is always the same - false. The program never makes it into the first if statement on the HoloLens.
I need to be able to detect if the users airtap/clicker input, when they click, is on a UI element. If I know when a user clicks on an UI element, I can block it from propagating through the UI (as is done above, but, again, it works in the Editor and not on the HoloLens).
So, what substitutes the same EventSystem functionality achieved here (in the Editor) on the HoloLens?
P.S. I am using IPointerClickerHandler from UnityEngine.EventSystems in other parts of my code and it works fine, so I am not sure why this implementation utilizing the EventSystems isn't working. My best guess (from implementing features in the Editor vs the HoloLens) is that the 'Pointer' used in IsPointerOverGameObject isn't utilizing airtap input akin to mouse click input.
if(GazeManager.Instance.IsGazingAtObject && GazeManager.Instance.HitObject != null)
{
Debug.Log("guiElementName: " + GazeManager.Instance.HitObject.transform.name);
}
Did not find an EventSystem code equivalent in the end, however there is an easy solution in the 'GazeManager' script provided in the HoloToolkit provided by Microsoft.
Utilizing a simple check like this one above, one is able to determine if the users gaze is on a GUI element. Like so,
// Object assignment
GameObject cursor;
// If on GUI menu control structure
if (GazeManager.Instance.IsGazingAtObject && GazeManager.Instance.HitObject != null &&
(GazeManager.Instance.HitObject.transform.name == "icon" ||
GazeManager.Instance.HitObject.transform.name == "guiManager"))
{
// Scale
cursor.transform.localScale = halfScale;
// Move the cursor to the point where the raycast hit
// to display it on the gui element
cursor.transform.position = GazeManager.Instance.HitInfo.point;
// Enable the cursor mesh
cursor.GetComponent<MeshRenderer>().enabled = true;
}
Self-explanatory really, the code above scales, moves, and displays a cursor gameObject on an GUI when the users gaze is on it.
In my case [I used] the boolean output [from the EventSystem code attempting to] block UI clicks from propagating through the UI and interacting with any gameObjects that may be behind the UI.
Rather then blocking the clicks, one can handle a users gaze input, when gazing at UI elements, properly using the GazeManager.
Your UI Element needs to have a collider, and you need to use the raycast to detect if your ui element is being hit by the raycast. In the hololens the only way that I know of to detect that an element or game object has been hit is using gaze.
Edit: Upon clarification of the problem, you need to make sure that the layer that the raycast is able to hit is only the layer your button is on.
The question asks about detecting if a button is clicked, and the comments bring more understanding of the full problem, but the nature of the question is asked from the perspective of the person asking the questions's experience with other systems not from the understanding of how Hololens and Unity work together, to give the answer you are looking for is not possible because it's the wrong question. Everything in unity is essentially a game object, but they all can sit on different and multiple layers at the same time in the scene, you can see the layers in the top right in unity. The answer to resolve the problem is to specify the layer that the button is on, and that layer is not shared with other game objects which is not default behavior in Unity. When you are doing the raycast, you need to ignore all of the other layers, then you will no longer interact with the background objects behind the button. The link I included tells you how to do that. Once you have done that you would use the airtap event wired to the button like any other game object.
Here is a link on how to work with layers in Unity.
https://answers.unity.com/questions/416919/making-raycast-ignore-multiple-layers.html

Move form on right click while resizing

I'm trying to move the form instead of resizing it while I'm resizing if the right button is down.
Resize event:
if (rightMouseDown)
{
this.SetDesktopLocation(MousePosition.X - this.Width, MousePosition.Y - this.Height);
this.MaximumSize = new System.Drawing.Size(this.Width, this.Height);
this.MinimumSize = new System.Drawing.Size(this.Width, this.Height);
}
Global mouse event:
bool rightMouseDown;
private void HoldMouse(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right && e.Clicks != 1)
{
rightMouseDown = true;
}
else if (e.Button == MouseButtons.Right)
{
rightMouseDown = false;
this.MaximumSize = new System.Drawing.Size(0, 0);
this.MinimumSize = new System.Drawing.Size(100, 100);
}
}
At the moment when I click the right button it freezes because the MaximumSize is constant, therefore I can't resize the form.
e.cancel would be awesome if it would work but I can't use that.
I'm able to capture the mouse events with a global one, the form events doesn't work weirdly.
I Got it working but only when the window's width is minimum and it goes to it's original width after the right mouse button goes up.
This is due to setting it to the default size. How can I keep the window in the changed size without setting it to MaximumSize 0,0?
First of all, I would suggest that you do not move the form while the right button is down, because this is highly non-standard and therefore likely to be perceived as highly peculiar by anyone trying to use your app. To be more specific, nobody is ever going to try either moving or resizing your form with the right mouse button, so:
if that's the only way you offer for moving your form, then nobody will ever be able to move your form.
if you also offer other ways of moving your form, then why bother with offering this way too?
Secondly, I would like to propose that this is most probably an X-Y problem meaning that you probably have some other issue, which you have told us nothing about, you think that you might address it by moving-by-right-click, then you discover that moving-by-right-click does not work for you, and you come here asking how to get moving-by-right-click to work. Why don't you begin by describing the real issue?
Thirdly, if you really want to proceed with moving your form by right-click, that's not how to do it.
First, you need to detect when the right mouse button is pressed. There are events for this. They work. If they don't work for you, that's not a reason to be doing other weird things instead. The statement "I'm not able to capture the mouse click with the Mouse events" is utterly bizarre, because a) it is a wrong use of the term "capture"; mouse capture is a very specific thing, (more about it later,) so please refrain from using it in other contexts, and b) if you cannot accomplish something, then that should be the subject of a stackoverflow question on its own. You cannot be trying utterly bizarre things because your attempts to do the right things failed.
So, once you have gotten detection of the right mouse click to work, then you need to set up mouse capture. (Look it up, search for "SetCapture".) That's what guarantees that you can keep receiving mouse move events, and finally a mouse-up event, even though the mouse has moved outside of your form while you were dragging it.

Touch method update Too fast for loop game

I'm having trouble in developing my windows phone app. It's a game and I'm using XNA.
Here one of the Navigation Page:
The problem is that in the top left the "back" button is at the same place on the page before this one. So when I tap the back button, it doesn't lead me to the previous page but to the first one. (sometimes if I click fast enought it leads me to the previous one). As it works sometimes, I think the error doesn't come from my code. I think as the back_button is at the same place on both pages, it update too fast and the "touch_event" stay for too long maybe. I don't know how to solve this.
There is how I catch the button click:
TouchPanelCapabilities touchCap = TouchPanel.GetCapabilities();
if (touchCap.IsConnected)
{
TouchCollection touches = TouchPanel.GetState();
if (touches.Count >= 1)
{
Vector2 PositionTouch = touches[0].Position;
return (Mouseclik((int)PositionTouch.X, (int)PositionTouch.Y));
}
}
return (Screen.ChooseLevelScreen);
You need something like this:
if (touches.Count >= 1)
if (touches[0].State == TouchLocationState.Released)
{
Vector2 PositionTouch = touches[0].Position;
return (Mouseclik((int)PositionTouch.X, (int)PositionTouch.Y));
}
Usually, a tap is detected when you release the touch, not while you are pressing it, in this way you are sure that you detect it only once.
Poll for when your finger is released as opposed to clicked. That way when you remove your finger from the touch button it will only be registered once and not the x amount of times your finger was still on the button and the loop was carried out.
see TouchLocationState.Released at
http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.input.touch.touchlocationstate.aspx
you need TouchLocationState.Released
http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.input.touch.touchlocationstate.aspx

C# - How to register onMouseClick in real time?

I am writing a silly little program (as we all do from time to time) to work on some basic C# coding. As I'm sure you can see, the mouse is clicked, the picture moves until it's at the place where the mouse was clicked. This bit works fine. I then want the picturebox to move randomly, which I have also managed! The next problem I have is that when I click, instead of moving to the new mouse coordinates, the picturebox continues to move randomly. Any help on this would be much appreciated! Here is the code I think is the problem.
Many Thanks!
John
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
int destX = e.X;
int destY = e.Y;
HasArrived = false;
while (HasArrived == false)
{
moveImage(destX, destY, pictureBox1);
if (pictureBox1.Left == destX && pictureBox1.Top == destY)
{
HasArrived = true;
while (HasArrived == true)
{
randomMove(pictureBox1);
hungry1 += 1;
}
}
}
}
You are stucked in your inner loop!
Try to put the loop in a backgroundworker,
so you can recognize the new mouseclick to set HasArrived to true.
And using the negative while statement in the positive while statemant seems very bad to me,
ive never seen that before.
I dont think thiss will work fine..
It's probably because HasArrived never gets set to false anywhere? But that will also mean you will be stuck in your Move loop as well.
EDIT: If you want to only move randomly once after you move to the position you clicked at, then remove the while around the random bit, else you'll need some other variable to know when to break out of the while.
Your inner loop seems to have a problem.
HasArrived = true;
while (HasArrived == true)
{
randomMove(pictureBox1);
hungry1 += 1;
}
Unless randomMove() can modify HasArrived, your loop never set HasArrived to false, so the while condition is always true, and you will never get out of the loop.
I looks like its because once the picture moves to its destination your program is stuck in a tight while loop from which it never escapes
First of all, you should never test equality of variables with boolean expressions. This means HasArrived == false is bad and it is better to write it like this:
while (!HasArrived)
{
// ...
while (HasArrived)
{
}
}
The problem is that your program does not give back control to the form, after the mouse has been clicked once.
Let me illustrate, what your program does as soon as the mouse has been clicked:
remember new x and y coords
while the picture has not arrived, move the image
in case the picture has arrived it's destination - move it around randomly
But it ends up moving the picture around until you kill the program, because your form does still handle the "OnMouseClicked"-event. And as long as the program does not return from your event handling method, the form is unable to record any other event (typically it should freeze, ending with "Application not responding").
So from what I think your code is supposed to do you should code your program like this:
1. Create a parallel thread to move around your image randomly
2. Pause the thread as long as your mouse-click event has not yet been handled
Also your question is wrong at all. Realtime means that your program is able to give any result in a predefined time-limit. Your method does not fit this at all, beacause the time your method takes for execution depends on how fast your image moves and which distance it should move.

Categories

Resources