简体   繁体   中英

Unity3d How to detect taps on android?

Currently I am struggling to work out how to create a simple bit of code that will tell me if a tap has happened, allowing me to get the position of taps. Having looked through many different articles I am rather stumped as Touch.tapCount that is often suggested only works for ios. Also, i have tried to use Touch.deltaTime and/or Touch.deltaPosition to detect a tap but have failed.

I define a tap as having

  • A very short period between the finger initially touching and finally exiting the phone

  • Little or no movement

Thanks for reading this, I hope its clear and precise and if any detail or assistance is required on my behalf in order for your to answer feel free to ask. Any assistance is gratefully received. Thanks.

Note - I work in C#

First you have to find out if your Android device indeed can register several touches. If you have a newer device, this shouldn't be a problem. I'm going to assume that your device can, and if it can not - you'll soon find out soon enough.

let's start with the update method.

void Update() {
// Nothing at the moment
}

What we first want to do is register touches. We can do this by putting a foreach inside, checking for touches in Input.touches. Like this:

    void Update() {
    foreach (Touch touch in Input.touches) {

    }
}

By doing this, we are always checking how many touches there are currently on the screen. What we can now do is check by fingerId, and if fingerId == 0, 1, 2... run some code. Here's what we got now:

    void Update() {
        foreach (Touch touch in Input.touches) {

        if (touch.fingerId == 0) {
            // Finger 1 is touching! (remember, we count from 0)
        }

        if (touch.fingerId == 1) {
            // finger 2 is touching! Huzzah!
        }
    }
}

We're great so far! What we now want to do is detect the motion we want. In our case, we wanted taps, right? That should work perfectly with TouchPhase Began, and Ended. There's also TouchPhase.Moved, but we don't need that now.

if (touch.fingerId == 0) {

    if (Input.GetTouch(0).phase == TouchPhase.Began) {
        Debug.Log("First finger entered!");
    }

    if (Input.GetTouch(0).phase == TouchPhase.Ended) {
        Debug.Log("First finger left.");
    }
}

Here we are checking the phase of the corresponding finger. If you run that now, you should be able to see the messages in the console whenever your first touch enters, as well as leaves the screen. This can be done with several touches, so here's the 'whole' script:

    void Update() {
    foreach (Touch touch in Input.touches) {

        if (touch.fingerId == 0) {
            if (Input.GetTouch(0).phase == TouchPhase.Began) {
                Debug.Log("First finger entered!");
            }
            if (Input.GetTouch(0).phase == TouchPhase.Ended) {
                Debug.Log("First finger left.");
            }
        }

        if (touch.fingerId == 1) {
            if (Input.GetTouch(1).phase == TouchPhase.Began) {
                Debug.Log("Second finger entered!");
            }
            if (Input.GetTouch(1).phase == TouchPhase.Ended) {
                Debug.Log("Second finger left.");
            }
        }
    }
}

I'm hoping this will help you. I'm fairly new at this myself, so if we're lucky - someone with more experience can come and help. I'm confident that this could be written a lot cleaner. Just remember that if you build it, you can't see the console messages. Check out Unity Remote if you haven't already. Good luck! :)

After doing some searching (Googling "unity mobile detect tap"), I found this as the top search result. @Mothil's answer gets us very close to the solution and OP's answer solves the question. However, OP's answer does not account for multiple taps. Also tracking the count of started touches, ended touches, and moved touches (eg int SCount, MCount, ECount ) is not necessary.

Instead we can directly loop through each touch and track each individual touch by its fingerId as @mothil did. For this we'll need two arrays to track a tap's characteristics: 1) short time delay, and 2) no movement. In the arrays below, the array index is the fingerId.

private float[] timeTouchBegan;
private bool[] touchDidMove;

We'll also need one more variable to store our desired tap time threshold. In my tests, I found that a threshold of 0.2f works pretty well.

private float tapTimeThreshold = 0.2f;

In the Start() function, we'll initialize this to hold 10 elements for 10 touches. This can be modified to however many touches desired.

In the Update() function, we loop through each touch. If in TouchPhase.Began then we set the timeTouchBegan[fingerIndex] to the current time; we also set touchDidMove[fingerIndex] to false.

If in TouchPhase.Moved , we set touchDidMove[fingerIndex] to true.

Finally, in TouchPhase.Ended , we can calculate the tap time.

float tapTime = Time.time - timeTouchBegan[fingerIndex];

If the tap time is less than the threshold and the touch did not move then we have a verified tap.

if (tapTime <= tapTimeThreshold && touchDidMove[fingerIndex] == false)
{
    // Tap detected at touch.position
}

Complete Script

Here's the full class:

public class TapManager : MonoBehaviour
{        
    private float[] timeTouchBegan;
    private bool[] touchDidMove;
    private float tapTimeThreshold = 0.2f;

    void Start()
    {
        timeTouchBegan = new float[10];
        touchDidMove = new bool[10];
    }

