Skip to content

Touch and Gestures

Raw fingers and recognised gestures, for the cases an action map can't express — pinch-to-zoom, two-finger rotate, swipe-to-dismiss, your own hit-testing.

Most touch input shouldn't come through here

Bind Touch/Stick0 and Touch/Button0 in your .jinput and your existing action code works on a phone unchanged. This page is the advanced path. See the Input guide.

Touchscreen

Touchscreen.TouchCount

How many fingers are on the screen right now.

Touchscreen.GetTouch(int index)

The touch at index (0 to TouchCount - 1). Order is roughly arrival order among active contacts — it is not a Touch.Id. Out of range returns a neutral default touch with Id of -1.

Touchscreen.GetTouches(Touch[] buffer)

Fills your buffer and returns how many were written. This is the one to use in OnUpdate.

csharp
public class Pincher : GameEntity
{
    private readonly Touch[] m_Touches = new Touch[10];

    protected override void OnUpdate(float ts)
    {
        int count = Touchscreen.GetTouches(m_Touches);
        for (int i = 0; i < count; i++)
        {
            Touch t = m_Touches[i];
            if (t.Phase == TouchPhase.Moved)
                Drag(t.Position, t.Delta);
        }
    }
}

A buffer shorter than TouchCount truncates rather than throwing — the same contract Physics2D.RaycastAll uses.

Touchscreen.Touches

Every active touch as a fresh array.

This allocates on every call

Fine in a menu or a one-off check; wrong in OnUpdate. Use GetTouches(buffer) with a buffer kept in a field — see Performance. The array form is kept because it's much nicer to write when the allocation genuinely doesn't matter.

Touch

A plain value snapshot for one finger, for one frame. Nothing to cache — re-read it fresh each frame.

FieldWhat it is
IdStable for the life of this contact, so you can track one finger across frames
PhaseSee below
PositionCurrent position in screen pixels
DeltaMovement since last frame
StartPositionWhere this contact began
PressureForce, where the device reports it
StartTimeWhen the contact began

TouchPhase

ValueMeaning
BeganThe finger just went down
MovedIt moved this frame
StationaryStill down, didn't move
EndedThe player lifted it
CancelledThe system took it away

Handle Cancelled separately from Ended

Cancelled means a call came in, a system gesture started, or the app lost focus mid-touch — the player did not deliberately let go. Treating it as Ended fires the action they were in the middle of abandoning. Treat it as "abort this interaction".

Gestures

Recognised engine-side over the raw touch stream — the same on every platform, rather than per-OS.

Gestures.Count

How many gestures are active this frame.

Gestures.GetGesture(int index)

The gesture at index. Out of range returns a default with Type of None.

Gestures.GetActive(Gesture[] buffer)

Fills your buffer, returns how many were written. The allocation-free form — use this per frame.

Gestures.Active

Every active gesture as a fresh array. Allocates per call, same trade-off as Touchscreen.Touches.

csharp
public class MapCamera : GameEntity
{
    private readonly Gesture[] m_Gestures = new Gesture[8];
    private CameraComponent? m_Camera;

    protected override void OnUpdate(float ts)
    {
        int count = Gestures.GetActive(m_Gestures);
        for (int i = 0; i < count; i++)
        {
            Gesture g = m_Gestures[i];
            switch (g.Type)
            {
                case GestureType.Pan:
                    Translation -= new Vector3(g.Delta.X, -g.Delta.Y, 0) * 0.01f;
                    break;

                case GestureType.Pinch:
                    m_Camera!.OrthographicSize /= g.Scale;
                    break;

                case GestureType.DoubleTap:
                    ResetView();
                    break;
            }
        }
    }
}

Gesture

FieldWhat it is
TypeSee below
PositionThe gesture's position, or the midpoint between both fingers for Pinch and Rotate
DeltaPan: this frame's movement. Swipe: total displacement from start to release. Zero otherwise
DirectionA SwipeDirection — Swipe only
ScalePinch only. Cumulative since the two-finger contact began; 1.0 is no change
RotationRotate only. This frame's change, in radians — not cumulative

Scale and Rotation count differently

Scale accumulates from the start of the pinch; Rotation reports only the current frame. Dividing by Scale every frame compounds and runs away — apply it against the size you had when the pinch began, or convert to a per-frame delta yourself.

GestureType

None, Tap, DoubleTap, LongPress, Swipe, Pan, Pinch, Rotate.

Discrete gestures — Tap, DoubleTap, LongPress, Swipe — appear for exactly one frame. Continuous ones — Pan, Pinch, Rotate — reappear every frame their contacts stay active. None never appears on a real gesture; it's only the out-of-range default.

SwipeDirection

None, Up, Down, Left, Right. Meaningful only when Type is Swipe.

See also

  • Input — actions, keyboard, mouse, gamepads
  • Input guide — action maps and on-screen controls
  • Performance — why the buffer forms exist