Quick answer: Track each touch by its TouchID, not by touch index. Indices renumber as fingers lift; the ID is stable for the life of that touch.

A twin-stick mobile game uses touch 0 for the move stick and touch 1 for the aim stick. Lift the move finger and the aim stick suddenly jumps — the aim touch became index 0.

Index vs ID

Tracking by index means “touch 1” can silently become a different finger.

Capture the ID on Start

On touch start (over MoveStickZone):
    Set move_touch_id to Touch.TouchID

On touch start (over AimStickZone):
    Set aim_touch_id to Touch.TouchID

Read by ID Each Frame

For each touch:
    Touch.TouchID = move_touch_id
        → Set move_vector from Touch.X, Touch.Y
    Touch.TouchID = aim_touch_id
        → Set aim_vector from Touch.X, Touch.Y

On any touch end:
    Touch.TouchID = move_touch_id → Set move_touch_id to -1
    Touch.TouchID = aim_touch_id  → Set aim_touch_id to -1

Each stick follows its own finger regardless of how the index list reorders.

Verifying

Hold both sticks, lift either finger, re-touch — the other stick never jumps. The two controls stay independently bound to their fingers.

“Touch index reshuffles; TouchID doesn’t. Bind controls to the ID, captured on touch start.”

Same principle for pinch-zoom — capture the two IDs at gesture start and track those, not ‘touch 0 and touch 1’.