Search Unity

Windows [PC] touch

Discussion in 'Scripting' started by eXsiLe, Oct 1, 2015.

  1. eXsiLe

    eXsiLe

    Joined:
    Oct 1, 2015
    Posts:
    4
    Hello everyone,

    I'm currently trying to make my game a bit more intuitive and therefore wanted to add the ability to use it via touch input. I have a very basic camera orbit script, with which you can turn the camera around a specific object. Now that I had mouse controls going, I wanted to add the touch component and went for the pinch-zoom first: (Note: All controls are in LateUpdate() )

    Code (CSharp):
    1. if (Input.touchCount == 2)
    2.         {
    3.             touchCount.text = "Touches: 2 - zoom";
    4.             Touch touchZero = Input.GetTouch(0);
    5.             Touch touchOne = Input.GetTouch(1);
    6.          
    7.             Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
    8.             Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;
    9.          
    10.             float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
    11.             float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;
    12.          
    13.             float deltaMagnitudeDiff = prevTouchDeltaMag - touchDeltaMag;
    14.          
    15.             desiredDistance += deltaMagnitudeDiff * 0.01f * Mathf.Abs (desiredDistance);
    All in all, this is pretty basic and made with the help of the docu from Unity, and it works really well. (Note that touchCount is a text label in my UI just for developing reasons)

    Anyway, as I tried to add touch movement, I was getting problems, like really weird problems. First, I tried to adapt my new wisdom of touch input in Unity and tried this:

    Code (CSharp):
    1. touchCount.text = "Touches: 1 - position";
    2.             Touch myTouch = Input.GetTouch(0);
    3.  
    4.             Vector2 myTouchMove = myTouch.position - myTouch.deltaPosition;
    5.  
    6.             xDeg += myTouchMove.x * xSpeed;
    7.             yDeg -= myTouchMove.y * xSpeed;
    8.             yDeg = ClampAngle(yDeg, yMinLimit, yMaxLimit);
    9.  
    10.             desiredRotation = Quaternion.Euler(yDeg, xDeg, 0);
    11.             currentRotation = transform.rotation;
    12.             rotation = Quaternion.Lerp(currentRotation, desiredRotation, 0.02f * zoomDampening);
    13.             transform.rotation = rotation;
    But touch wasn't working until I deactivated the zooming part in my script.

    It should make the exact same thing as with the zooming part, but it doesn't. I can see, thanks to the touchCount output, that the touch isn't really recognized as it is with the zooming part. Instead of updating every frame, it only switches from 0 touches to 1 touch when I release my finger from the touchscreen, and then it makes a huge jump. At first I thought this jump could be a matter of adjusting parameters for the lenght of my direction vector, but it wasn't since the movement of my finger does not have any influence on the direction and the lenght of the jump. In fact, a single tap lets the camera jump around in different directions, and as far as it wants.

    I also tried another suggested method with TouchPhases: (firstpoint and lastpoint are declared outside the function)

    Code (CSharp):
    1. if(Input.GetTouch(0).phase == TouchPhase.Began){
    2.                 firstpoint = Input.GetTouch(0).position;
    3.             }
    4.             if(Input.GetTouch(0).phase == TouchPhase.Moved){
    5.                 secondpoint = Input.GetTouch(0).position;
    6.                 xDeg += firstpoint.x - secondpoint.x;
    7.                 yDeg -= firstpoint.y - secondpoint.y;
    8.                 yDeg = ClampAngle(yDeg, yMinLimit, yMaxLimit);
    9.  
    10.                 desiredRotation = Quaternion.Euler (yDeg, xDeg, 0);
    11.                 currentRotation = transform.rotation;
    12.                 rotation = Quaternion.Lerp (currentRotation, desiredRotation, 0.02f * zoomDampening);
    13.                 transform.rotation = rotation;
    But it wouldn't work either.

    I googled for any problems with touch and Windows and found many topics, but most of them said that multitouch wouldn't work with Unity (3.x and 4.x mainly), but in Unity 5 it should do. Well, I obviously don't have a problem with multitouch, more with single touch!

    Does anyone know, why Unity detects two touch positions without a problem, but one touch position (or deltaPosition) causes so many problems?

    I am aware of the fact that it also can be a rookie-mistake by myself, so please let me know! ;)

    EDIT: Totally forgot: Unity Version 5.1.2f1 Personal, Windows 8.1 with touchscreen display (Acer, as far as I know and if this makes any difference)

    Greetings!
     
  2. martinmr

    martinmr

    Joined:
    Mar 25, 2015
    Posts:
    325
    As far as i know for Smartphones and Touch you need to transform the touch position(which is in screen space) into world space so you can use it in unity

    http://docs.unity3d.com/ScriptReference/Camera.ScreenToWorldPoint.html

    Code (CSharp):
    1.  
    2. //get your camera here:
    3. Camera camera = GetComponent<Camera>(); // for example!
    4.  
    5. firstpoint = Input.GetTouch(0).position;
    6. firstpoint = camera.ScreenToWorldPoint(firstpoint.x, firstpoint.y, camera.nearClipPlane);
    7.  
    8.  
    9. something like that
     
  3. eXsiLe

    eXsiLe

    Joined:
    Oct 1, 2015
    Posts:
    4
    Thanks for that hint, martinmr!

    I tried it with this code:
    Code (CSharp):
    1.  
    2. else if (Input.touchCount == 1)
    3.         {
    4.             touchCount.text = "Touches: 1 - position";
    5. if(Input.GetTouch(0).phase == TouchPhase.Began)
    6.             {
    7.                 touchCount.text = "Touches: 3 - active Touchphase";
    8.             }
    9.             else if(Input.GetTouch(0).phase == TouchPhase.Moved)
    10.             {
    11.                 touchCount.text = "Touches: 3 - moved Touch";
    12.             }
    13.          
    14.             Camera camera = GetComponent<Camera>();
    15.             if(Input.GetTouch(0).phase == TouchPhase.Began){
    16.                 firstpoint = Input.GetTouch(0).position;
    17.                 firstpoint = camera.ScreenToWorldPoint(firstpoint);
    18.             }
    19.             if(Input.GetTouch(0).phase == TouchPhase.Moved){
    20.                 secondpoint = Input.GetTouch(0).position;
    21.                 secondpoint = camera.ScreenToWorldPoint(secondpoint);
    22.                 xDeg += firstpoint.x - secondpoint.x;
    23.                 yDeg -= firstpoint.y - secondpoint.y;
    24.                 yDeg = ClampAngle(yDeg, yMinLimit, yMaxLimit);
    25.  
    26.                 desiredRotation = Quaternion.Euler (yDeg, xDeg, 0);
    27.                 currentRotation = transform.rotation;
    28.                 rotation = Quaternion.Lerp (currentRotation, desiredRotation, 0.02f * zoomDampening);
    29.                 transform.rotation = rotation;
    30.                 idleTimer = 0;
    31.                 idleSmooth = 0;
    32.             }
    33. }
    Sadly, it wouldn't work either. Unity does not seem to get the movement of my finger on the screen right. I build these new if clauses to check whether only one touch (so the point of the touch/the position) is recognized or the whole movement. Unfortunatley, when I move my finger on screen, it keeps on 0 (no touch detected) and when I lift my finger up from the screen, it jumps over to 1-position for one frame.

    The question I'm asking to myself is: Why does Unity register two touches even as phase/movement, but not a single touch action?

    I appreciate all help! :)
     
  4. martinmr

    martinmr

    Joined:
    Mar 25, 2015
    Posts:
    325
    what you shouldn't forget is to set firstpoint to secondpoint. in the code you postet after line 31 there should be
    firstpoint = secondpoint;
    so you will get the delta movement between frames and not from the point you started your movement there you will get wrong angels
     
  5. martinmr

    martinmr

    Joined:
    Mar 25, 2015
    Posts:
    325
    I tried a little script, rotating a camera around a cube. maybe this will help you.

    Code (CSharp):
    1. public class TestRotateCameraAround : MonoBehaviour {
    2.  
    3.     [SerializeField]
    4.     GameObject gameObjectRotateAround;
    5.  
    6.     [SerializeField]
    7.     Text text;
    8.  
    9.     Vector3 startPoint;
    10.     Vector3 tempPoint;
    11.  
    12.     float xDif;
    13.     float yDif;
    14.     float rotationSpeed = 3f;
    15.  
    16.     void Update() {
    17.         if (Input.touchCount != 0) {
    18.             if (Input.GetTouch(0).phase == TouchPhase.Began) {
    19.                 startPoint = GetScreenToWorldPoint(Input.GetTouch(0).position);
    20.             }
    21.             if (Input.GetTouch(0).phase == TouchPhase.Moved) {
    22.                 tempPoint = GetScreenToWorldPoint(Input.GetTouch(0).position);
    23.  
    24.                 yDif = startPoint.x - tempPoint.x;
    25.                 xDif = startPoint.y - tempPoint.y;
    26.  
    27.                 transform.position = RotatePointAroundPivot(transform.position, gameObjectRotateAround.transform.position, new Vector3(-xDif, yDif, 0f) * rotationSpeed);
    28.                 transform.LookAt(gameObjectRotateAround.transform.position);              
    29.                 startPoint = tempPoint;
    30.  
    31.                 text.text = "CameraRotation: " + transform.rotation.eulerAngles;
    32.             }
    33.         }
    34.     }
    35.  
    36.     private Vector3 GetScreenToWorldPoint(Vector3 _touchPos) {
    37.         return Camera.main.ScreenToWorldPoint(new Vector3(_touchPos.x, _touchPos.y, -Camera.main.transform.position.z));
    38.     }
    39.  
    40.     Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Vector3 angles) {
    41.         Vector3 dir = point - pivot; // get point direction relative to pivot
    42.         dir = Quaternion.Euler(angles) * dir; // rotate it
    43.         point = dir + pivot; // calculate rotated point
    44.         return point; // return it
    45.     }
    46. }
    the rotate around pivot i found with google -> http://answers.unity3d.com/questions/532297/rotate-a-vector-around-a-certain-point.html

    the text field you can ignore. also with to high "rotationSpeed" (at 5f) also something is messed up :/ but havent the time to fully debug everything.
     
    eXsiLe likes this.
  6. eXsiLe

    eXsiLe

    Joined:
    Oct 1, 2015
    Posts:
    4
    Hi again,

    thank you very much for that example! I got it going now, but it is still not very user friendly. Apart from adjustment, it seems that with the new code, every point on the screen is a exact point for the camera to be. This means that when I touch my screen in the middle, the camera centers in front of my object, if I move my finger to the left end of the screen, the camera moves. So far so good. But when I try to get more movement by setting my finger back on the center of the screen, the camera automatically switches back to the center in front of my object.

    I just increased the movement speed parameter so less finger movement equals more movement of the camera and, as you already said, the camera begins to jump at a certain point of movement on the screen (as I set it up here, at about 0.75/0.25). This is the code I use right now (slightly adapted, thank you very much @martinmr):

    Code (csharp):
    1.  
    2. void LateUpdate()
    3. {
    4. if(Input.touchCount == 1){
    5.             if(Input.GetTouch(0).phase == TouchPhase.Began)
    6.             {
    7.                 firstpoint = GetScreenToWorldPoint(Input.GetTouch(0).position);
    8.             }
    9.             if(Input.GetTouch(0).phase == TouchPhase.Moved)
    10.             {
    11.                 secondpoint = GetScreenToWorldPoint(Input.GetTouch(0).position);
    12.  
    13.                 xDeg += (secondpoint.x - firstpoint.x) * xSpeed * 0.02f;
    14.                 yDeg -= (secondpoint.y - firstpoint.y) * ySpeed * 0.02f;
    15.                 yDeg = ClampAngle(yDeg, yMinLimit, yMaxLimit);
    16.              
    17.                 desiredRotation = Quaternion.Euler(yDeg, xDeg, 0);
    18.                 currentRotation = transform.rotation;
    19.                 rotation = Quaternion.Lerp(currentRotation, desiredRotation, 0.02f  * zoomDampening);
    20.                 transform.rotation = rotation;
    21.                 idleTimer = 0;
    22.                 idleSmooth = 0;
    23.  
    24.             }
    25.         }
    26.         else if (Input.GetMouseButton(0))
    27.         {
    28.             xDeg += Input.GetAxis("Mouse X") * xSpeed * 0.02f;
    29.             yDeg -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
    30.             yDeg = ClampAngle(yDeg, yMinLimit, yMaxLimit);
    31.          
    32.             desiredRotation = Quaternion.Euler(yDeg, xDeg, 0);
    33.             currentRotation = transform.rotation;
    34.               rotation = Quaternion.Lerp(currentRotation, desiredRotation, 0.02f  * zoomDampening);
    35.             transform.rotation = rotation;
    36.             idleTimer=0;
    37.             idleSmooth=0;
    38.         }
    39. }
    40.  
    41. private Vector3 GetScreenToWorldPoint(Vector3 _touchPos)
    42.     {
    43.         return Camera.main.ScreenToWorldPoint (new Vector3 (_touchPos.x, _touchPos.y, -Camera.main.transform.position.z));
    44.     }
    45.  
    I put the code for mouse input with in, as you can see, I build it very similar. And, if I go for mouse input, it works correctly, so the point im clicking is the actual point the camera is at the moment. Any idea why this doesn't work with touch input?

    I start to think that I missed something with touch input. Why is it so different than a normal click and drag with the mouse?

    Greetings and thanks for all the help!

    EDIT: I managed to get a somehow smoth movement if you don't go to fast. Anyway, I can not get a full turn around the object. E.g. there is a car in a showroom and i want to turn the camera around it, but it only goes around the front side so the back side of the car is never shown... Any ideas what could possibly cause this?
     
    Last edited: Oct 2, 2015
  7. eXsiLe

    eXsiLe

    Joined:
    Oct 1, 2015
    Posts:
    4
    Finally! I got it going!

    It turned out that you don't need to convert the screen points into world points to use them in Unity. However, I'm also not developing for mobile devices at the moment (as @martinmr said you would need to convert for Smartphones), but for Windows Desktop with Touch Input.

    So for everyone trying to figure out how to create an orbit camera touch script, this is one way how to do it:

    Code (CSharp):
    1. void LateUpdate()
    2.     {
    3.         if (Input.touchCount == 2) // pinch zoom
    4.         {
    5.             Touch touchZero = Input.GetTouch(0);
    6.             Touch touchOne = Input.GetTouch(1);
    7.  
    8.             Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
    9.             Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;
    10.  
    11.             float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
    12.             float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;
    13.  
    14.             float deltaMagnitudeDiff = prevTouchDeltaMag - touchDeltaMag;
    15.  
    16.             desiredDistance += deltaMagnitudeDiff * 0.01f * Mathf.Abs (desiredDistance);
    17.             idleTimer = 0;
    18.             idleSmooth = 0;
    19.         }
    20.         else if(Input.touchCount == 1) // touch rotation
    21.         {
    22.             if(Input.GetTouch(0).phase == TouchPhase.Began)
    23.             {
    24.                 firstpoint = Input.GetTouch(0).position;
    25.             }
    26.             if(Input.GetTouch(0).phase == TouchPhase.Moved)
    27.             {
    28.                 secondpoint = Input.GetTouch(0).position;
    29.  
    30.                 xDeg += (secondpoint.x - firstpoint.x) * 0.08f * xSpeed * 0.01f;
    31.                 yDeg -= (secondpoint.y - firstpoint.y) * 0.08f * ySpeed * 0.01f;
    32.                 yDeg = ClampAngle(yDeg, yMinLimit, yMaxLimit);
    33.                
    34.                 desiredRotation = Quaternion.Euler(yDeg, xDeg, 0);
    35.                 currentRotation = transform.rotation;
    36.                 rotation = Quaternion.Lerp(currentRotation, desiredRotation, 0.02f  * zoomDampening);
    37.                 transform.rotation = rotation;
    38.  
    39.                 firstpoint = secondpoint;
    40.  
    41.                 idleTimer = 0;
    42.                 idleSmooth = 0;
    43.  
    44.             }
    45.         }
    46. }
    47.  
    I'd like to thank you once again, martinmr, for giving me the right hints so I could finally adapt and develop my code to get this working! Nice help, very glad, such wow :)
     
    martinmr likes this.
  8. Kidodelog

    Kidodelog

    Joined:
    Mar 4, 2015
    Posts:
    5
    Hi @eXsiLe , is it working on windows standalone(.exe)?