x


C# script for unity camera. Detect difference between two finger swipe and pinch to zoom?

I have a problem with my current code, it works to detect the zoom, and i think my code detects the difference between rotation and pinch, but if i put two fingers down and swipe it still zooms. How do i detect this and stop it? I also have to accomodate for users being sloppy when pinching and zoom and move fingers sideways a little bit (probably defining a variable that can be changed)

Here is my current code:

using UnityEngine;
using System.Collections;

public class CameraZoomPinch : MonoBehaviour 
{
    public int speed = 4;
    public Camera selectedCamera;
    public float MINSCALE = 2.0F;
    public float MAXSCALE = 5.0F;
    public float minPinchSpeed = 5.0F;
    public float varianceInDistances = 5.0F;
    private float touchDelta = 0.0F;
    private Vector2 prevDist = new Vector2(0,0);
    private Vector2 curDist = new Vector2(0,0);
    private float speedTouch0 = 0.0F;
    private float speedTouch1 = 0.0F;
    private float startAngleBetweenTouches = 0.0F;
    private int vertOrHorzOrientation = 0; //this tells if the two fingers to each other are oriented horizontally or vertically, 1 for vertical and -1 for horizontal
    private Vector2 midPoint = new Vector2(0,0); //store and use midpoint to check if fingers exceed a limit defined by midpoint for oriantation of fingers
    // Use this for initialization
    void Start () 
    {

    }

