unity-game-engineaugmented-realityvuforiaandroid-augmented-reality

How do I place a UI button on image target using AR Camera and Vuforia


I want to place a UI button based on the image target. I am using Vuforia engine and I have an image which has chairs and tables. So I want to place a button on the UI (not a virtual button) based on image. For example, one button should be on the chair. When I move, the button should re-adjust to always show up over the chair and when clicked, it should display a 3d text saying that its a chair. I am a new to AR and wanted to explore onto some of such tutorials.


Solution

  • You can place UI elements in 3D space by choosing WorldSpace for the RenderMode a Canvas (also see the Manual) via the inspector.

    Than you can simply make that Canvas GameObject a child of the according ImageTarget. So that whenever the ImageTarget is moved (by the tracking) the Canvas is automatically moved along without having to take care of it.

    Alternative you could also move and place the object with that Canvas attached like any other object in a component e.g.

    public class PlaceCanvasOnTarget : MonoBehaviour
    {
        public Transform ImageTarget;
        public Canvas CanvasToPlace;
    
        // set an offset for the canvas e.g. 20cm above
        public Vector3 offset = new Vector3(0, 0.2, 0);
    
        private void LateUpdate()
        {
            Canvas.transform.position = ImageTarget.transform.position + offset;
        }
    }
    

    Additionally afaik the childs of an ImageTarget by default get not all disabled onTrackingLost and enabled onTrackingFound. Only some components are disabled but you can change this by copying the content of DefaultTrackableEventHandler to a custom one and adjust it something like (important for you are only OnTrackingFound and OnTrackingLost at the bottom)

    using UnityEngine;
    
    namespace Vuforia
    {
        /// <summary>
        /// A custom handler that implements the ITrackableEventHandler interface.
        /// </summary>
        public class DefaultTrackableEventHandler : MonoBehaviour,
        ITrackableEventHandler
        {
            #region PRIVATE_MEMBER_VARIABLES
    
            private TrackableBehaviour mTrackableBehaviour;
    
            #endregion // PRIVATE_MEMBER_VARIABLES
    
    
            #region UNTIY_MONOBEHAVIOUR_METHODS
    
            void Start()
            {
                mTrackableBehaviour = GetComponent<TrackableBehaviour>();
                if (mTrackableBehaviour)
                {
                    mTrackableBehaviour.RegisterTrackableEventHandler(this);
                }
            }
    
            #endregion // UNTIY_MONOBEHAVIOUR_METHODS
    
    
            #region PUBLIC_METHODS
            public GameObject show;
            public GameObject hide;
            /// <summary>
            /// Implementation of the ITrackableEventHandler function called when the
            /// tracking state changes.
            /// </summary>
            public void OnTrackableStateChanged(
                TrackableBehaviour.Status previousStatus,
                TrackableBehaviour.Status newStatus)
            {
                if (newStatus == TrackableBehaviour.Status.DETECTED ||
                    newStatus == TrackableBehaviour.Status.TRACKED ||
                    newStatus == TrackableBehaviour.Status.EXTENDED_TRACKED)
                {
                    OnTrackingFound();
                }
                else
                {
                    OnTrackingLost();
                }
            }
    
            #endregion // PUBLIC_METHODS
    
    
            #region PRIVATE_METHODS
    
            private void OnTrackingFound()
            {
                show.SetActive(true);
    
                // enabled all children
                foreach(Transform child in transform)
                {
                    child.gameObject.SetActive(true);
                }
    
                Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " found");
            }
    
            private void OnTrackingLost()
            {
                hide.SetActive(true);
    
                // disable all children
                foreach(Transform child in transform)
                {
                    child.gameObject.SetActive(false);
                }
    
                Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " lost");
            }
    
            #endregion // PRIVATE_METHODS
        }
    }
    

    For the button showing and hiding some text:

    1. Add a Button as child to the Canvas.

    2. Add a Panel as child to the Canvas. And add a Text as child to that Panel. (You don't need to use a Panel - it is basically just an Image component - but it might be easier to read the Text if it placed on a semitransparent or opaque surface instead of just "floating" in the air)

    3. Than make a simple component

      public TogglePanel : MonoBehaviour
      {
          public void Toggle()
          {
              gameObject.SetActive(!gameObject.activeSelf);
          }
      }
      
    4. Attach that component to the Panel object

    5. In the Button's onClick event now reference the TogglePanel component and select the method TogglePanel->Toggle()


    The result should be now