Daevin
Daevin

Reputation: 901

How do I block ARCore model placement when the user touches a GameObject?

Disclaimer: I'm pretty new to Unity3D and ARCore, so please bear with me.

I'm using ARCore in Unity3D to create a scene where the user can select models in a ScrollView on the screen and place them using Google's ARCore framework.

So far I have it working; the user touches a model in the ScrollView (which is indicated on-screen in a Panel as the currently selected model, as my plans are to have the ScrollView toggle visibility for more screen view space).

The problem is that when a user selects a model, ARCore is placing a model on the detected plane behind where the ScrollView and selected model Panel objects are (even when you first touch to start scrolling the ScrollView). See below to help visualize.

enter image description here

How can I get ARCore to not place the object behind the ScrollView and Panel? What I've tried is adding to my controller (which is really just the Google HelloARController) a collection of objects that I want to block ARCore's Raycast and iterate through them with a foreach to see if the Raycast hits the GameObjects in the collection

Touch touch;
if (Input.touchCount < 1 || (touch = Input.GetTouch(0)).phase != TouchPhase.Began)
{
    return;
}

//my code; above is Google's
foreach (var item in BlockingObjects) { //BlockingObjects is a List<GameObject>
    if (IsTouchInObject(FirstPersonCamera.ScreenPointToRay(touch.position), item)) {
        return;
    }
}
//end of my code; below is Google's

TrackableHit hit;
TrackableHitFlag raycastFilter = TrackableHitFlag.PlaneWithinBounds | TrackableHitFlag.PlaneWithinPolygon;

With the IsTouchInObject function defined like this:

private bool IsTouchInObject(Ray ray, GameObject obj) {
    RaycastHit rch;
    Physics.Raycast (ray, out rch);
    return (rch.collider != null);
}

The thing that is failing is that rch.collider is always null (I know that I'm not testing against the object at all, I'll worry about that once I can get the Raycast to actually collide with a GameObject). I've tried using Physics/Physics2D with RaycastHit/RacastHit2D and attaching BoxCollider/BoxCollider2D components to the objects I want to detect the hit on, but nothing I've done is working.

(This solution was taken from something on the Unity3D forums in which someone had a similar issue, but not with AR, with their own 3D world with a 2D overlay. I can't find that forum post to provide reference, sorry).

Any help would be greatly appreciated.

EDIT/NOTE: I've now noticed there is a Graphic Raycaster component on the Canvas, which contains my ScrollView and Panel. I've tried setting the Blocking Objects to Two D (while adding a Box Collider 2D to the ScrollView and Panel) and Blocking Mask to Ignore Raycast (and a couple other things) to no avail. Is there a combination of values for these properties that could do it?

In the spirit of today's date:

Help me, StackOverflow...uh...Kenobi... You're my only hope.

Upvotes: 4

Views: 2230

Answers (2)

Adil.R
Adil.R

Reputation: 180

This is my first answer on StackOverflow so bear with me. I'm building a similar app where you can select different models from a UI Panel in unity and place the model on the detected plane in real world. The only hack I was able to devise to overcome the problem you're having is to do the following :

In your HelloARController.CS, initialize a bool 'place_model' (or any name you want) and set it to false. Now you need to scroll to the portion where you augment your model in the real world.

if (Session.Raycast(m_firstPersonCamera.ScreenPointToRay(touch.position), raycastFilter, out hit))
                {
                    // Create an anchor to allow ARCore to track the hitpoint as understanding of the physical
                    // world evolves.



                        var anchor = Session.CreateAnchor(hit.Point, Quaternion.identity);

                        // Intanstiate an Andy Android object as a child of the anchor; it's transform will now benefit
                        // from the anchor's tracking.
                        var andyObject = Instantiate(m_andy, hit.Point, Quaternion.identity,
                            anchor.transform);

                        // Andy should look at the camera but still be flush with the plane.
                        andyObject.transform.LookAt(m_firstPersonCamera.transform);
                        andyObject.transform.rotation = Quaternion.Euler(0.0f,
                            andyObject.transform.rotation.eulerAngles.y, andyObject.transform.rotation.z);

                        andyObject.AddComponent<BoxCollider>();
                        // Use a plane attachment component to maintain Andy's y-offset from the plane
                        // (occurs after anchor updates).
                        andyObject.GetComponent<PlaneAttachment>().Attach(hit.Plane);
}

Change the above code to something like this :

if (place_model){   // changed portion
if (Session.Raycast(m_firstPersonCamera.ScreenPointToRay(touch.position), raycastFilter, out hit))
                {
                    // Create an anchor to allow ARCore to track the hitpoint as understanding of the physical
                    // world evolves.



                        var anchor = Session.CreateAnchor(hit.Point, Quaternion.identity);

                        // Intanstiate an Andy Android object as a child of the anchor; it's transform will now benefit
                        // from the anchor's tracking.
                        var andyObject = Instantiate(m_andy, hit.Point, Quaternion.identity,
                            anchor.transform);

                        // Andy should look at the camera but still be flush with the plane.
                        andyObject.transform.LookAt(m_firstPersonCamera.transform);
                        andyObject.transform.rotation = Quaternion.Euler(0.0f,
                            andyObject.transform.rotation.eulerAngles.y, andyObject.transform.rotation.z);

                        andyObject.AddComponent<BoxCollider>();
                        // Use a plane attachment component to maintain Andy's y-offset from the plane
                        // (occurs after anchor updates).
                        andyObject.GetComponent<PlaneAttachment>().Attach(hit.Plane);

place_model = false // changed portion
    }

}

Now you won't be able to place a model no matter what. Because the script augmenting the model to the real world isn't running, as the bool 'place_model' is set to false. Now, just when you want to augment the model to the real world, you select the model from your panel and set the 'place_model' bool to true. This is simple as you just need to set an event listener to your button, setting the bool to true when you click the button. The next time you touch on the tracked plane, your model gets augmented.

Hope this solves your problem...

Upvotes: 3

Saico
Saico

Reputation: 644

Have you tried wrapping the Raycast with:

 if (!EventSystem.current.IsPointerOverGameObject(touch.fingerId)) {...}

https://answers.unity.com/questions/1406243/ui-in-arcore-helloar-example.html

Upvotes: 6

Related Questions