Search code examples
javascriptc#unity-game-engineunity-webgl

Getting touchInput from a touch that starts outside of the Unity player


I have a Unity WebGL player that is embedded in a React application. The React application has drag and drop tiles that can be drug and dropped onto the WebGL player. When the tiles begin to be drug unity starts raycasting so that you can tell what object in the screen you are going to drop onto. All of this works perfectly when using a mouse, but I've noticed Input.touchCount always returns 0 unless the touch originates inside the WebGL player. Does anyone know a fix to this? Been head bashing this one for a moment now...

Here is the raycasting code. Like I said, it works perfectly for a mouse... but I cannot get a touch.position returned.

public void LateUpdate()
{
    if (SHOULD_CAST_RAY)
    {
        // always returning 0
        Debug.Log(Input.touchCount);

        RaycastHit hit;
        Vector3 position = Input.touchSupported
            && Input.touchCount == 1
                ? new Vector3(Input.GetTouch(0).position.x, Input.GetTouch(0).position.y, 0)
                : Input.mousePosition;

        if (Physics.Raycast(RigsCamera.ScreenPointToRay(position), out hit, CameraRigControllerScript.CameraDistanceMax * 1.5f, 1 << 10))
        {
            if (CURRENT_SELECTION == null)
            {
                CURRENT_SELECTION = UnsafeGetModelInstantiationFromRaycast(hit);
                ApplySelectionIndication();
            }
            else if (!IsAlreadySelected(hit))
            {
                RemoveSelectionIndication();
                CURRENT_SELECTION = UnsafeGetModelInstantiationFromRaycast(hit);
                ApplySelectionIndication();
            }
            return;
        }

        if (CURRENT_SELECTION != null)
        {
            RemoveSelectionIndication();
            CURRENT_SELECTION = null;
        }
    }
}

Also, if I touch the screen on the Unity WebGL player and then start dragging one of my React components which sends a message to Unity to start raycasting; I get atouch.position that is at the point I touched and does not move with the finger... The hell?


Solution

  • I posted a solution on the Unity forums in-case there is any update to this thread in the future.

    In WebGL, the Unity Input class does not register touch events that originate begin outside of the WebGL player. To solve this problem I used a couple boolean values that are toggled by my React components through the GameInstance.SendMessage method; as well as a Vector3 to store a value sent from React, also through SendMessage. Here are the important c# bits. If anyone has any questions, please ask and I'll walk you through the rest!

    bool SHOULD_CAST_RAY;
    bool USE_EXTERNAL_ORIGINATING_TOUCH_POS;
    Vector3 EXTERNAL_ORIGINATING_TOUCH_POS;
    
    public void LateUpdate()
    {
        if (SHOULD_CAST_RAY)
        {
            if (USE_EXTERNAL_ORIGINATING_TOUCH_POS && EXTERNAL_ORIGINATING_TOUCH_POS.z < 0) { return; }
    
            RaycastHit hit;
            Vector3 screenPoint = Input.mousePresent ? Input.mousePosition : Vector3.zero;
            Vector3 viewportPoint = USE_EXTERNAL_ORIGINATING_TOUCH_POS ? RigsCamera.ScreenToViewportPoint(EXTERNAL_ORIGINATING_TOUCH_POS) : Vector3.zero;
    
            if (Physics.Raycast(
                USE_EXTERNAL_ORIGINATING_TOUCH_POS
                    ? RigsCamera.ViewportPointToRay(new Vector3(viewportPoint.x, 1 - viewportPoint.y, 0))
                    : RigsCamera.ScreenPointToRay(screenPoint),
                out hit,
                CameraRigControllerScript.CameraDistanceMax * 1.5f,
                1 << 10
            )) {
                if (CURRENT_SELECTION == null)
                {
                    CURRENT_SELECTION = UnsafeGetModelInstantiationFromRaycast(hit);
                    ApplySelectionIndication();
                }
                else if (!IsAlreadySelected(hit))
                {
                    RemoveSelectionIndication();
                    CURRENT_SELECTION = UnsafeGetModelInstantiationFromRaycast(hit);
                    ApplySelectionIndication();
                }
                return;
            }
    
            if (CURRENT_SELECTION != null)
            {
                RemoveSelectionIndication();
                CURRENT_SELECTION = null;
            }
        }
    }
    
    // The below methods are used to control the raycasting from React through sendMessage
    public void ClearExternalOriginatingTouchPosition()
    {
        EXTERNAL_ORIGINATING_TOUCH_POS = new Vector3(0, 0, -1f);
        USE_EXTERNAL_ORIGINATING_TOUCH_POS = false;
    }
    
    public void DisableRaycasting()
    {
        SHOULD_CAST_RAY = false;
        RemoveSelectionIndication();
        CURRENT_SELECTION = null;
    }
    
    public void EnableRaycasting()
    {
        SHOULD_CAST_RAY = true;
    }
    
    public void SetExternalOriginatingTouchPosition(string csv)
    {
        string[] pos = csv.Split(',');
        EXTERNAL_ORIGINATING_TOUCH_POS = new Vector3(float.Parse(pos[0]), float.Parse(pos[1]), 0);
        USE_EXTERNAL_ORIGINATING_TOUCH_POS = true;
    }