Drag multiple object using multitouch?

Hi guys, I have a problem when dragging several objects at the same time in 2D using multitouch. I need to colliding 2 object or more if they have same tag and if they aren’t, the dragged object will return to its original position. Count and phase is working, but still cant drag any object. Please help, stuck for 1 week. T_T

void Update()
{

    if (Input.touchCount > 0)
    {
        foreach (Touch t in Input.touches)
        {
            touches[t.fingerId] = Camera.main.ScreenToWorldPoint(Input.GetTouch(t.fingerId).position);
            Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch(t.fingerId).position);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit))
            {
                if (hit.transform.tag == "Draggable")
                {
                    if (Input.GetTouch(t.fingerId).phase == TouchPhase.Began)
                    {
                        startPos = hit.transform.position;
                    }
                    if (Input.GetTouch(t.fingerId).phase == TouchPhase.Moved)
                    {
                        hit.transform.position = Input.GetTouch(t.fingerId).position;
                    }
                    if (Input.GetTouch(t.fingerId).phase == TouchPhase.Ended)
                    {
                        hit.transform.position = startPos;
                    }
                }
            }
        }
    }

,

You are confusing things here a bit. Input.GetTouch takes an index not a fingerID. There is no direct way to query a touch for a certain fingerID. You usually just iterate through all touches and use the fingerID of the current touch in your logic.

Note that the fingerID does not change over the time as long as a touch is still tracked, So just imagine putting 5 fingers down one after another. So you will have 5 touches in your touches array (touchcount ==5). The fingerIDs would probably be also something like the numbers from 0 to 4 but they do not correspond to the index. If you now release the first 3 fingers and keep the last to the touch count goes down to 2 and the only valid indices for GetTouch are 0 and 1. However the fingerIDs of those touches are most likely 3 and 4 since, as I said, the fingerID doesn’t change as long as the touch is still tracked.

Your next issue might be your startPos variable. You only have a single startPos variable. If you want to be able to drag multiple objects at the same time you have to track this information for each object you’re dragging. Also it usually makes more sense to store the object you drag around along with the corresponding fingerID and startpos in a custom class / struct.

The next issue is you set the dragged objects position to the hit position. However since the hit position is on the surface of the collider of your object the object most likely would move towards the camera until it wents through the near clipping plane, You should seperate the actual picking of the object (TouchPhase.Began) from the actual dragging (TouchPhase.Moved). Touch movement on a 2d screen is of course just a 2d motion. So to translate that properly into 3d there are generally two ways:

  • Either calculate the distance of the object from the camera when you start dragging and place the object simply at that distance along the Ray (Ray.GetPoint(dist)). That way you would drag your object on a spherical surface around your camera origin since we will always have this distance from the camera. So when the touch point moves we describe an angle in worldspace
  • The second more common solution is to use a “drag plane” which we place at the oritinal objects position and use that to determine the actual position where the object should be placed.

Something like this:

public class DragObject
{
    public Transform obj;
    public int fingerID;
    public Vector3 dragOffset;
    public Vector3 startPos;
    public Plane dragPlane;
}
public Dictionary<int, DragObject> draggedObjects = new Dictionary<int, DragObject>();
private void Update()
{
    foreach (Touch t in Input.touches)
    {
        Ray ray = Camera.main.ScreenPointToRay(t.position);
        if (t.phase == TouchPhase.Began)
        {
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit) && hit.transform.CompareTag("Draggable"))
            {
                DragObject dragO;
                if (!draggedObjects.TryGetValue(t.fingerId, out dragO))
                {
                    dragO = new DragObject();
                    draggedObjects.Add(dragO.fingerID, dragO);
                }
                dragO.fingerID = t.fingerId;
                dragO.obj = hit.transform;
                dragO.startPos = hit.transform.position;
                dragO.dragPlane = new Plane(-Camera.main.transform.forward, dragO.startPos);

                float dist;
                dragO.dragPlane.Raycast(ray, out dist);
                dragO.dragOffset = dragO.startPos - ray.GetPoint(dist);
            }
        }
        else if (t.phase == TouchPhase.Moved)
        {
            DragObject dragO;
            if (draggedObjects.TryGetValue(t.fingerId, out dragO))
            {
                if (dragO.obj == null)
                {
                    // object has been destroyed, so remove this DragObject and continue
                    draggedObjects.Remove(t.fingerId);
                    continue;
                }
                float dist;
                dragO.dragPlane.Raycast(ray, out dist);
                dragO.obj.position = ray.GetPoint(dist) + dragO.dragOffset;
            }
        }
        else if (t.phase == TouchPhase.Ended || t.phase == TouchPhase.Canceled)
        {
            DragObject dragO;
            if (draggedObjects.TryGetValue(t.fingerId, out dragO))
            {
                if (dragO.obj == null)
                {
                    dragO.obj.position = dragO.startPos;
                }
                draggedObjects.Remove(t.fingerId);
            }
        }
    }
}

Some notes:
When you touch with a finger it casts a ray against the objects in the scene to determine which object you hit. If the object we hit can be dragged we create / update the DragObject instance for the current fingerID. We of course save the actual object reference of the object we’ve hit (obj) but also the fingerID and the startpos of this object. In addition we setup a plane perpendicular to the camera plane which we will use to drag the object around. We also calculate the drag offset since we most likely did not touch the object at its origin. This offset will be used when we update the objects position.

Whenever a finger is moved we check our dictionary if the fingerID actually has a DragObject. If we have we simply update the objects position according to the hit point on the drag plane + our drag offset

When the touch finishes (either “Ended” or “Cancelled”) we do move the object back to the initial start position of this object and also remove the object from our dictionary since the touch no longer exists from now on.

That’s all what is happening here. Of course at the moment the object will always return back to the starting point once you release the object. If you don’t want that you have to remove the line

dragO.obj.position = dragO.startPos;

from the last case.