    private void Update()
    {
        // Touches
        foreach (Touch touch in Input.touches)
        {
            int fingerIndex = touch.fingerId;

            if (touch.phase == TouchPhase.Began)
            {
                Debug.Log("Finger #" + fingerIndex.ToString() + " entered!");
                timeTouchBegan[fingerIndex] = Time.time;
                touchDidMove[fingerIndex] = false;
            }
            if (touch.phase == TouchPhase.Moved)
            {
                Debug.Log("Finger #" + fingerIndex.ToString() + " moved!");
                touchDidMove[fingerIndex] = true;
            }
            if (touch.phase == TouchPhase.Ended)
            {
                float tapTime = Time.time - timeTouchBegan[fingerIndex];
                Debug.Log("Finger #" + fingerIndex.ToString() + " left. Tap time: " + tapTime.ToString());
                if (tapTime <= tapTimeThreshold && touchDidMove[fingerIndex] == false)
                {
                    Debug.Log("Finger #" + fingerIndex.ToString() + " TAP DETECTED at: " + touch.position.ToString());
                }
            }            
        }
    }   
}

Unity Tests

I tested this in my game using Unity Remote. In the screenshot below, you can see my debug console logs. I did a four finger tap. You can see that fingers 0 through 3 entered and left without any movement detected. A tap was detected for each finger and each tap's location was printed to the console.

在此处输入图片说明

I am pretty sure that tapCount will work on Android. In my scenario, what handles input is not deriving from a MonoBehaviour. Instead its just an object I pass around. I wont supply the full class, but just what handles phone touches/taps.

if (Input.touchCount > i)
                {
                    if (Input.GetTouch(i).phase == TouchPhase.Began)
                    {
                    }
                    if (Input.GetTouch(i).phase == TouchPhase.Canceled)
                    {
                    }
                    if (Input.GetTouch(i).phase == TouchPhase.Ended)
                    {
                    }
                    if (Input.GetTouch(i).phase == TouchPhase.Moved)
                    {
                    }
                    if (Input.GetTouch(i).phase == TouchPhase.Stationary)
                    {
                    }
                }

The i is because I want to know how many touches the user has, but this should get you going at least.

Edit: I forgot to mention, you might want this if statement chunk inside a coroutine.

https://docs.unity3d.com/Manual/Coroutines.html

Hope this helps.

Thank you for everyone response's and I am grateful and I am sure there are several different ways to detect a tap. Below is my code, which having been prompted about phases, uses phases to tell if a touch has a movement phase or not allowing me to tell if a touch has moved, fulfilling the tap characteristic of :

Little or no movement

Also by looking at the time elapsed between the touch began and ended phase i can detect the second characteristic for a tap, which is :

A very short period between the finger initially touching and finally exiting the phone

Here is my code below -

using UnityEngine;
using System.Collections;

public class TouchManager : MonoBehaviour
{
    public Vector2 touchPos;
    int SCount; // Count of started touches
    int MCount; // Count of ended touches
    int ECount; // Count of moved touches
    int LastPhaseHappend; // 1 = S, 2 = M, 3 = E
    float TouchTime; // Time elapsed between touch beginning and ending
    float StartTouchTime; // Time.realtimeSinceStartup at start of touch
    float EndTouchTime; // Time.realtimeSinceStartup at end of touch

    void Start()
    {
        SCount = 0;
        MCount = 0;
        ECount = 0;
        StartTouchTime = 0;
        EndTouchTime = 0;
        TouchTime = 0;
        // All variables are intialized at zero, likely uneccessary as i believe they're 'zeroed' by default
    }

    // Update is called once per frame
    void Update()
    {
        touchPos = Vector3.zero;
        if (Input.touchCount != 0)
        {
            Touch currentTouch = Input.GetTouch(0);
            switch (currentTouch.phase)
            {
                case TouchPhase.Began:
                    if (LastPhaseHappend != 1)
                    {
                        SCount++;
                        StartTouchTime = Time.realtimeSinceStartup;
                    }
                    LastPhaseHappend = 1;
                    break;

                case TouchPhase.Moved:
                    if (LastPhaseHappend != 2)
                    {
                        MCount++;
                    }
                    LastPhaseHappend = 2;
                    break;

                case TouchPhase.Ended:
                    if (LastPhaseHappend != 3)
                    {
                        ECount++;
                        EndTouchTime = Time.realtimeSinceStartup;
                        TouchTime = EndTouchTime - StartTouchTime;
                    }
                    LastPhaseHappend = 3;
                    break;
            }
            if (SCount == ECount && ECount != MCount && TouchTime < 1)
                // TouchTime for a tap can be further defined
            {
                //Tap has happened;
                touchPos = currentTouch.position;
                MCount++;
            }
        }
    }
}

simple yet effective, jsut add the engine at top UI and add code in update dune

enter image description here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM