Unity click and touch script
A 4 minutes story written on Aug 2014 by Adrian B.G.
Every game needs some kind of input control from the user, usually we need to let the user interact with our unity objects by click/touch them.
Problem
You are a unity beginner programmer that makes standalone and/or mobile games and searches how to make a Click/Touch (controller) class to handle Player’s input.
Solution (more tips than the actual solution)
I've put together a small script component that handles all the clicks and simple touches in my prototypes. It is meant to be used as a single instance on scene. You can make a similar script that can be attached to all interactable objects, and in each object’a Update() function check if the users clicks now and points at ir, trigger the Click. It’s a waste of code, CPU and memory.
First step the Input
Unity aggregates all the different platform input controllers into one class Input http://docs.unity3d.com/ScriptReference/Input.html .
If the game input is relative light, I usually attach all input scripts to the Main camera, if things get more complicated is best to create a special empty controller object with children as modules.
Quick documentation
If you didn't already read the basic documentation, the click and touch coordinates are in Screen coordinates, meaning:
The bottom-left of the screen or window is at (0, 0). The top-right of the screen or window is at (Screen.width, Screen.height).
All the events have multiple states (like mouse down, touch has began now), I prefer to consider it a click at click release (GetMouseButtonUp or TouchPhase.Ended see documentation). It is recommended that all the Input related changes to be captured in the Update() function, the Unity keeps all the changes between frame updates and aggregates them in the Input event data (like the touch movement between last Update call).
Catch the mouse, piece of cake.
if (Input.GetMouseButtonUp(0)) {
click = Input.mousePosition;
}
Both Mouse and Touch devices will return a Vector3 position, you will need to ignore the Z axis and keep in mind the these are Screen relative axis.
if (Input.touches[0].phase == TouchPhase.Ended) {
click = t.position;
}
I usually trigger the click at the end phase.
Anti-spam
There can be only one … mouse cursor, but on Android and Apple devices there can be multiple touches at once, usually they mean the user is making a gesture, so you will want to ignore the drag and multi-touches.if ( Input.touchCount == 1 ) {....}
We narrowed down to one touch at a time, but we still need to handle:
- double tap (click) — delay the click trigger and wait for a 2nd click in (nearly) same position, if so cancel the pending trigger and trigger a DoubleClick one.
- Drag — I found only the Touch.hasMoved property, that tell us if the finger has moved since the last Update(), so we need to mark this touch internally as a drag and discard it.
Second step, find the interaction scene object
We will be using the most popular and simple way to check if an object is “under” a screen point (from the camera perspective), and that is Ray Casting technique see documentation. Basically we draw a line (as an extent to our input/finger) and see what object(s) hits.
Requirements
- the input coordinates (see part 1, mouse or touch position)
- a direction to shoot the raycast — see documentation
Camera.main.ScreenToWorldPoint(clickPosition)
- all the interactable objects must have a physic collider.
- calculate (or hard code) the ray’s length _(how deep you want the input to take effect == max distance of the objects from the camera position) in unity units.
- decide how many objects, along the ray, you need to interact with (only the first, all or just some == use Physics.RaycastAll or Physics_.Raycast
The best way to learn how Raycast work is to see them in action, and for this you will need a visual tool, Unity has it cover for us: Debug.DrawRay(ray.origin, ray.direction * clickMaxDepth, Color.yellow, 3f))`
This will help you debug and see exactly (very very useful in 3D) where the ray is created but works in editor only.
Spam
If you have lots of objects with colliders, the raycast will find them all, so you need to filter them:
- object layer — set your interactable objects in a separate layer
- filter collided objects by a component — ex Clickable
- [C#] filter collided objects by a component’s class Interface/Parent
Tips
- there are different Physics and raycast for 2d/3d
- you can raycast only for a specific layer (to optimize the process)
- there are multiple constructors, be careful at the parameters order
- see all Unity Raycast functions http://docs.unity3d.com/ScriptReference/30_search.html?q=raycast