Replacing OnMouseEnter/Exit/Down/etc with Raycasting

OnMouseDown/etc won’t work in scenes with multiple cameras and one being Orthographic.

I think I’ve solved some of them (still need to add a LayerMask), but I’m not sure how do the OnMouseEnter/Exit properly. The issue is just sending the message once.

I was thinking just store the hoveredGO at all times, and check if it changes, but I’m not sure how to get the OnMouseExit to work doing this:

	public GameObject hoveredGO;

	void Update()
	{
		RaycastHit hitInfo = new RaycastHit();
		Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

		// OnMouseDown
		if (Input.GetMouseButtonDown(0))
		{
			if (Physics.Raycast(ray, out hitInfo))
			{
				hitInfo.collider.SendMessage("OnMouseDown", SendMessageOptions.DontRequireReceiver);
			}
		}

		// OnMouseUp
		if (Input.GetMouseButtonUp(0))
		{
			if (Physics.Raycast(ray, out hitInfo))
			{
				hitInfo.collider.SendMessage("OnMouseUp", SendMessageOptions.DontRequireReceiver);
			}
		}

		// OnMouseOver
		if (Physics.Raycast(ray, out hitInfo))
		{
			hitInfo.collider.SendMessage("OnMouseOver", SendMessageOptions.DontRequireReceiver);
		}

		// OnMouseEnter
		if (Physics.Raycast(ray, out hitInfo))
		{
			if (hitInfo.collider.gameObject != hoveredGO)
			{
				hitInfo.collider.SendMessage("OnMouseEnter", SendMessageOptions.DontRequireReceiver);
				hoveredGO = hitInfo.collider.gameObject;
			}
		}
	}

The way you have it now works, but I’d write it a little differently - All you need to do is have a way to remember what the mouse was doing in the previous frame, be it with a boolean or anything. I just chose to use an enum because the names make it simple to look at.

public GameObject hoveredGO;
public enum HoverState{HOVER, NONE};
public HoverState hover_state = HoverState.NONE;
    	
void Update () {
    RaycastHit hitInfo = new RaycastHit();
    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    		
    if(Physics.Raycast(ray, out hitInfo)){
    	if(hover_state == HoverState.NONE){
            hitInfo.collider.SendMessage("OnMouseEnter", SendMessageOptions.DontRequireReceiver);
    	    hoveredGO = hitInfo.collider.gameObject;
        }
        hover_state = HoverState.HOVER;       
    }
    else{
    	if(hover_state == HoverState.HOVER){
            hoveredGO.SendMessage("OnMouseExit", SendMessageOptions.DontRequireReceiver);
        }
        hover_state = HoverState.NONE;
    }
    		
    if(hover_state == HoverState.HOVER){
        hitInfo.collider.SendMessage("OnMouseOver", SendMessageOptions.DontRequireReceiver); //Mouse is hovering
        if(Input.GetMouseButtonDown(0)){
            hitInfo.collider.SendMessage("OnMouseDown", SendMessageOptions.DontRequireReceiver); //Mouse down
    	}
        if(Input.GetMouseButtonUp(0)){
            hitInfo.collider.SendMessage("OnMouseUp", SendMessageOptions.DontRequireReceiver); //Mouse up
        }
    		
    }
}

You don’t need to use this, but the key here is just having a way to remember what the mouse was doing in the previous frame like the other person commented, so you can check if it only just entered or exited, or is staying over the object.

This is what I used for multi-touch raycasting in a 2D game. It supports many types of messages.
Since I’m very new to programing, I’m interested in any comments and advice on my code.

What I notice in both codes proposed above is that OnMouseDown, OnMouseOver, etc. messages are used. According to this post:

its not good too use OnMouse events in mobile development (Unity has to dance through fiery hoops in every frame to make it work). Therefore IMO its better to use custom messages.

public class TouchInput2D : MonoBehaviour {

    public LayerMask touchInputMask;
    private Camera cam;
    private List<GameObject> touchList = new List<GameObject>();
    private List<GameObject> touchListOld = new List<GameObject>();
    private RaycastHit2D hit;
    
