2

We are building an example that is similar to the "Kitten - Placing Virtual objects in AR" as shown here:

https://developers.google.com/tango/apis/unity/unity-howto-placing-objects.

Basically when you touch the screen, a kitten appears on the real world plane (floor).

In our app we have a side menu, with a few buttons and each shows a different game object. We want to detect touch anywhere on the screen except where there is UI. We want the UI to block touches in Tango, and only allow touches to instantiate the related game objects on areas of the screen without UI elements.

The touch specific code is here:

void Update() {
    if (Input.touchCount == 1) {
        // Trigger placepictureframe function when single touch ended.
        Touch t = Input.GetTouch(0);
        if (t.phase == TouchPhase.Ended) {
            PlacePictureFrame(t.position);
        }
    }
}

(The PlacePictureFrame() places a picture frame object at the touch position.)

I can't find any Tango examples which has touch and UI combined. I've tried an asset called LeanTouch to block touches behind UI elements but it doesn't seem to work with Tango specifically. Please help!

I have tried using method 5 from this:

How to detect events on UI and GameObjects with the new EventSystem API

and while it does add a PhysicsRaycaster to the TangoARCamera (which is tagged as MainCamera), the OnPointerDown method produces no debug logs no matter where you touch the screen. Tango is a special case so this is not a duplicate question. See below:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class PictureFrameUIController : MonoBehaviour, IPointerClickHandler {

    public GameObject m_pictureFrame;
    private TangoPointCloud m_pointCloud;

    void Start() {
        m_pointCloud = FindObjectOfType<TangoPointCloud>();
        addPhysicsRaycaster();
    }

    void addPhysicsRaycaster() {
        PhysicsRaycaster physicsRaycaster = GameObject.FindObjectOfType<PhysicsRaycaster>();
        if (physicsRaycaster == null) {
            Camera.main.gameObject.AddComponent<PhysicsRaycaster>();
        }
    }

    public void OnPointerClick(PointerEventData eventData) {
        Debug.Log("Clicked: " + eventData.pointerCurrentRaycast.gameObject.name);
        PlacePictureFrame(eventData.pointerCurrentRaycast.screenPosition);
    }


    //void Update() {
    //  if (Input.touchCount == 1) {
    //      // Trigger placepictureframe function when single touch ended.
    //      Touch t = Input.GetTouch(0);
    //      if (t.phase == TouchPhase.Ended) {
    //          PlacePictureFrame(t.position);
    //      }
    //  }
    //}

    void PlacePictureFrame(Vector2 touchPosition) {
        // Find the plane.
        Camera cam = Camera.main;
        Vector3 planeCenter;
        Plane plane;
        if (!m_pointCloud.FindPlane(cam, touchPosition, out planeCenter, out plane)) {
            Debug.Log("cannot find plane.");
            return;
        }

        // Place picture frame on the surface, and make it always face the camera.
        if (Vector3.Angle(plane.normal, Vector3.up) > 60.0f && Vector3.Angle(plane.normal, Vector3.up) < 140.0f) {
            Vector3 forward = plane.normal;
            // Vector3 right = Vector3.Cross(plane.normal, cam.transform.forward).normalized;
            // Vector3 forward = Vector3.Cross(right, plane.normal).normalized;
            Instantiate(m_pictureFrame, planeCenter, Quaternion.LookRotation(forward, Vector3.up));
        } else {
            Debug.Log("surface is not steep enough for picture frame to be placed on.");
        }
    }

    public void DeleteAllFrames() {
        GameObject[] frames = GameObject.FindGameObjectsWithTag("Frame");
        if (frames == null) {
            return;
        }
        foreach (GameObject frame in frames) {
            Destroy(frame);
        }
    }
}
fiixed
  • 141
  • 2
  • 10
  • This is a duplicate. If that solution did **not** work for you, should have asked this question with what you tried in that solution. Please, update your question with what you tried from that solution. Include the class too. – Programmer Apr 01 '17 at 01:17
  • I did. There was no answer as you had already marked it duplicate. Your solution is not relevant to Tango. – fiixed Apr 01 '17 at 01:23
  • Again, you should update your question with that code that "did not work". The code you tried from *method 5*. That code should be attached to the GameObject you detect clicks on. You have to show that code so that people can help you. – Programmer Apr 01 '17 at 01:27
  • Hi @Programmer, if you add it to the Gameobject that hasn't been instantiated yet, how will any of the Events be called? I've added the class as per your suggestion. – fiixed Apr 01 '17 at 01:53
  • I don't understand your last comment but wait. Can you attach that script to the object you want to detect click on and see if the `Debug.Log` is working? – Programmer Apr 01 '17 at 01:59
  • I can but the object isnt in the scene at runtime. When you touch the screen it instantiates, so there is no object to click on. – fiixed Apr 01 '17 at 02:01
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/139633/discussion-between-fiixed-and-programmer). – fiixed Apr 01 '17 at 02:04

1 Answers1

4

If you want to detect a click anywhere on the screen except for where there is a UI control/component, you have to check if the pointer is over the UI with EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId).

If on desktop, use EventSystem.current.IsPointerOverGameObject(). You are using Tango so EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId). should be used.

void Update()
{
    if (Input.touchCount == 1)
    {
        //Trigger placepictureframe function when single touch ended.
        Touch t = Input.GetTouch(0);
        if (t.phase == TouchPhase.Ended)
        {
            //Make sure that pointer is not over UI before calling  PlacePictureFrame
            if (!EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
            {
                PlacePictureFrame(t.position);
            }
        }
    }
}

Edit:

It seems like this works with TouchPhase.Began only.

Change t.phase == TouchPhase.Ended to t.phase == TouchPhase.Began and this should work as expected. Make sure to test with a mobile device/tango instead of your mouse.

Programmer
  • 121,791
  • 22
  • 236
  • 328
  • 1
    Mmm, unfortunately that didn't work. It's still placing picture frames behind the UI objects when touched. – fiixed Apr 01 '17 at 10:07
  • Hi, I looked at it, Change `t.phase == TouchPhase.Ended` to `t.phase == TouchPhase.Began`. That was the problem. I don't understand why it wouldn't work with `t.phase == TouchPhase.Ended`. – Programmer Apr 01 '17 at 16:20
  • you my friend are a f**king gun! It worked! Thank-you! – fiixed Apr 02 '17 at 03:04
  • You are welcome. Don't forget to [accept](https://meta.stackexchange.com/a/5235) this answer if your problem is solved. – Programmer Apr 02 '17 at 03:17
  • @Programmer if (!EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))...Is this still working? – zyonneo Jan 15 '19 at 05:13
  • As mentioned this only works with TouchPhase.Began. Which raises a question why this does not work for other situations where you want to for example drag the object around? – John Stock Dec 09 '20 at 08:22