multi-touch woe, index out of bounds?

I’m making my first attempt at multi-touch controls in Unity, right now I’ve made a small test scene with a cube that moves according to X/Y values taken from a touch input in the bottom left corner of the screen.

My problem is hard to put into words, but I’ll try anyway:

Placing one finger on the control pad works fine, and the object moves. Any other fingers are ignored.

Placing a finger outside the control does nothing, and while holding that finger down touching the control pad with another works fine as well.

The problem occurs when I have two fingers placed on the screen like that, whenever I lift the finger that came first (outside the box) there is an immediate Unityexception: Index out of bounds error, and the fingerID of the touch I want to track for movement gets ‘stuck’ and won’t respond without resetting the scene or pressing the screen with multiple fingers repeatedly.

Here’s the code I’m using:

using UnityEngine;
using System.Collections;

public class tushcontrl : MonoBehaviour {
public GameObject obj1;	
public GameObject obj2;		
private float relativeX;
private float relativeY;
private float rotateX;
private float rotateY;
public int leftControlTouchID;
public int rightControlTouchID;
public int touchCounter;

	void Start () {
	leftControlTouchID = -1;
	rightControlTouchID = -1;
	}
	
	void Update () {
		
		touchCounter=Input.touchCount;
		
		//Loop through all touches
		foreach (Touch touch in Input.touches){
			Debug.Log(touch.fingerId + " " + touch.position);
			
				//Assignment for Left
				if(touch.phase == TouchPhase.Began){
					if(touch.position.x<Screen.height/2&&touch.position.y<Screen.height/2&&leftControlTouchID<0){
					leftControlTouchID = touch.fingerId;}
				}
			
				//Do something with it
				if(touch.fingerId==leftControlTouchID){
				relativeX = Mathf.Clamp(Input.GetTouch(leftControlTouchID).position.x/Screen.height*4 - 1,-1.0f,1.0f);
				relativeY = Mathf.Clamp(Input.GetTouch(leftControlTouchID).position.y/Screen.height*4 - 1,-1.0f,1.0f);
				obj1.transform.position = obj1.transform.position + 0.5f*new Vector3(relativeX,0,relativeY);
				}
			
				//Removal when touch phase ends
				if (touch.phase == TouchPhase.Ended){
					if(touch.fingerId==leftControlTouchID){
						leftControlTouchID=-1;}
				}
			}
		}
		
		void OnGUI () {
			
			GUI.Box(new Rect(0,Screen.height/2,Screen.height/2,Screen.height/2),"");	
			GUI.Box(new Rect(Screen.width-Screen.height/2,Screen.height/2,Screen.height/2,Screen.height/2),"");
				
			foreach (Touch touch in Input.touches){
			GUI.Box(new Rect(touch.position.x-25,Screen.height-touch.position.y-25,50,50),touch.fingerId.ToString());	
			
		}
		
	
	}
}

I can’t work out a way to avoid going out of the touch array range, and this issue is preventing me from making a functional ‘two-joystick’ style control setup. :frowning:

relativeX = Mathf.Clamp(Input.GetTouch(leftControlTouchID).position.x/Screen.height*4 - 1,-1.0f,1.0f);

These lines are your problem, Input.GetTouch() takes the index of the touch not the touch ID, so when your finger of touchID 0 and index 0 leaves your finger of touchID 1 and index 1 now turns to touchID 1 and index0 causing an exception.

I’m gonna include my similar script that has seemed to work for me so far on my twin stick shooter if you want some reference

void Update () {
#if UNITY_IPHONE && !UNITY_EDITOR
		moving = false;
		for(int i = 0; i < Input.touches.Length; i++)
		{
			
			Vector2 pos = new Vector2(Input.touches_.position.x, sh - Input.touches*.position.y);*_

_ if(Input.touches*.phase == TouchPhase.Began)
{
if(!GUIManager.ButtonPress(pos))
{
if(pos.x > sw * .5f)
{
aimFinger = Input.touches.fingerId;
StartCoroutine(FadeIndicator(false, aimGUI));
aimIndicator.renderer.enabled = true;
if(Vector2.Distance(pos, aimControlPos) > fingerDistFromControl)
{
aimControlPos = pos;
UpdateGUI(aimGUI, aimControlPos);*_

* }*
* }*
* else*
* {*
_ moveFinger = Input.touches*.fingerId;
StartCoroutine(FadeIndicator(false, moveGUI));
moveIndicator.renderer.enabled = true;
if(Vector2.Distance(pos, moveControlPos) > fingerDistFromControl)
{
moveControlPos = pos;
UpdateGUI(moveGUI, moveControlPos);
}
}
}*_

* }*
_ else if(Input.touches*.phase != TouchPhase.Ended)
{
if(Input.touches.fingerId == moveFinger && Vector2.Distance(pos, moveControlPos) > deadZone)
{
UpdateIndicator(pos, moveControlPos, moveIndicator.transform);
EventManager.instance.QueueEvent(new MoveEvent(moveControlPos - pos));
moving = true;
}
else if(Input.touches.fingerId == aimFinger && Vector2.Distance(pos, aimControlPos) > deadZone)
{
UpdateIndicator(pos, aimControlPos, aimIndicator.transform);
EventManager.instance.QueueEvent(new AimEvent(aimControlPos - pos));
}*_

* }*
* else*
* {*
_ if(Input.touches*.fingerId == aimFinger)
{
aimFinger = -1;
StartCoroutine(FadeIndicator(true, aimGUI));
aimIndicator.renderer.enabled = false;
WeaponsManager.instance.StopFireEffect();
}
else if(Input.touches.fingerId == moveFinger)
{
StartCoroutine(FadeIndicator(true, moveGUI));
moveIndicator.renderer.enabled = false;
moveFinger = -1;
}
}
}
}*_