"Venkat at Axiom Studios Please Help" 谁知道我将如何在 unity3D 中使用手势 Android 来跟踪 2 指 V 运动

"Venkat at Axiom Studios Please Help" dose anyone know how i would use gestures in unity3D for Android to track a 2 finger V motion

好的,这是我的问题,

我正在尝试弄清楚如何才能注册用户已使用两根手指在屏幕上制作 V(手指起点将是用户用两根手指触摸的位置,然后展开有两个手指同时向上移动以形成 V 形)使用 Unity 3d for android。

我之前从未做过涉及形状的手势,因此非常感谢任何关于如何做到这一点的建议、链接或示例

提前致谢 格雷姆

已编辑: 所以我在等待有人帮助我的时候一直在努力解决这个问题。

这是我到目前为止所得到的,它还没有按照我想要的方式工作,但我不确定我做错了什么,因为我以前从未尝试过用手势做这样的事情.任何帮助将不胜感激

再次感谢 格雷姆

using UnityEngine;

使用 System.Collections;

public class Pinchv : MonoBehaviour {

public Vector2 leftFingerStartPosition;
public Vector2 leftFingerEndPosition;

public Vector2 rightFingerStartPosition;
public Vector2 rightFingerEndPosition;

void Update () {

    foreach(Touch touch in Input.touches)
    {
        if(touch.phase == TouchPhase.Began){

            Touch leftFinger = Input.GetTouch (0);
            Touch rightFinger = Input.GetTouch (1);

            leftFingerStartPosition = Input.GetTouch (0).position;
            leftFingerEndPosition = Input.GetTouch(0).position;

            rightFingerStartPosition = Input.GetTouch(1).position;
            rightFingerEndPosition = Input.GetTouch(1).position;


            if(Input.touchCount == 2 && Mathf.Abs(leftFingerEndPosition.x + Screen.width - leftFingerStartPosition.x) > 20 &&
               Mathf.Abs(leftFingerEndPosition.y + Screen.height - leftFingerStartPosition.y) > 60){

                if(Input.touchCount == 2 && Mathf.Abs(rightFingerEndPosition.x + Screen.width - rightFingerStartPosition.x) > 20 &&
                   Mathf.Abs(rightFingerEndPosition.y + Screen.height - rightFingerStartPosition.y) > 60){
                    Debug.Log ("its a v ");
                }
            }
            if(touch.phase == TouchPhase.Ended){
                leftFingerStartPosition = Vector2.zero;
                leftFingerEndPosition = Vector2.zero;


                rightFingerStartPosition = Vector2.zero;
                rightFingerEndPosition = Vector2.zero;  
            }
        }
    }
}

}

编辑

所以我听取了你的建议并尝试了一些不同的方法,但不幸的是它根本不起作用。

如果我不能解决这个问题,我很快就要开始拔头发了,哈哈。这是我尝试创建的新代码,但它不起作用。有人可以帮我解决这个问题吗?它已经让我发疯了 3 天了,哈哈。

@Axiom Studios 的 Venkat 你能再次帮助我吗,我将不胜感激:)

耐心等待 格雷姆

using UnityEngine;

使用 System.Collections;

public class Pinchv : MonoBehaviour {

public Vector2 fingerOneStartPosition;
public Vector2 fingerOneEndPosition;

public Vector2 fingerTwoStartPosition;
public Vector2 fingerTwoEndPosition;

void Update () {

    foreach(Touch touch in Input.touches)
    {
        if(touch.phase == TouchPhase.Began){

        //  Touch leftFinger = Input.GetTouch (0);
        //  Touch rightFinger = Input.GetTouch (1);

            fingerOneStartPosition = Input.GetTouch (0).position;
            fingerOneEndPosition = Input.GetTouch(0).position;

            fingerTwoStartPosition = Input.GetTouch(1).position;
            fingerTwoEndPosition = Input.GetTouch(1).position;

            if(Input.touchCount == 2 && Mathf.Abs(fingerOneStartPosition.x - fingerOneEndPosition.x) > 700 &&
               Mathf.Abs(fingerOneStartPosition.y - fingerOneEndPosition.y) > 120){
                if(Input.touchCount == 2 && Mathf.Abs(fingerTwoStartPosition.x - fingerTwoEndPosition.x) > 700 &&
                   Mathf.Abs(fingerTwoStartPosition.y - fingerTwoEndPosition.y) > 120){

                    Debug.Log ("its a v ");
                }
            }
        }
            if(touch.phase == TouchPhase.Ended){
                fingerOneStartPosition = fingerOneEndPosition;
                fingerOneEndPosition = Vector2.zero;


                fingerTwoStartPosition = fingerTwoEndPosition;
                fingerTwoEndPosition = Vector2.zero;    
            }
        }
    }

public void OnGUI(){

    GUILayout.Label("Where am i fingerone X : " + fingerOneStartPosition + "end position" + fingerOneEndPosition);
    GUILayout.Label("Where am i fingerone X : " + fingerTwoStartPosition + "end position" + fingerTwoEndPosition);
}

}