    // Update is called once per frame
    void Update () 
    {
        if (Input.touchCount == 2 && Input.GetTouch(0).phase == TouchPhase.Began && Input.GetTouch(1).phase == TouchPhase.Began) 
        {
            startAngleBetweenTouches = Vector2.Angle(Input.GetTouch(0).position, Input.GetTouch(1).position); //to determine that gesture isnt a rotation
            midPoint = new Vector2((Input.GetTouch(0).position.x - Input.GetTouch(1).position.x), (Input.GetTouch(0).position.y - Input.GetTouch(1).position.y)); //store midpoint from first touches

            if ((Input.GetTouch(0).position.x - Input.GetTouch(1).position.x) > (Input.GetTouch(0).position.y - Input.GetTouch(1).position.y))
            {
                vertOrHorzOrientation = -1;
            }
            if ((Input.GetTouch(0).position.x - Input.GetTouch(1).position.x) < (Input.GetTouch(0).position.y - Input.GetTouch(1).position.y))
            {
                vertOrHorzOrientation = 1;
            }
        }
        if (Input.touchCount == 2 && Input.GetTouch(0).phase == TouchPhase.Moved && Input.GetTouch(1).phase == TouchPhase.Moved) 
        {

            curDist = Input.GetTouch(0).position - Input.GetTouch(1).position; //current distance between finger touches
            prevDist = ((Input.GetTouch(0).position - Input.GetTouch(0).deltaPosition) - (Input.GetTouch(1).position - Input.GetTouch(1).deltaPosition)); //difference in previous locations using delta positions
            touchDelta = curDist.magnitude - prevDist.magnitude;
            speedTouch0 = Input.GetTouch(0).deltaPosition.magnitude / Time.deltaTime;
            speedTouch1 = Input.GetTouch(1).deltaPosition.magnitude / Time.deltaTime;


            //(curDist < prevDist --- inward pinch) && (if touch0 is fast enough) && (if touch1 is fast enough) && (angle of two touches-using vector2.angle-is less than an angle limit to make sure pinch isnt a rotation)
            if ((touchDelta < 1) && (speedTouch0 > minPinchSpeed) && (speedTouch1 > minPinchSpeed) && (Vector2.Angle(Input.GetTouch(0).position, Input.GetTouch(1).position) < (startAngleBetweenTouches+10)) && (Vector2.Angle(Input.GetTouch(0).position, Input.GetTouch(1).position) > (startAngleBetweenTouches-10))) //
            {
                //(if the two touches are oriented vertically) && (if touch0 x is less than right side of midpoint x) or (if touch0 x is greater than left of midpoint x) && (if touch1 x is less than right side of midpoint x) or (if touch1 x is greater than left of midpoint x) (all values are assuming left and right variances of 20 pixels)
                if ((vertOrHorzOrientation == 1) && ((Input.GetTouch(0).position.x < midPoint.x + 20) || (Input.GetTouch(0).position.x > midPoint.x -20)) && ((Input.GetTouch(1).position.x < midPoint.x + 20) || (Input.GetTouch(1).position.x > midPoint.x -20)))
                {
                    selectedCamera.fieldOfView = Mathf.Clamp(selectedCamera.fieldOfView + (1 * speed),15,90);
                }
                //(same as above, but these checks are for horizontal orientation of touches and the y components are checked for limiting the direction)
                 if ((vertOrHorzOrientation == -1) && ((Input.GetTouch(0).position.y < midPoint.y + 20) || (Input.GetTouch(0).position.y > midPoint.y -20)) && ((Input.GetTouch(1).position.y < midPoint.y + 20) || (Input.GetTouch(1).position.y > midPoint.y -20)))
                {
                    selectedCamera.fieldOfView = Mathf.Clamp(selectedCamera.fieldOfView + (1 * speed),15,90);
                }
            }

            if ((touchDelta > 1) && (speedTouch0 > minPinchSpeed) && (speedTouch1 > minPinchSpeed) && (Vector2.Angle(Input.GetTouch(0).position, Input.GetTouch(1).position) < (startAngleBetweenTouches+10)) && (Vector2.Angle(Input.GetTouch(0).position, Input.GetTouch(1).position) > (startAngleBetweenTouches-10)))
            {
                                if ((vertOrHorzOrientation == 1) && ((Input.GetTouch(0).position.x < midPoint.x + 20) || (Input.GetTouch(0).position.x > midPoint.x -20)) && ((Input.GetTouch(1).position.x < midPoint.x + 20) || (Input.GetTouch(1).position.x > midPoint.x -20)))
                                {
                                    selectedCamera.fieldOfView = Mathf.Clamp(selectedCamera.fieldOfView - (1 * speed),15,90);
                                }

                                 if ((vertOrHorzOrientation == -1) && ((Input.GetTouch(0).position.y < midPoint.y + 20) || (Input.GetTouch(0).position.y > midPoint.y -20)) && ((Input.GetTouch(1).position.y < midPoint.y + 20) || (Input.GetTouch(1).position.y > midPoint.y -20)))
                                {
                                    selectedCamera.fieldOfView = Mathf.Clamp(selectedCamera.fieldOfView - (1 * speed),15,90);
                                }
            }

        }       
    }

}

