I have a forward and back button that allows the player to scroll through an inventory of objects, and they can also skip ahead by clicking on a object in the train as well. The problem is, some of the objects are big enough that they are underneath the forward and back buttons themselves, so when you hit the button, you are actually hitting the object through the button. The scripts called to move the inventory around start working even though you just wanted to go forward or back. How can I make it so the raycast doesn’t work through UI objects?
Sadly you cannot at present. The Normal raycast system does not interact with the raycasts used by the UI system.
Other than using separate layers, we are stuck at present
I have no familiarity with layers. Are you saying it IS or is NOT possible with some layer hacking?
If you restrict Raycasts to particular render layers (the Layers combo at the top right of every GO in the inspector), you can distinguish between them, yes. And you can block UI cases with a CanvasGroup.
But there isn’t a way for a UI canvas to block ALL raycasts through the UI layer currently. (like the OnMouseDown event)
There are a few tweaks/hacks you can do, like changing your 3D raycast techniques to use the new UI techniques but the result is fugly.
Still searching for a better solution / example. Will blog one as soon as I find it :S
A couple of links that you can read up on:
How to prevent raycast when clicking 4.6 UI? - Questions & Answers - Unity Discussions (Check the youtube link for the fugly answer)
UI To Block Raycasts - Unity Engine - Unity Discussions (half an answer)
The search continues
I have an idea that I think might work;
On update() turn the layer mask on my raycast off.
I then move my physics raycast from Update into LateUpdate, then on the UI button click, call a function in the main game controller’s script that turns on the layer mask in the script. Then won’t the raycast ignore the GO’s behind the UI on that frame because it’s only checking for raycast hit on lateupdate?
So I have buttons that move through the inventory by calling forward() and back(), and here is my LateUpdate:
As you can see, it turns the layer off if you hit the buttons, and skips the if(), and then right after turns the layer back on. It works flawlessly.
public LayerMask layer;
void LateUpdate()
{
if (Input.GetMouseButtonDown (0)) {
RaycastHit hitInfo = new RaycastHit ();
if (Physics.Raycast (Camera.main.ScreenPointToRay (Input.mousePosition), out hitInfo, Mathf.Infinity, layer) && hitInfo.transform.tag == "Position") {
inventoryPos = hitInfo.transform.position;
initDistance = Vector3.Distance(transform.position, inventoryPos);
itemText.text = hitInfo.transform.GetComponent<itemInfo> ().itemText;
itemName.text = hitInfo.transform.GetComponent<itemInfo> ().itemName;
StartCoroutine ("Move");
}
}
layer.value=256;
}
public void forward(){
layer = 0;
savedPosition++;
inventoryPos = itemPositions [savedPosition].transform.position;
initDistance = Vector3.Distance(transform.position, inventoryPos);
itemName.text = itemPositions [savedPosition].GetComponent<itemInfo> ().itemName;
itemText.text = itemPositions [savedPosition].GetComponent<itemInfo> ().itemText;
StartCoroutine ("Move");
}
public void back(){
layer = 0;
savedPosition--;
inventoryPos = itemPositions [savedPosition].transform.position;
initDistance = Vector3.Distance(transform.position, inventoryPos);
itemName.text = itemPositions [savedPosition].GetComponent<itemInfo> ().itemName;
itemText.text = itemPositions [savedPosition].GetComponent<itemInfo> ().itemText;
StartCoroutine ("Move");
}
Sorry to spam, but I’ll delete this comment once I get it working again. It worked once but not now.
edit: I’m getting some results if I hold onto the position for one frame, and use an integer as a switch. I’d like to figure out how to do it better though.
Basically it works if I:
Make the UI button to change the switch to zero, starts enumerator to move the inventory
Raycast runs on same frame (goes through UI) and gets it’s own hitInfo and the new position
saved the position as the target position
run ienumerator with yield return 0; to wait a frame
on next frame, check if the target position is different than the current position, and if the switch is on
if so, start enumerator to move to that new position
I’d like to not have to wait a frame though.
Because the Canvas has a Graphic Raycaster component by default, I used the solution of raycasting with that first. If the results list is empty, then I raycast in my scene after using Physics.Raycast.
Unity doc: https://docs.unity3d.com/ScriptReference/UI.GraphicRaycaster.Raycast.html.
For idiots like me remember to use the GraphicRaycaster on the canvas you want to get information from, do not create a new one like in the Unity sample.
Do:
canvas.GetComponent().Raycast(d, results);