Unity 3D: Working With Touch Input
In this tutorial we’ll walk through how to handle single and multi-touch input in Unity. Note that this tutorial is based on Unity 4. The scripts have been written and tested on version 4.5.1. As major releases are distributed, Unity API may change or behave differently, and new functionalities may be added.
Detecting touches
Here, we’ll use the Input
class to detect, count and retrieve touches. First of all, we can check whether the current device supports multi-touch input using this flag:
bool supportsMultiTouch = Input.multiTouchEnabled;
print("MultiTouchSupport : " + supportsMultiTouch);
For the purpose of this tutorial we’ll assume that multi-touch is supported. The only difference is that, for single-touch devices, you’ll only get one touch input at a time.
For example, here we detect all current touches and their position:
void Update ()
{
int nbTouches = Input.touchCount;
if(nbTouches > 0)
{
print(nbTouches + " touch(es) detected");
for (int i = 0; i < nbTouches; i++)
{
Touch touch = Input.GetTouch(i);
print("Touch index " + touch.fingerId + " detected at position " + touch.position);
}
}
}
Input.touchCount
defines how many touches are detected in the current frame. We can then loop and use Input.GetTouch
to retrieve all touches.
Each Touch
struct has a fingerId
, which identifies the instance for its lifetime, until the touch stops. This can be used to identify the same touch on different frames.
touch.position
defines the coordinates of that touch in the screen. Remember that the origin of the axis is on the lower left corner of the screen.
Another method to retrieve touches is through Input.touches
, which returns a list of current touches objects. The first method should be preferred though, as Input.touches allocates temp variables and could impact your game’s performance.
Input data is refreshed every frame, right before Update()
, so that’s where you should put all input-management logic. Doing so somewhere else, like in FixedUpdate
, would prevent you from catching all input from the user, as FixedUpdate
is not called every frame but on fixed steps.
Touch Phases
When dealing with Touch input, it’s useful to know whether the touch has just started, ended or if it’s swiping in the screen. To do so, we can use the phase property:
void Update ()
{
int nbTouches = Input.touchCount;
if(nbTouches > 0)
{
for (int i = 0; i < nbTouches; i++)
{
Touch touch = Input.GetTouch(i);
TouchPhase phase = touch.phase;
switch(phase)
{
case TouchPhase.Began:
print("New touch detected at position " + touch.position + " , index " + touch.fingerId);
break;
case TouchPhase.Moved:
print("Touch index " + touch.fingerId + " has moved by " + touch.deltaPosition);
break;
case TouchPhase.Stationary:
print("Touch index " + touch.fingerId + " is stationary at position " + touch.position);
break;
case TouchPhase.Ended:
print("Touch index " + touch.fingerId + " ended at position " + touch.position);
break;
case TouchPhase.Canceled:
print("Touch index " + touch.fingerId + " cancelled");
break;
}
}
}
}
When the user is swiping his finger on the screen, we track the delta position instead of the actual position, which may be useful in case you want to drag stuff in games using touch input. We can then use deltaTime
to calculate the moving speed of the touch input:
float touchSpeed = touch.deltaPosition.magnitude / touch.deltaTime;
A touch is marked as cancelled when the system assumes it has been done by mistake, for example when a large area is pressed against the screen, or when more touches than the device can support are detected.
To detect whether the user has tapped on something, we first check if the touch phase is “Began.” Then, we can cast a ray from the input position using the camera, and then check any Raycast collision against that ray
void Update ()
{
int nbTouches = Input.touchCount;
if(nbTouches > 0)
{
for (int i = 0; i < nbTouches; i++)
{
Touch touch = Input.GetTouch(i);
if(touch.phase == TouchPhase.Began)
{
Ray screenRay = Camera.main.ScreenPointToRay(touch.position);
RaycastHit hit;
if (Physics.Raycast(screenRay, out hit))
{
print("User tapped on game object " + hit.collider.gameObject.name);
handleTap(hit.collider.gameObject);
}
}
}
}
}
In some cases, we want to do different things if the user is quickly tapping in the screen. For example, we may tell our player to walk when you tap on the screen, but run if you quickly double-tap:
void Update ()
{
int nbTouches = Input.touchCount;
if(nbTouches > 0)
{
for (int i = 0; i < nbTouches; i++)
{
Touch touch = Input.GetTouch(i);
if(touch.phase == TouchPhase.Began)
{
if(touch.tapCount >= 2)
{
Run();
}
else
{
Walk();
}
}
}
}
else
{
StopMoving();
}
}
When a touch begins, the player will start walking on a single tap, or running if it was a multi-tap. The game character will stop moving if no touches are detected. When implementing the different methods, Walk for example, you’ll also want to check if the user is already walking.
In some cases, Unity will not be able to distinguish whether you’re quickly tapping with the same finger or with two different fingers in succession, so keep that in mind.
Author: Attilio Carotenuto