As Shubius explained, Raycast has no sense of Sprite SortingOrder. Here’s a script I whipped up though that will loop through all the Sprites under the current mouse point, that are in the defined layers of your choice, and return what the closest Sprite is in terms of SortingOrder.
private LayerMask clickableLayers;
private RaycastHit2D[] hits; //Change this number to however many selectable objects you think you'll have layered on top of eachother. This is for performance reasons.
private float rayStart = -2; //Start Raycast from this Z-Coordinate
private float rayEnd = 5; //End Raycast at this Z-Coordinate
private Vector3 clickedPos;
private GameObject selectedObject;
void Start ()
{
clickableLayers = (1 << LayerMask.NameToLayer("Tiles")) //Set the layers you want to be clickable here, duplicate the lines for however many you need
|(1 << LayerMask.NameToLayer("Ground"))
|(1 << LayerMask.NameToLayer("SomeMoreClickableObjects"));
}
void Update ()
{
if(Input.GetButtonDown("Fire1"))
{
SelectTopTile(); //This is how you can call it at any time
}
}
void SelectTopTile()
{
clickedPos = Camera.main.ScreenToWorldPoint (Input.mousePosition);
hits = Physics2D.LinecastAll(new Vector3(clickedPos.x,clickedPos.y,rayStart),new Vector3(clickedPos.x,clickedPos.y,rayEnd),clickableLayers); //Cast ray at the world space the mouse is at
if(hits.Length > 0) //Only function if we actually hit something
{
int topHit = 0; //Set our top hit to a default of the first index in our "hits" array, in case there are no others
int preValue = hits[0].transform.GetComponent<SpriteRenderer>().sortingOrder; //Set our first compare value to the SortingOrder value of the first object in the array, so it doesn't get skipped
for (int arrayID = 1; arrayID < hits.Length; arrayID++) //Loop for every extra item the raycast hit
{
int tempValue = hits[arrayID].transform.GetComponent<SpriteRenderer>().sortingOrder; //Store SortingOrder value from the current item in the array being accessed
if (tempValue > preValue) //If the SortingOrder value of the current check is higher than the previous lowest
{
preValue = tempValue; //Set the "Previous Value" to the current one just changed, for comparison later in loop
topHit = arrayID; //Set our topHit with the Array Index value of the current Array item, since it currently has the highest/closest SortingOrder value
}
}
selectedObject = hits[topHit].transform.gameObject;
}
}
When called, it will assign “selectedObject” whatever the closest object under your mouse cursor is, in terms of SortingOrder.
And here’s a version that will also take the SortingLayer into account:
private LayerMask clickableLayers;
private RaycastHit2D[] hits; //Change this number to however many selectable objects you think you'll have layered on top of eachother. This is for performance reasons.
private float rayStart = -2; //Start Raycast from this Z-Coordinate
private float rayEnd = 5; //End Raycast at this Z-Coordinate
private GameObject selectedObject;
void Start ()
{
clickableLayers = (1 << LayerMask.NameToLayer("Tiles")) //Set the layers you want to be clickable here, duplicate the lines for however many you need
|(1 << LayerMask.NameToLayer("Ground"))
|(1 << LayerMask.NameToLayer("SomeMoreClickableObjects"));
}
void Update ()
{
if(Input.GetButtonDown("Fire1"))
{
SelectTopTile(); //This is how you can call it at any time
}
}
void SelectTopTile()
{
Vector3 clickedPos = Camera.main.ScreenToWorldPoint (Input.mousePosition); //Store out clicked position
hits = Physics2D.LinecastAll(new Vector3(clickedPos.x,clickedPos.y,rayStart),new Vector3(clickedPos.x,clickedPos.y,rayEnd),clickableLayers); //Cast ray at the world space the mouse is at
if(hits.Length > 0) //Only function if we actually hit something
{
int topHit = 0; //Set our top hit to a default of the first index in our "hits" array, in case there are no others
int preVal = hits[0].transform.GetComponent<SpriteRenderer>().sortingLayerID; //Store the SortingLayerID of the first object in the array, so it doesn't get skipped
int preSubVal = hits[0].transform.GetComponent<SpriteRenderer>().sortingOrder; //Store the SortingOrder value of the first object in the array, in case it needs to be compared to
for (int arrayID = 1; arrayID < hits.Length; arrayID++) //Loop for every extra item the raycast hit
{
int curVal = hits[arrayID].transform.GetComponent<SpriteRenderer>().sortingLayerID; //Store SortingLayerID of the current item in the array being accessed
if (curVal < preVal) //If the SortingLayerID of the current array item is lower than the previous lowest
{
preVal = curVal; //Set the "Previous Value" to the current one since it's lower, as it will become the "Previous Lowest" on the next loop
topHit = arrayID; //Set our topHit with the Array Index value of the current closest Array item, since it currently has the highest/closest SortingLayerID
preSubVal = hits[arrayID].transform.GetComponent<SpriteRenderer>().sortingOrder; //Store SortingOrder value of the current closest object, for comparison next loop if we end up going to the "else if"
}
else if( (curVal == preVal) (hits[arrayID].transform.GetComponent<SpriteRenderer>().sortingOrder > preSubVal) ) //If SortingLayerID are the same, then we need to compare SortingOrder. If the SortingOrder is lower than the one stored in the previous loop, then update values
{
topHit = arrayID;
preSubVal = hits[arrayID].transform.GetComponent<SpriteRenderer>().sortingOrder;
}
}
selectedObject = hits[topHit].transform.gameObject;
}
}