Detect right clicks on game objects in Unity3D

GFX47 April 4, 2011 10
Detect right clicks on game objects in Unity3D

The problem

I’ve seen a lot of people asking for help on forums on how to detect right clicks in a Unity3D game. It’s indeed not that easy as no native method is really made for it.

We have the good old OnMouseDown method that can be used on any game object having a collider component but it only handles the left clicks :(

Web games warning: first disable context menu!

By default, when you build your game for web player, right clicks on the game frame will open the context menu.

You can easily disable this behavior by setting the disableContextMenu parameter to true in your web page’s javascript code.

See web player’s behavior documentation for more details.

The easy way

The solution

As mentionned by Eric5h5 in his answer to my unity3D forum thread, there’s a very easy way to detect right clicks on a game objects in Unity3D.

The principles is to combine OnMouseOver and Input.GetMouseButtonDown methods.

The first one is triggered when the mouse cursor is dragged over current game object. Every left/right/middle mouse button click on our game object will occur while the mouse cursor is over it. So this is our first filter.

The second one will return true if one of the mouse button as been pressed during the frame it’s being called. The first (integer) parameter of this method specifies which mouse button you want to track (0 for left button, 1 for right button, 2 for the middle button). This is how we know if a right click as occur on our game object.

The code

function OnMouseOver()
{
    if (Input.GetMouseButtonDown(1))
    {
        // Right button clicked on this object
    }
}

Pros and cons

The advantage of this method is that it’s easy to implement and it also works on GUI texts and textures.

The counterpart is that the code will have to be executed on every game object you want to listen for right clicks.

Simply put, it is the best solution for most of the cases but you could need to optimize your code and want to scan clicks only once per frame. The next chapter presents another solution which is recommenced for scenes with a lot of game objects tracking right clicks.

The complicated/optimized way

Detect mouse button clicks

The right way to detect right (and middle) clicks is to use the Input class and especially its GetMouseButtonDown method.

As the Unity3D script reference documentation states it, the first (integer) parameter of this method specifies the mouse button you want to track (0 for left button, 1 for right button, 2 for the middle button). All right, so now we know when the left/right/middle mouse button is clicked! … but wait, what has been clicked ?

Find the targeted game object

Our new problem is to know which game object was targeted during the click we just detected. Indeed, unlike the OnMouseDown method, the GetMouseButtonDown is a method of the Input class, which isn’t “linked” to any game object in particular.

The way we’ll solve this problem is pretty simple: draw a line from camera to mouse position and continue drawing until our pen hits something (our target). There’s a very useful tool present in Unity3D which will help us to perform it: ray casting (simply put, “ray” = “line” and “casting” = “drawing” :) ), and more precisely the Physics.RayCast method.

But first we have to build this ray. We know current mouse position thanks to the Input.MousePosition vector. We also know where our main camera is placed as we can access its transform component.

We could build it using those 2 informations but there’s an easier way to do it: the Camera.ScreenToPointRay method. Simply set the mouse position as first parameter of this method and magic! you have your ray.

Final step is to find which game object has been hit by the ray we just casted. Once again it’s pretty simple as the Physics.RayCast method takes a pointer/modifiable parameter name “hitinfo”. This parameter will be filled with a RaycastHit object, containing the transform of the targeted game object. Add a “.gameObject” to the result and you get your target!

[Advanced] Wait not this one, the one behind it

Maybe you just want to listen for clicks on your main character and don’t want to trigger anything when clicking on the robot passing by between your camera and your character. Or you still want your click to be detected when he moved behind a tree. You can do it by using Unity3D’s layer filtering feature.

By chance, the Physics.RayCast method handles it perfectly :) Just set the correct layer mask has fourth parameter and you’re done: ray will only hit game objects of the layer(s) you specified.

ClickDetector.cs

Here’s the script I use on several of my Unity3D projects for mouse click detection. It can be reused on any project as it doesn’t rely on any other specific class.

It’s based on the SendMessage method usage and is fully customizable (see exposed parameters in Unity3D editor).

Simply drag it to only one game object in your scene and define the OnLeft/Right/Middle methods on the game objects you want to handle clicks.

Download this script or copy it from following source:

