Ray goes through GUI

In my turn-based game I am using Camera.ScreenPointToRay to allow interaction with all the various objects (e.g. selection, deselection, movement etc). However, I have quickly come to realize that using OnGUI results in one annoying problem - the ray casting goes through the GUI and acts on whatever is behind the GUI.

While I can envisage a work around with booleans and Rectangle.Contains(Input.mousePosition), this seems like quite a messy solution for more complicated GUIs. Is there a better work around, or have I just missed something very simple?

Alternatively, should I just get something off the Asset store? I would assume that a GUI manager from there would not have problems like this? I’m going to be aiming for iOS with this eventually, does that change what advice might be most appropriate?

I’ve glanced at Prime31 UIToolKit (aimed at iOS = good?) and NGUI so far…

Thanks,

Matt

Your probably right the simplest solution would be to create a set of transparent colliders where your GUI areas are which would block any Raycast from going any further.

Other GUI systems use a raycast to hit their UI elements, but even here if you still have a mouse based raycast from the main camera to interact with game elements, and setup a separate camera layer for the GUI you can end up with the same problem.

Maybe some flag that detects when the user interacts with a GUI element and then enables disables raycasting for that frame. Using LateUpdate for the raycasting to ensure it only occurs after all other events in that frame.

Thanks, I hadn’t though of LateUpdate - I’ll give that a go and see if I can make it work.

Would you advise using any 3rd party GUI systems over Unity’s built in system? I can see what you mean about the issue perhaps still being a problem though…

It depends, if your using a lot of GUI elements and want to see what your getting at build time and have the money but can’t wait for Unity to roll out it’s new GUI then yes have a look at the gui editors.

Check them out they all have free versions (I think) that you can try before you buy and make sure they work on your target platform.

Android Matt, if using the LateUpdate idea worked let us know. I am having the same problem, as I am using raycasting a lot in my game, and every time I click on a gui button I select things I don’t want to select.

I’m interested to see if this idea worked. Otherwise, I’ll have to limit my gui interface to a box on the bottom of my screen, behind which I can block raycasts with a primitive collider or something. Seems very clunky as a solution.

Try something like this:

if (Input.GetButtonDown("mouse 1")  GUIUtility.hotControl == 0) {
   DoTheRaycast();
} else {
   // Clicked on GUI
}

You could try out eDriven.Gui (Asset Store link, evaluation version here).

eDriven solves the problem of propagating mouse clicks to background by introducing two global flags.

The developer should check these flags from “3D code” to see if mouse is over the GUI, as well if any GUI element is in focus (for ignoring the keypresses):

// script attached to a 3D game object
using eDriven.Gui;
using UnityEngine;
 
public class Demo : MonoBehaviour {
	void OnMouseDown () {
		if (MouseEventDispatcher.Instance.MouseTarget == null) {
			// GUI not under the mouse. Do stuff with a game object.
		}
	}
	void Update () {
		if (Input.GetKeyDown ("space")  FocusManager.Instance.FocusedComponent == null) {
			print ("space key was pressed");
			// GUI not focused. Do stuff with a game object.
		}
	}
}

It works well with iOS. If you go for performance that you could use NGUI or such (eDriven.Gui is built on top of UnityGUI which isn’t completely optimized for draw calls).

Cheers!

Danko

Thanks everyone, I have finally got it working.

The key was the GUIUtility.hotControl == 0 that genail suggested. It didn’t quite work by itself for me since all my interactions were defined by OnMouseUp rather than Down (so hotControl equalled zero once the mouse was up, which meant that it never recognised the GUI button click).
The way that I got it to work was to make it so that an OnMouseDown on my GUI buttons called a function that sets a boolean to false for one frame. I then set the boolean back to true at the end of my update function after calling my raycast function, this prevents it from running in the same frame that a GUI button was interacted with. It works perfectly now!

Thanks again!

Matt

I got it working as well with the technique you stated Android Matt. But there is one small problem that is really annoying. There is a little green dot that shows up wherever I click on a GUI Button, and I’m assuming it’s supposed to be there to indicate the “hotspot” for where the mouse was clicked, but I don’t want the little green dot indicator.

Is there a way to get rid of the little green dot that indicates where your mouse is clicked? It’s showing up in my final builds as well.

Are you sure that you don’t have any other script that is drawing that green dot? :slight_smile:

lol. Well I’m 99% sure it’s not like a Debug.Drawline or anything like that. I havn’t needed to use those in a long time, and they’re all commented out.

So, as soon as I posted how it can’t be anything in my code, I started thinking about it and remembered I had another person’s GUI skin I was using.

In the GUI skin’s editor settings I turned off the Normal—>Background texture I was using, and that seemed to correct the green dot problem. However, switching this setting to “None” means that when I drag select I no longer have a selection box texture…which I need.

At least I may have pinpointed where my problem is, and it’s actually a good thing that it might be something I can fix. Thanks for getting me thinking genail :slight_smile:

So after messing with it for awhile, if my Editor GUI skin has a border on my box, it shows up on all my buttons when I click them. :frowning:

I still can’t figure out why. If I set the border on my box to zero, then it works as it should. Only drawback to this, is my selection box when doing multiple unit selection is less defined around the borders, since apparently the borders show up on my buttons when I click them lol.