    void Start ()
    {
        cam = this.GetComponent<Camera>();
    }
    void Update () 
    {
#if UNITY_EDITOR || UNITY_STANDALONE
        if (Input.GetMouseButton(0) || Input.GetMouseButtonDown(0) || Input.GetMouseButtonUp(0))
        {
            touchListOld.Clear();
            touchListOld.AddRange(touchList);
            touchList.Clear();
     
            hit = Physics2D.Raycast(cam.ScreenToWorldPoint(Input.mousePosition), Vector2.zero, 1f, touchInputMask);
            if (hit.collider != null)
            {
                GameObject recipent = hit.transform.gameObject;
                touchList.Add(recipent);
                
                if (Input.GetMouseButtonDown(0))
                {
                    recipent.SendMessage("OnTouchDown", hit.point, SendMessageOptions.DontRequireReceiver);
                }
                if (Input.GetMouseButtonUp(0))
                {
                    recipent.SendMessage("OnTouchUp", hit.point, SendMessageOptions.DontRequireReceiver);
                }
                if (Input.GetMouseButton(0) && Input.GetMouseButtonDown(0) == false &&touchListOld.Contains(recipent) == false)
                {
                    recipent.SendMessage("OnTouchEntered", hit.point, SendMessageOptions.DontRequireReceiver);
                }
                if (Input.GetMouseButton(0))
                {
                    recipent.SendMessage("OnTouchStay", hit.point, SendMessageOptions.DontRequireReceiver);
                }
            }
            foreach (GameObject g in touchListOld)
            {
                if (!touchList.Contains(g) && g != null)
                {
                    g.SendMessage("OnTouchExit", hit.point, SendMessageOptions.DontRequireReceiver);
                }
            }
        }
#endif

#if UNITY_IOS
        if (Input.touchCount > 0)
        {
            touchListOld.Clear();
            touchListOld.AddRange(touchList);
            touchList.Clear();
            
            Touch[] currentTouches = Input.touches;
            for (int i = 0; i < Input.touchCount; i++)
            {
                hit = Physics2D.Raycast(cam.ScreenToWorldPoint(currentTouches*.position), Vector2.zero, 1f, touchInputMask);*

if (hit.collider != null)
{
GameObject recipent = hit.transform.gameObject;
touchList.Add(recipent);

if (currentTouches*.phase == TouchPhase.Began)*
{
recipent.SendMessage(“OnTouchDown”, hit.point, SendMessageOptions.DontRequireReceiver);
}
if (currentTouches*.phase == TouchPhase.Ended)*
{
recipent.SendMessage(“OnTouchUp”, hit.point, SendMessageOptions.DontRequireReceiver);
}
if (currentTouches_.phase == TouchPhase.Stationary || currentTouches*.phase == TouchPhase.Moved)
{
recipent.SendMessage(“OnTouchStay”, hit.point, SendMessageOptions.DontRequireReceiver);
}
if (currentTouches.phase == TouchPhase.Moved && currentTouches.phase != TouchPhase.Began && touchListOld.Contains(recipent) == false)
{
recipent.SendMessage(“OnTouchEntered”, hit.point, SendMessageOptions.DontRequireReceiver);
}_

_if (currentTouches.phase == TouchPhase.Moved)
{
recipent.SendMessage(“OnTouchMoved”, hit.point, SendMessageOptions.DontRequireReceiver);
}_

_if (currentTouches.phase == TouchPhase.Canceled)
{
recipent.SendMessage(“OnTouchExit”, hit.point, SendMessageOptions.DontRequireReceiver);
}
}
}
foreach (GameObject g in touchListOld)
{
if (!touchList.Contains(g) && g != null)
{
g.SendMessage(“OnTouchExit”, hit.point, SendMessageOptions.DontRequireReceiver);
}
}
}
#endif*

}
}_

using UnityEngine;

internal class test : MonoBehaviour
{


    public GameObject hoveredGO;
    public enum HoverState { HOVER, NONE };
    public HoverState hover_state = HoverState.NONE;

    public bool colour_Changed = false;

    private void Awake()
    {
        hoveredGO = gameObject;
    }

    void Update()
    {
        RaycastHit hitInfo = new RaycastHit();
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

        if (Physics.Raycast(ray, out hitInfo))
        {
            if (hover_state == HoverState.NONE && hitInfo.collider.gameObject == hoveredGO)
            {
                colour_Changed = true;

                gameObject.GetComponent<Renderer>().material.color = Color.red;

                hitInfo.collider.SendMessage("OnMouseEnter", SendMessageOptions.DontRequireReceiver);
                hoveredGO = hitInfo.collider.gameObject;
            }
            hover_state = HoverState.HOVER;
        }
        else
        {
            if (hover_state == HoverState.HOVER && colour_Changed == true)
            {
                hoveredGO.SendMessage("OnMouseExit", SendMessageOptions.DontRequireReceiver);

                gameObject.GetComponent<Renderer>().material.color = Color.blue;

                colour_Changed = false;
            }
            hover_state = HoverState.NONE;
        }

        if (hover_state == HoverState.HOVER)
        {
            hitInfo.collider.SendMessage("OnMouseOver", SendMessageOptions.DontRequireReceiver); //Mouse is hovering
            if (Input.GetMouseButtonDown(0) && hitInfo.collider.gameObject == hoveredGO)
            {
                hitInfo.collider.SendMessage("OnMouseDown", SendMessageOptions.DontRequireReceiver); //Mouse down

                gameObject.GetComponent<Renderer>().material.color = Color.yellow;
            }
            if (Input.GetMouseButtonUp(0) && hitInfo.collider.gameObject == hoveredGO)
            {
                hitInfo.collider.SendMessage("OnMouseUp", SendMessageOptions.DontRequireReceiver); //Mouse up

                gameObject.GetComponent<Renderer>().material.color = Color.green;
            }

        }
    }


}

However, this script can be used with more object without the problem that when you have 2 or more different gameobjects with the first script it becomes both objects red also if your mouse is only on one of this.