Guidance needed : (2D project) OnMouseDown vs Raycast(2D) for mouse click on GameObject

Hi all, need some guidance on the mentioned topic.
As i search through internet, there are normally 2 ways of implementing Mouse Click detection on Game Object (2D/3D) in Unity: OnMouseDown and Raycast. Most threads that i read on answers.unity3d are suggesting Raycast over onMouseDown.

May i know:

  1. Why is Raycast better than OnMouseDown?
    Any Example that Raycast can achieve while OnMouseDown cant?

  2. If i understand correctly:
    For OnMouseDown, the detection script lies on the Game Object itself, and so does the action following the MouseDown detection.
    As for Raycast implementation, the script normally goes into a controllerObject/Camera. Upon the return of “hit”, the script will then find out what the ray has hit, and from there determine what action should be executed.

Scenerio: Let’s say my game have 30 clickable GameObject on scene, each Object will perform its own action when onMouseDown.

Does this mean i have to sort out all the action logic in the Raycast script:
eg:
if(hit.collider.gameObject.tag == “ObjA”)>>then do action A;
if(hit.collider.gameObject.tag == “ObjB”)>>then do action B;
if(hit.collider.gameObject.tag == “ObjC”)>>then do action C;
if(hit.collider.gameObject.tag == “ObjD”)>>then do action D;
…etc.

Is that the normal way to handle the scenario mentioned?

Thank you

OnMouseDown uses a raycast.
With a raycast, you could filter which layers you want to collide with.
OnMouseDown only works with a mouse (duh) so if someday you decide to change your input (hey let’s use an Htc Vive controller!) you are kinda screwed.

  1. Like I said, OnMouseDown uses a raycast behind the scene, to raycast your mouse click on screen coordinates. It’s kind of a quick convenient implementation that works in most simple scenarios. If you want more control, go with your own raycast.

That would be a very bad way of doing it.

You can have a clickHandler interface on your objects, and then use something like hit.collider.gameObject.GetComponent().MyOnClick();

And then implement MyOnClick however you want in each object.

1 Like

Thank you for the reply~

This is a very important piece of info!!! tq.

thank you for the tips. That is way more logical and manageable!

Can you further elaborate a bit on the implementation part?

At the moment my understanding is:

1)i have a controller object that detect when user click and do a 2Draycast.
2) if something is hit, the “hit” will contain a return of the object being hit.
3) from here i am not sure what’s next…

I am not quite sure i understand the above line. is “MyClickHandler” a Class or Interface?

I think what you suggested is that i should then create an “IClickable” interface, with an MyOnClick() method.
Then i should implement the interface to all clickable GameObject and define the MyOnClick method within respectively. Is that correct?

How can i :

  1. check if the “hit” has implemented “IClickable”?
  2. from there how can i call the MyOnClick(). As MyOnClick() could be implemented to a Player class, Enemy Class …etc, how can i know what component/class to look for when calling MyOnClick()?

Sorry for being ignorant, i am a total newbie to Unity and programming.
I have read through pages and unity documentation, the “Interface” concept is still very abstract to me :frowning:

There is something else to consider here, what if you have loads of gameobjects attaching a heavy script to each one of these object and using the Mouse down, will over burden the system and could potentially slow it down.

This will depend on your game and how it’s designed, but here is what I’ve done for my new game.

    • I have a grid, this is a rect.
    • I attach a script to this grid, I thin make sure that the mouse click contains within the area. e.g. see same code below
Vector3 mMousePosition = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));
  if (mArea.Contains(mMousePosition) == true || _mouseState == true)
{
    • once I know this has happended, I use the following code.
if (Input.GetMouseButtonDown(0) == true)
       {
        GameObject mObject = Utilities2D.SelectObject();
        if (mObject != null)
           {
// do something
           }
}

Now here is the SelectObject code.

using System;
using UnityEngine;

public class Utilities2D
{
/// <summary>
///  This will return a 2D gameobject ONLY using orthographic projection
///  use the 3D version if your object is a 3D object but is in a 2D world.
/// </summary>
/// <returns>selected gameobject otherwise null</returns>
public static GameObject SelectObject()
{
  GameObject mObject = null;
  Ray mRay = Camera.main.ScreenPointToRay(Input.mousePosition);
  RaycastHit2D mHit = Physics2D.Raycast(mRay.origin, Vector2.zero, Mathf.Infinity);
  if (mHit != null && mHit.collider != null)
   mObject = mHit.collider.gameObject;

  return mObject;
}
}

Like I said it depends on what sort of game you are making to whether you can do it this way, but it will make for a lighter app especially on mobiles.

I apologize, I am new to answering forum questions, I work as a senior developer and I’m used to answering questions from other professional developers so I may get a bit too technical.

MyClickHandler in that case would be an interface, which means a class that only defines methods that should be implemented. A basic example would be:

Interfaces are generally prefixed with an I to indicate they are an interface, but it’s not necessary.

public class IMyClickHandler
{
    public virtual bool MyOnClickMethod()
    {
        return false;
    }
}

Now that we have that interface, it means you can use a raycast, and look for hit results that implement that interface by doing something like
hit.collider.gameObject.GetComponent();
if this returns null, the object does not have a click handler, so forget it, otherwise, you can call MyOnClickMethod on it.

Notice that we made MyOnClickMethod virtual. That’s because objects that implement our interface will override that method.

So if you have a class for an enemy, that should explode when you click it, you could have something like this:

public class MyExplodingEnemy : MonoBehaviour, IMyClickHandler
{
    //insert your enemy logic...
   public override bool MyOnClickMethod()
   {
        Explode();
        true;
   }
}

If you want you can also have a reusable component (such as a button) and use a delegate to assign a behavior when it gets clicked (just call the delegate in MyOnClickMethod)

The poster above provided a solid example of how to do the raycast from screen to scene in his SelectObject method.

3 Likes

piggybank1974 , Thank you for the alternative suggestion. Due to my noob-ness, I am not sure i get it correctly:
first you have a rect (as controller) to detect if the user click within a grid, if clicked it runs a SelectObject() and return the object selected (mObject), which is a static function (if i understand correctly static function is sumthing like a global function where every object can call?)

Interesting, but still, i don know how to execute the “onClickAction” when i got the mObject. because i can’t be sure in what class/component within the mObject that “onClickAction” is defined. Is my worry valid?


JC_SummitTech
Thank you for your patient and kind elaboration. I have got it works!!!
I was very much hold back by what mentioned in this thread, where it says getComponent<>() cant get interface.
But weirdly, somehow it seems working. :\

thank you~

My understanding is that behind the scens GetComponent<> uses “typeof” to determine if a component should be returned. an Interface is the same as a class you would inherit from, therefore it should properly be returned.

Please note I have written the code on the fly and might have gotten something wrong, I did not test it or anything, but if you got your code to work, that’s great :slight_smile:

The lille utilities2d class I posted will detect if your mouse is over a gameobject if you use that with the code just above in an update function it will return the gameobject you clicked, if your struggling, private message me and ill help, I could post the demo project here if you like.

You don’t mention what game your trying to make so, so that’s why I mentioned the grid, if it a hack and slash game the this is not for it, but a match 3 game most definatly,

@piggybank1974 . Great example, thank you.

For anyone having this problem in the future, if your project is in 2D, check if the camera is further away than the gameObject. In other words, the camera z position has to be less than the gameObject z position.