using UnityEngine;
using System.Collections;
public class ClickDetector : MonoBehaviour
{
	public bool HandleLeftClick = true;
	public bool HandleRightClick = true;
	public bool HandleMiddleClick = false;
	public string OnLeftClickMethodName = "OnLeftClick";
	public string OnRightClickMethodName = "OnRightClick";
	public string OnMiddleClickMethodName = "OnMiddleClick";
	public LayerMask layerMask;
	void Update()
	{
		GameObject clickedGmObj = null;
		bool clickedGmObjAcquired = false;
		// Left click
		if (HandleLeftClick && Input.GetMouseButtonDown(0))
		{
			/*if (!clickedGmObjAcquired)
			{*/
				clickedGmObj = GetClickedGameObject();
				clickedGmObjAcquired = true;
			/*}*/
			if (clickedGmObj != null)
				clickedGmObj.SendMessage(OnLeftClickMethodName, null, SendMessageOptions.DontRequireReceiver);
		}
		// Right click
		if (HandleRightClick && Input.GetMouseButtonDown(1))
		{
			if (!clickedGmObjAcquired)
			{
				clickedGmObj = GetClickedGameObject();
				clickedGmObjAcquired = true;
			}
			if (clickedGmObj != null)
				clickedGmObj.SendMessage(OnRightClickMethodName, null, SendMessageOptions.DontRequireReceiver);
		}
		// Middle click
		if (HandleMiddleClick && Input.GetMouseButtonDown(2))
		{
			if (!clickedGmObjAcquired)
			{
				clickedGmObj = GetClickedGameObject();
				clickedGmObjAcquired = true;
			}
			if (clickedGmObj != null)
				clickedGmObj.SendMessage(OnMiddleClickMethodName, null, SendMessageOptions.DontRequireReceiver);
		}
	}
	GameObject GetClickedGameObject()
	{
		// Builds a ray from camera point of view to the mouse position
		Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
		RaycastHit hit;
		// Casts the ray and get the first game object hit
		if (Physics.Raycast(ray, out hit, Mathf.Infinity, layerMask))
			return hit.transform.gameObject;
		else
			return null;
	}
}

Demo project

A demo is worth a thousand words so here’s a project showing how to use it all.

Awaiting your comments and feedbacks! ;)

10 Comments »

  1. Jonathan Fryxelius April 30, 2011 at 9:17 pm - Reply

    Hi! I am new to Unity3D and I am presently trying to create my first game. This explanation of the Right-Mouse-Button problem and the Demo project has greatly helped me. Just wanted you to know that, and to say a great Thank You!

    • GFX47 May 8, 2011 at 2:30 pm - Reply

      You’re welcome, glad I could help! ;)

  2. Nireas January 31, 2012 at 12:49 pm - Reply

    You are a god (not really, but close :) ). Very helpful and informative for new Unity users

    • GFX47 January 31, 2012 at 12:52 pm - Reply

      Comment approved! XD

  3. Zinon February 19, 2012 at 5:16 pm - Reply

    Hello good sir,
    Can you explain to a new Unity user a bit more how to use this nice piece of code that you so unselfishly share it with us?
    For example: I have created a tile grid and I have tile objects and unit objects on the map (as you may have guessed it’s a turn-based strategy wanna be game). How can I use your ClickDetector?
    I have put your script on an empty object in my scene, but what next? Do I have to put any code in my “tile” and “unit” objects? I can understand most of the code, but the variables at the top before the update functions and the SendMessage confuse me.
    Thank you for your code and it would be really nice, if you could help me :)

    • Zinon February 19, 2012 at 5:48 pm - Reply

      Just something more to ask. Do you think that your script is a good solution to handle mouse clicks for my project or shall I try to find something different?

  4. Jay Borman April 16, 2012 at 4:06 pm - Reply

    If you’re using the right click and don’t want that menu in your face:

    File > Build Settings > Player Settings > “Resolution and Presentation” > WebPlayer Template” > “No Context Menu”

  5. Gary G April 20, 2012 at 12:33 am - Reply

    I downloaded the ClickDetect.cs code but not having any luck getting it to work. The Raycast() in GetClickedGameObject() is always returning null.

    • Gary G April 20, 2012 at 12:34 am - Reply

      I also have the demo project which works. Is there a camera setting that I am missing?

      • Gary G April 20, 2012 at 12:57 am - Reply

        I copy one of the cubes from the demo scene to my scene. Clicking on the cube works, but still nothing for the objects I have created. In the editor my object is the same as cube. What am I missing?

Leave A Response »