I’m not sure if you are working with 2D colliders; however, the example I will provide for you could probably be re-worked for 3D as well.
Basically my solution was to create two scripts; first, a MouseDispatcher which I would attach to an empty game object and second, a MouseTarget script that I would add to my objects (I have two sprites with a Box Collider 2D component marked as a trigger). The MouseTarget script has two fields, one to hold the game object for the MouseDispatcher, and one called “Z Order”.
The MouseTarget script adds itself to a collection on the mouse dispatcher on Awake() and removes itself in OnDestroy(). In addition, the OnMouseDown() method defined needs to delegate the mouse down event to the dispatcher. Finally, a “MouseButtonDown()” handler in a script needs to exist to get the “real” dispatched mouse down.
The dispatcher’s job is to then determine from the mouse position all of the coinciding 2D colliders and get them ordered by their Z Order. Note that the only real purpose of the Z Order property is to determine which one is going to be considered the foreground by sorting. The item with the lowest Z Order value is considered the foreground, so if you set one object’s Z Order property to zero, then set all the rest to one, your object will have priority if overlapped by those 10 other items. Note that you can also set the priority of multiple items to determine the absolute order, kind of like layers. However, it is important to note that it only uses the Z Order for ordering, the first object intersected with the lower Z Order value is triggered.
All of that said, here are the scripts that I used. First, the MouseDispatcher, added to an empty GameObject in the scene:
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class MouseDispatcher : MonoBehaviour
{
private static List<GameObject> mouseActionItems = new List<GameObject>();
public void AddMouseTarget(GameObject mouseTarget)
{
MouseDispatcher.mouseActionItems.Add(mouseTarget);
}
public void RemoveMouseTarget(GameObject mouseTarget)
{
MouseDispatcher.mouseActionItems.Remove(mouseTarget);
}
public void TriggerMouseDown()
{
Ray cameraRay = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit2D[] result = Physics2D.GetRayIntersectionAll(cameraRay);
var item = result.AsEnumerable()
.Select(i => i.collider.gameObject.GetComponent<MouseTarget>())
.Where(i => i != null)
.OrderBy(i => i.zOrder)
.Select(i => i.gameObject)
.FirstOrDefault();
if (item != null)
{
item.SendMessage("MouseButtonDown");
}
}
}
And next, the MouseTarget script, which is added to your objects with the colliders and you must attach your MouseDispatcher’s game object, give it a Z Order value (lower is higher priority) and a name for testing:
using UnityEngine;
public class MouseTarget : MonoBehaviour
{
public MouseDispatcher mouseDispatcher;
public int zOrder;
public string displayName;
public void Awake()
{
mouseDispatcher.AddMouseTarget(this.gameObject);
}
public void OnDestroy()
{
mouseDispatcher.RemoveMouseTarget(this.gameObject);
}
public void OnMouseDown()
{
mouseDispatcher.TriggerMouseDown();
}
public void MouseButtonDown()
{
Debug.Log("Item " + displayName + " was triggered.");
}
}
Error handling was left out for brevity. If you create two sprites, then overlap them, change the following lines temporarily to see which one is always getting called first on the overlap:
public void OnMouseDown()
{
//mouseDispatcher.TriggerMouseDown();
MouseButtonDown();
}
This will show you which one is always being called… now fix the code back and change your Z Orders to set your foreground item and then test clicking on the overlapping again. You should see the item which has your lower Z Order being called.
Hopefully this is an acceptable approach.