Closed southrop closed 4 years ago
I agree, I would definitely need this as well.
Valid issue, agree.
PlaceOnPlane
is a very bare bones example of how to do a raycast. You can adjust the logic to only do this if UI elements are interacted with first. I think the longer term solution would be to have a custom InputModule for ARFoundation. I've added it to our backlog.
@timm-unity Thanks for the explanation. I realised that Physics.Raycast
actually ignores UI components to begin with, so I might have been misunderstanding the raycast behaviour. Do you have any suggestions as to how best to adjust this logic to ignore the touch if it hits a UI component first? Would the best thing to do be to add a script to the ARPlane
that subscribes to touch events instead of using a script on ARSessionOrigin
, or is that unreliable somehow?
edit:
Managed to get it working using the method I mentioned above. Hopefully this is the "right"/"recommended" way to do it haha...
Or maybe not...
@southrop Check if touch was above any GUI element with GraphicsRaycaster first.
@Ursanon Thanks for the suggestion. Correct me if I'm wrong, but GraphicRaycaster
is specific to the canvas it is attached to. Unfortunately, my game uses multiple canvases to handle pop up dialogs and such additional UI elements (I didn't make the system), so it seems like an expensive operation to have to Find
all the canvases and then raycast for each one of them.
@southrop I ended up doing normal raycasting against layers and it works very well. It also respects UI elements such as raycast blocking etc., if you include it in the layermask you cast against. I've got the ARPlanes in its own layer, and AR-placed objects in another layer. So it's easy to cast again specific things by using the layermasks.
Pseudocode:
void CheckIfStartDrag(){
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(finger.ScreenPosition);
if (Physics.Raycast(ray, out hit, rayDistance, 1 << layerMaskUI)) {
Debug.Log("-- hit was ui, not starting drag"); //this will just allow the UI system to handle the click/touch/whatever
return;
}
if (!Physics.Raycast(ray, out hit, rayDistance, layerMaskDragGroup)) {
Debug.Log("hit nothing draggable, hit layer: "+hit.transform.gameObject.layer);
return;
}
}
@bitfabrikken Do you have UI with colliders?
@southrop Yeah, that's quite expensive, you can try caching GraphicRaycasters
or even better - use: EventSystem.IsPointerOverGameObject
.
@Ursanon huh? No.
@bitfabrikken Interesting. Physics.Raycast
isn't supposed to work on UI because they don't have colliders, which is why GraphicRaycaster
exists.
@Ursanon I'm actually already using EventSystem.current.IsPointerOverGameObject
in my code as I found that via Googling around this issue, but my experience has been that it returns false
all the time for some reason. As a quick test, I tried sticking it into the Update()
function in PlaceOnPlane.cs
in this sample project to make it just return without doing anything if it is true
, but no effect even when tapping above a UI button. I can't honestly say I'm too familiar with EventSystem
as a whole, so I might be doing something wrong...
@Ursanon @southrop Sorry got confused there a moment. I do have "Raycast target" checked on interactable stuff on my UI, like buttons with images. (replied from wrong account)
After trying all solutions I found this piece of code works fine for me on Unity 2019.2.15f1 and AR Foundation 3.0.0
bool IsPointOverUIObject(Vector2 pos)
{
if (EventSystem.current.IsPointerOverGameObject())
return false;
PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current);
eventDataCurrentPosition.position = new Vector2(pos.x, pos.y);
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(eventDataCurrentPosition, results);
return results.Count > 0;
}
And the Add the following in every touch call:
if (!IsPointOverUIObject(touchPosition) && m_RaycastManager.Raycast(touchPosition, s_Hits, TrackableType.Planes))
I hope it helps even after the thread closed.
After trying all solutions I found this piece of code works fine for me on Unity 2019.2.15f1 and AR Foundation 3.0.0
bool IsPointOverUIObject(Vector2 pos) { if (EventSystem.current.IsPointerOverGameObject()) return false; PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current); eventDataCurrentPosition.position = new Vector2(pos.x, pos.y); List<RaycastResult> results = new List<RaycastResult>(); EventSystem.current.RaycastAll(eventDataCurrentPosition, results); return results.Count > 0; }
And the Add the following in every touch call:
if (!IsPointOverUIObject(touchPosition) && m_RaycastManager.Raycast(touchPosition, s_Hits, TrackableType.Planes))
I hope it helps even after the thread closed.
This worked for me! Thanks :)
@digitalmkt The most straightforward, working solution I've found. Thanks! FYI I'm on 2019.1.0f2 abd ARFoundation 2.2.0
After trying all solutions I found this piece of code works fine for me on Unity 2019.2.15f1 and AR Foundation 3.0.0
bool IsPointOverUIObject(Vector2 pos) { if (EventSystem.current.IsPointerOverGameObject()) return false; PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current); eventDataCurrentPosition.position = new Vector2(pos.x, pos.y); List<RaycastResult> results = new List<RaycastResult>(); EventSystem.current.RaycastAll(eventDataCurrentPosition, results); return results.Count > 0; }
And the Add the following in every touch call:
if (!IsPointOverUIObject(touchPosition) && m_RaycastManager.Raycast(touchPosition, s_Hits, TrackableType.Planes))
I hope it helps even after the thread closed.
Worked for me as well...Thanks :)
It only works if I remove the
if (EventSystem.current.IsPointerOverGameObject())
return false;
bit. After all UI elements are game objects as well. Why does this snippet exist?
bool IsPointOverAnyObject(Vector2 pos)
{
PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current);
eventDataCurrentPosition.position = new Vector2(pos.x, pos.y);
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(eventDataCurrentPosition, results);
return results.Count > 0 || Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out RaycastHit hit, Mathf.Infinity, 1 << 3);
}
Slight modification so it blocks clicks from other gameobjects as well, not just UI. Need to set layer 3.
Is it possible to block the raycast from
ARSessionOrigin
inPlaceOnPlane.cs
with a UI component? It appears as though the raycast ignores all non-Trackable objects at the moment.I feel that having a UI element overlaid over an AR view is not an uncommon usecase, and surely people don't want the AR scene reacting to taps when all they wanted to do is press a button.