How do i edit this to detect if a user swipe with two fingers and pinches to zoom? (i also have to take into account a user can have the pinch horizontal, vertical, or a combo of the two diagonally which i tried doing with the midpoint code.

more ▼

asked Nov 05, 2011 at 09:11 PM

1337GameDev gravatar image

1337GameDev
735 163 134 146

Does somebody know this answer? This is covered all the time by people's projects, but why doesn't NOBODY want to give insight into how THEY did it? I have spent over 2 weeks on this code and concept....

Nov 20, 2011 at 01:54 AM 1337GameDev

Dude, chill. Try reducing your question. Picture yourself trying to answer a question this big. Go to http://stackexchange.com/sites pick your favorite topic, one you're an expert on, and try to answer the biggest questions there. At least 100 lines like yours, with something dense to read. Code reviews are always a pain to do, specially when the code is this big and unfiltered.

Nov 21, 2011 at 07:15 PM Cawas

ok ill structure my questions better and break them down. Im just getting used to this being viewed and posting using my galaxy tab10. 1. The main question is defined in the title tho. I just need a way to discern pinching from two finger swiping, as my code zooms if i swipe (obviously im not pinching to zoom and other apps supports those gestures).

Nov 21, 2011 at 10:20 PM 1337GameDev

Actually, your other question on the same topic is much better already: http://answers.unity3d.com/questions/182757/two-finger-swipe-vs-pinch-and-zoom-problem.html which is indeed much more a question about logic than Unity3D or C#.

Nov 22, 2011 at 12:44 PM Cawas
(comments are locked)
10|3000 characters needed characters left

2 answers: sort voted first

first, whining never helps, it won't get you pity or sympathy. You're asking for help from people who have no reason to help you and do not stand to benefit in any way from doing so; you're entitled to nothing. Remember that.

That said, I'll attempt to help anyway this time, despite your attitude, because I do understand your frustration.

This is basically a variation of the standard mouse dragging vs clicking problem, and the same solution ought to work. What you need is some state information.

in Idle state:

check for a two-finger touch. If you get one, remember the finger positions and calculate and store the distance between them, and change state to TwoFingerTouch.

in TwoFingerTouch state:

If one or both touches have ended: compare their final positions to their originals. If they've both moved in the same general direction, and moved far enough to qualify as a swipe, ... do a swipe, and go back to the Idle state. If they moved different directions, or not far enough, go back to Idle state.

If both touches are still there, calc the current distance between them. If it's reduced enough, transition to PinchZoom state.

in PinchZoom state:

Calculate the new distance between the fingers, and apply zoom level based on this relative to original distance between them.

If one or both fingers released, return to Idle state.

state machine

As for how to implement this simple state machine, there are many ways, a search for State Machines will probably find you many examples. Here's one very simple way it could be done:

enum TouchState {
    Idle,
    TwoFingerTouch,
    PinchZoom,
};

var currentState:TouchState=Idle;

function Update()
{
    /*any pre-processing of input common to all states here */

    switch(idle)
    {
        case TouchState.Idle:
            /*Idle state code here*/
            break;

        case TouchState.TwoFingerTouch:
            /*Two finger touch state code here*/
            break;

        case TouchState.PinchZoom:
            /*PinchZoom state here*/
            break;
    }
}
more ▼

answered Nov 20, 2011 at 02:22 AM

WillTAtl gravatar image

WillTAtl
807 6 7 16

Btw the first statement makes all forum posters seem selfish and makes this system seem very one way, where ppl expect help but never give it. Sorry about "whining" but this question was up long enough for people to see and yet they chose not to respond to such an easy question, which kinda concerns me. And i wasnt lookkng for sympathy. Its a forum. Asking questions why others dont post is acceptabke (obviously to a point).

Second, i would prefer using code i have. If i wanted to use state machines i would have posted elsewhere and wouldnt have posted code. I also would have used playmaker rather than code the fsm in a script. How can i modify my code to not zoom while swiping with both fingers.

Nov 21, 2011 at 05:53 PM 1337GameDev

very simple reason people were ignoring the question. Try typing "pinch zoom," "swipe," or "flick" into the search box at the top, you get pages and pages of people asking this same question.

If the only acceptable answer is one that specifically involves someone fixing your specific code, then you're in the wrong place anyway. FAQ rules clearly state, answer must be reasonably interesting or useful to at least one person besides you; if the only answer you want is how to fix your specific code, your question doesn't meet that criteria.

Nov 21, 2011 at 05:56 PM WillTAtl

And also, to add thanks for helping. I just am curious how to do this with code i have rather than resort to FSM for everything, i have this coded this way otherwise it wont fit my OOD i wish to have.

Nov 21, 2011 at 05:59 PM 1337GameDev

Most people who i have searched use a similar code, so i assumed people would be able to help with the code that seems pretty common. Obiously it is interesting to others besides just me, or people wouldn't be posting the question using code similar to mine (and not using FSM's)

Nov 21, 2011 at 06:01 PM 1337GameDev

