Search code examples
c#androidunity-game-engineunity-webgl

Why is the orbital camera twitching from the phone?


I'm creating a game for webgl, the goal is to create an orbital camera for a phone, I managed to do it for computers, but I can't do it for a phone. I've been trying to do this for a few days now, and I'm still not getting it. I need your help. At the moment I have written a script, and there is 1 error in it that does not allow me to use it. Where I have the error: step 1. I press my finger on any element of the user interface and keep my finger pressed on it. Step 2. I press the other finger in the free zone and swipe my finger across the screen while still holding it. Step 3. I raise the first finger that was above the user interface and still hold the finger that was in the free zone. Stage 4. The camera jerks sharply 1 time. How to avoid this camera jump?

my script

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

public class OrbitCamera : MonoBehaviour
{
   
    public Transform target; 
    public float distance = 30; 
    public float sensitivityX = 4.0f;
    public float sensitivityY = 1.0f; 
    public float minYAngle = -80.0f; 
    public float maxYAngle = 80.0f; 

    private float rotationX = 0.0f;
    private float rotationY = 0.0f;

    public bool CanRotateCamera;
    public float smoothSpeed = 5f;
    private Dictionary<int, bool> fingerIdToFirstTouchOverUI = new Dictionary<int, bool>();

    private Touch _touchcurrent;
 

    private void Start()
    {
        CanRotateCamera = true;
        if (Application.isMobilePlatform)
        {
            Input.multiTouchEnabled = true;
            sensitivityY = 7f;
            sensitivityX = 7f;
        }
        else
        {
            Cursor.lockState = CursorLockMode.Locked;
            Cursor.visible = false;
        }
    }

    private void LateUpdate()
    {
        if (Application.isMobilePlatform)
        {
            HandleMobileInput();
        }
        else
        {
            HandlePCInput();
        }
    }

    private void HandleMobileInput()
    {
        if (CanRotateCamera)
        {
            bool isAnyTouchMoving = false;

            for (int i = 0; i < Input.touchCount; i++)
            {
                Touch touch = Input.GetTouch(i);

                if (touch.phase == TouchPhase.Began)
                {
                    fingerIdToFirstTouchOverUI[touch.fingerId] = IsTouchOverUI(touch.position);
                    if (fingerIdToFirstTouchOverUI[touch.fingerId])
                    {
                        continue;
                    }
                }

                if (touch.phase == TouchPhase.Moved && !fingerIdToFirstTouchOverUI[touch.fingerId])
                {
                    rotationY -= touch.deltaPosition.x * sensitivityX * Time.deltaTime;
                    rotationX += touch.deltaPosition.y * sensitivityY * Time.deltaTime;
                    rotationX = Mathf.Clamp(rotationX, minYAngle, maxYAngle);
                    isAnyTouchMoving = true;
                }
            }

            if (isAnyTouchMoving)
            {
                transform.rotation = Quaternion.Euler(rotationX, rotationY, 0);
            }

            Vector3 newPosition = target.position - transform.forward * distance;
            newPosition.y += 7.0f;
            transform.position = newPosition;
        }
    }
   

    private void HandlePCInput()
    {
        if (CanRotateCamera)
        {
            float mouseX = Input.GetAxis("Mouse X");
            float mouseY = Input.GetAxis("Mouse Y");

            rotationY += mouseX * sensitivityX;
            rotationY = Mathf.Repeat(rotationY, 360);

            rotationX -= mouseY * sensitivityY;
            rotationX = Mathf.Clamp(rotationX, minYAngle, maxYAngle);

            distance -= Input.GetAxis("Mouse ScrollWheel");
            distance = Mathf.Clamp(distance, 8.0f, 30.0f);

            transform.rotation = Quaternion.Euler(rotationX, rotationY, 0);

            Vector3 newPosition = target.position - transform.forward * distance;
            newPosition.y += 7.0f;
            transform.position = newPosition;
        }
    }





    private bool IsTouchOverUI(Vector2 touchPosition)
    {
        PointerEventData eventData = new PointerEventData(EventSystem.current);
        eventData.position = touchPosition;

        List<RaycastResult> results = new List<RaycastResult>();
        EventSystem.current.RaycastAll(eventData, results);

        return results.Any(result => result.gameObject.layer == LayerMask.NameToLayer("UI"));
    }
}

I have tried a large number of different options, but for many days in a row I have not been able to do anything, I am disappointed and go around in circles, I really need the help of people who understand how to implement it. I just want an orbital camera that will not interfere with the UI and it will all work in webgl from the phone.


Solution

  • I suspect that when you lift your first finger, the second finger then gets assigned as touchId 1 and you end up with a very large deltaPosition. Can you try just checking for and rejecting that touch?

    if (touch.phase == TouchPhase.Moved && !fingerIdToFirstTouchOverUI[touch.fingerId])
    {
        // check for large delta and skip this time
        // experiment with the value so that it doesn't miss certain fast swipes
        if (touch.deltaPosition.magnitude > 100)
        {
            continue; // skip to next for loop
        }
    
        // delta looks good, handle normally
        rotationY -= touch.deltaPosition.x * sensitivityX * Time.deltaTime;
        rotationX += touch.deltaPosition.y * sensitivityY * Time.deltaTime;
        rotationX = Mathf.Clamp(rotationX, minYAngle, maxYAngle);
        isAnyTouchMoving = true;
    }