@Graeme

好的,我想出了一些不完整的代码。这绝不是一段很棒的代码,但它应该让您对如何解决问题有一个相当清晰的认识。

代码确实有效(使用 Moto G 1st Gen,Unity 4.6 测试),但它仍然会给出一些不正确的结果。
我已经评论了 class 中我发现潜在问题的部分,因此您可以将其作为一个不错的起点。

试试看,然后告诉我。肯定有比这个更优雅的解决方案,事实上,几个月前我正在为 Unity 开发一个手势识别库。我应该把它掸掉然后完成它 :P

Unity C#代码

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

public class DetectVGesture : MonoBehaviour {

//Note : This was tested on a 1st Gen Moto G (1280 x 720 resolution, IIRC)
// and an orthographic size of 5.
//Why this matters :
//The Vector2 minimumDeltas uses Screen units, so resolution matters here
//The float maxDistBetInitPos uses World units, so if the camera's ortho size is larger, this value becomes larger as well

//Therefore, some trial and error in these values will be needed to get it to work right

//TODO : Write some code to take into account screen resolution and camera size / FOV.
//If anyone feels like editing that portion in, please feel free

//The touches used are maintained in these two lists
private List<Touch> firstTouches = new List<Touch>();
private List<Touch> secondTouches = new List<Touch>();

//This is the minimum distance in SCREEN units 
//of touch.deltaTouch for a new touch in TouchPhase.Moved to register
public Vector2 minimumDeltas = new Vector2(1, 1);

//This is the maximum distance between the two initial touches
//in WORLD units for the algorithm to work
public float maxDistBetInitPos = 3f;

//These are the minimum and maximum angles between the two
//"ARMS" of the "V" for a V gesture to be recognized
public Vector2 vAnglesMinMax = new Vector2(15, 60);


// Use this for initialization
void Start () {

}


void OnGUI () {


    GUI.Label(new Rect(10, 10, 100, 50), "Touches "+Input.touchCount.ToString());

    if(Input.touchCount > 0)
        GUI.Label(new Rect(110, 10, 100, 50), "Touch1 "+Input.touches[0].position.ToString());

    if(Input.touchCount > 1)
        GUI.Label(new Rect(210, 10, 100, 50), "Touch2 "+Input.touches[1].position.ToString());

}

// Update is called once per frame
void Update () {


    //For this sample, we're only interested in a "V" created with 
    //2 fingers, so we'l ignore the rest
    if(Input.touchCount == 2) {

        foreach(Touch touch in Input.touches) {

            //The below two lines are to allow for an early
            //exit if EITHER of the fingers is stationary. 
            //Uncomment the lines if you want touches to be registered
            //only when BOTH fingers move.
            //if(touch.phase == TouchPhase.Stationary)
                //return;

            //This is the first time TWO fingers are registered,
            //so we can use this as our starting point, where the
            //touches are closest to each other. 
            //From here on, I'll refer this to as the BOTTOM of the "V"
            if(touch.phase == TouchPhase.Began) {
                CheckTouchAndAdd(Input.touches[0], Input.touches[1]);
            }

            //There was some movement, so let's check what it is
            if(touch.phase == TouchPhase.Moved) {

                //The movement in this touch is at least as much as we want
                //So, we add both the touches, and we move to the next iteration
                //Here, I want both the X & Y delta positions to meet my minimum
                //delta distance. You can change this to either X or Y.
                if(Mathf.Abs(touch.deltaPosition.x) >= minimumDeltas.x &&
                   Mathf.Abs(touch.deltaPosition.y) >= minimumDeltas.y) {
                    CheckTouchAndAdd(Input.touches[0], Input.touches[1]);
                }

                else {
                    Debug.Log("There was too less of delta!");
                }

            }

            //The touch / touches have ended. 
            //So let's clear the lists for the next trial
            if(touch.phase == TouchPhase.Ended) {
                firstTouches.Clear();
                secondTouches.Clear();
            }

        }//Iterate over touches in Input.touches ends
    }//Input.touchCount == 2 ends
}

private void CheckTouchAndAdd (Touch touch1, Touch touch2) {
    if(!firstTouches.Contains(touch1) && !secondTouches.Contains(touch2)) {
        firstTouches.Add(touch1);
        secondTouches.Add(touch2);
        CheckForV();
    }

}


private void CheckForV () {

    if(firstTouches.Count < 5 || secondTouches.Count < 5) {
        Debug.Log("Not enough touches to perform the check! ");
        return;
    }


    //First, let's check if the two initial touch points
    //were relatively close enough to warrant a "V"
    //If they're not, we'll have an early exit
    Vector3 firstTouchInitPos = Camera.main.ScreenToWorldPoint(firstTouches[0].position);
    Vector3 secondTouchInitPos = Camera.main.ScreenToWorldPoint(secondTouches[0].position);

    //First we check if the X distance falls within our limit of maximum distance
    if(Mathf.Abs(secondTouchInitPos.x - firstTouchInitPos.x) > maxDistBetInitPos) {
        Debug.Log (string.Format("The X values were too far apart! Exiting check First {0}," +
                                 "Second {1}, Distance {2}", 
                                 new object[] { firstTouchInitPos.x, secondTouchInitPos.x, 
        Mathf.Abs(secondTouchInitPos.x - firstTouchInitPos.x)} ));
        return;
    }

    //Then we check the same for Y
    if(Mathf.Abs(secondTouchInitPos.y - firstTouchInitPos.y) > maxDistBetInitPos) {
        Debug.Log (string.Format("The Y values were too far apart! Exiting check First {0}," +
                                 "Second {1}, Distance {2}", 
                                 new object[] { firstTouchInitPos.y, secondTouchInitPos.y, 
            Mathf.Abs(secondTouchInitPos.y - firstTouchInitPos.y)} ));
        return;
    }


    //If we reach this point, both the X & the Y positions are within the maximum distance
    //we want. So, they're close enough that we can calculate the average between the two Vectors
    //and assume that both these Vectors intersect at the average point. (i.e. the average point
    //is the corner at the BOTTOM of the "V")

    //Note that there are more elegant ways of doing this. You can always use trignometry to do so
    //but for the sake of this example, this should yield fairly good results.

    Vector3 bottomCornerPoint = new Vector3( (firstTouchInitPos.x + secondTouchInitPos.x) * 0.5f, 
                                            (firstTouchInitPos.y + secondTouchInitPos.y) * 0.5f );


    //Now that we have our bottom point, we then calculate the Vector between this common
    //bottom point, and the last touch point added to each list. From this point
    //I'll refer to these two Vectors as the ARMS of the "V" 
    Vector3 arm1 = new Vector3( firstTouches[firstTouches.Count - 1].position.x - bottomCornerPoint.x,
                               firstTouches[firstTouches.Count - 1].position.y - bottomCornerPoint.y );

    Vector3 arm2 = new Vector3( secondTouches[secondTouches.Count - 1].position.x - bottomCornerPoint.x,
                               secondTouches[secondTouches.Count - 1].position.y - bottomCornerPoint.y );


    //Now let's calculate the angle between the ARMS of the "V".
    //If the angle is too small (< 15 degrees), or too large (> 60 degrees), 
    //it's not really a "V", so we'll exit

    //Note: Vector2.Angle / Vector3.Angle perform a DOT product of the two vectors
    //Therefore in certain cases, you're going to get incorrect results.
    //TODO : If anyone can, please change the below to use a cross product
    //to calculate the angle between the Vectors.


    if(Vector3.Angle(arm1, arm2) < vAnglesMinMax.x ||
       Vector3.Angle(arm1, arm2) > vAnglesMinMax.y) {

        Debug.Log (string.Format("The angle was outside the allowed range! Angle {0}", 
                                 new object[] { Vector3.Angle(arm1, arm2) } ));
        return;
    }

    //If we reach this point, everything's great, we have a "V"!
    Debug.Log ("There's a V gesture here!");
}


}

代码背后的逻辑

我假设如下

  1. "V"总是用两根手指开始并拢,然后逐渐远离。
  2. 当手指彼此远离时,它们在一个轴上的移动方向相同,而在另一个轴上的移动方向相反。 (比如"V"画的跟字母一模一样,那么两根手指都往Y轴正方向移动,一根手指往X轴负方向移动,另一根手指往X轴正方向移动)
  3. 手指靠得比较近,所以可以取两点的平均值作为"V"两臂的交点。 (请阅读替代机制的代码)

在下面以图形方式表达我的想法的糟糕尝试