you don't need to rewrite the code to use enums or switches; you just need to add a minimal amount of state info.

Basically, what you're doing now is using a series of If(condition..) statements, where the condition tries to identify which state you're in for you. The problem with this is that it can and will detect pinch and flick in the same updates.

All you have to do is add a new variable, isPinching. When pinch is first detected, set isPinch to true, and when a pinch ends (one or both fingers releases), set it back to false. Then add !isPinch to the condition for swipe, so it will not swipe if it's detected a pinch. It's really not that different from what you're doing, but you absolutely are going to require at least this minimal amount of state information.

And I must say, despite what you think, using states will actually make it far easier to extend the code if you wanted to add more input types later, like three-finger swipes or two- or three-finger taps. The problem you're having is trying to explicitly and absolutely differentiate one from the other without any kind of state information. As illustrated by the other answer, this problem doesn't exist at all if you're only doing pinch, and it will get exponentially worse the more the more cases you add.

Nov 21, 2011 at 10:24 PM WillTAtl
(comments are locked)
10|3000 characters needed characters left

Since I'm not sure what your question is, I'll go with the title and give you my C# script that will detect pinch. This is extracted from a 342 lines of code bloated script, so I'm sorry if I'm missing something. Just let me know and I'll dig deeper to fix it:

using UnityEngine;
using System.Collections;

public class DetectPinch : MonoBehaviour {
    const float pinchRatio = 0.005f; // this is from trial and error
    const float minPinchDistance = 0;

    ///<summary>
    /// Only update after all character movement logic has been handled.
    ///</summary>
    void LateUpdate()
    {
        float pinchAmmount = 0f;

        // if two fingers are touching the screen at the same time and ...
        if (Input.touchCount == 2)
        {
            Touch touch1 = Input.touches[0];
            Touch touch2 = Input.touches[1];

            if (touch1.phase == TouchPhase.Moved || touch2.phase == TouchPhase.Moved)
            {
                // ... pinching, prepare ZOOM

                float pinchDistance = Vector2.Distance(touch1.position, touch2.position);
                float prevDistance = Vector2.Distance(touch1.position - touch1.deltaPosition,
                                                  touch2.position - touch2.deltaPosition);
                float pinchDistanceDelta = pinchDistance - prevDistance;
                if (Mathf.Abs(pinchDistanceDelta) > minPinchDistance)
                {
                    pinchAmmount = pinchDistanceDelta * pinchRatio; 
                }
            }
        }

        transform.position += Vector3.forward * pinchAmmount; // not so sure this will work
    }
}

Hope this is enough to help you!

more ▼

answered Nov 21, 2011 at 07:49 PM

Cawas gravatar image

Cawas
2.2k 111 102 121

This code just handles punching to zoom, but dies not take into account if i swipe with two fingers, same as my code. I do like that you calculated the distance between fingers and set the position of camera closer or further depending on that. My camera uses fov, because i am planning on having it rotate and want to avoid complex circle math, so i used fov of the camera and used mathf. Clamp to limit its value to a certain degree value. (fov is field of view, a property of camera object that uses degrees)

Nov 21, 2011 at 10:16 PM 1337GameDev

Yes, I did consider what you said about swiping. But if this is not enough that means I don't understand what you meant. In this code, if you put two fingers down and swipe, it won't do anything, as long as you swipe it in equal distance. And you can increase "minPinchDistance" to reduce sensibility of detecting the pinch. In other words, a higher value will require a higher distance between fingers to trigger the pinch. Here, if you can try it on android see if this is what you want: http://www.p3d.com.br/android

Nov 22, 2011 at 12:27 PM Cawas
(comments are locked)
10|3000 characters needed characters left
Your answer
toggle preview:

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Topics:

x9075
x155
x105
x15
x3

asked: Nov 05, 2011 at 09:11 PM

Seen: 6449 times

Last Updated: Nov 22, 2011 at 03:05 PM