How to raycast from mouse position in scene view?

I’m trying to raycast in scene view based mouse position. I pretty much want to mimic the unity’s behavior when you click some where it selects that objects except I wish to get all the objects through that ray.

Here’s exactly what I want to do:

  1. Press a button on the inspector of a script to start raycasting.
  2. As user moving through scene it selects (keeps record of) all the objects that intersect with ray from current mouse position.
  3. Then user clicks in the scene view to stop the raycasting and the last set of selected objects is shown in the inspector.

I’ve search and not seen a solution that works. It seems like such a trivial thing to be able to do.

So if anyone can guide me to create a simple scriptthat works that would be great.

Here’s a link to what I have so far: https://gist.github.com/lordlycastle/fdc919da37585410309df3231ed03e00

You can get scene camera reference inside OnWillRenderObject handlers from Camera.current, but idk how to get mouse position in scene view coordinates if your scene/game windows do not overlap

You can cast a ray through the camera as described here:

You can get all objects (instead of the first) that the ray hits by using RaycastAll instead:

Note that the order is not guaranteed.

You can then make a script run in editor mode by adding the [ExecuteInEditorMode] attribute:

Hope this helps :slight_smile:

Edit: To reply to palex-nx, you can get the mouse position by getting Event.current.mousePosition in OnSceneGui(). If i remember correctly, OnScenGui is basically your Update() equivalent for custom editors. And while i’m at it, you may wanna create the script as a custom editor instead of a Monobehavior, depending on if it’s only used in SceneView.

1 Like

@Yoreki I’ve actually seen all those. As you can see from the script linked above. The problem isn’t that I can’t raycast through all. You can do that with RaycastAll. The problem is getting the Ray from scene camera based on mouse position.

Have you tried the following to get the ray? Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);

I’ve tried many solutions. This one works, but not in all scenarios. I’m not sure what is going on but depending on the PC (maybe resolution) the ray is not sent properly.

In my PC its fov and resolution independent but we had to revert this because was not consistent across different people.

Also there is a weird consisten 45 pixels offset between the cursor and the actual click in the screen. If anyone has any extra clues about this would be very appreciated.

This includes what I tried for debug purposes:

Ray camRay = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);

        // For debug purposes
        Debug.DrawRay(camRay.origin, camRay.direction * 50, Color.blue, 15);
        if (Physics.Raycast(camRay, out var hitDebug, float.MaxValue, -1, QueryTriggerInteraction.Ignore))
        {
            Debug.DrawLine(hitDebug.point - Vector3.forward * 1f, hitDebug.point + Vector3.forward * 1f, Color.red, 10);
            Debug.DrawLine(hitDebug.point - Vector3.right * 1f, hitDebug.point + Vector3.right * 1f, Color.red, 10);
            Debug.DrawLine(hitDebug.point - Vector3.up * 1f, hitDebug.point + Vector3.up * 1f, Color.red, 10);
        }

First, to be clear, HandleUtility.GUIPointToWorldRay is an editor only thing.

Second, regarding the offset, it is because of the ribbon above the scene view, and I’ve seen weird ways people try to get rid of it, over the years, which is unnecessary. This should be sorted out if you capture your mouse inside the scene view callback.

Refer to this utility class I shared recently (specifically onSceneGui handler in UserScript class). It does mouse capturing as a side-thing, just to show off some features.

Also this line of code to hook up the handler

SceneView.duringSceneGui += onSceneGui;

I’ve made an array of very luxurious custom editors in the last 5 years. This is the staple way I’m always using to get the mouse cursor inside the scene.

1 Like
using UnityEditor;
using UnityEngine;

public class SpawnSphereOnHit : MonoBehaviour
{
    [MenuItem("Tools/Enable Spawn Sphere On Hit")]
    private static void EnableSpawnSphereOnHit()
    {
        SceneView.duringSceneGui += OnSceneGUI;
    }

    [MenuItem("Tools/Disable Spawn Sphere On Hit")]
    private static void DisableSpawnSphereOnHit()
    {
        SceneView.duringSceneGui -= OnSceneGUI;
    }

    private static void OnSceneGUI(SceneView sceneView)
    {
        if (Event.current.type == EventType.MouseDown && Event.current.button == 0)
        {
            Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
            RaycastHit hit;

            if (Physics.Raycast(ray, out hit))
            {
                Debug.Log("Hit object name: " + hit.collider.gameObject.name);

                GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                sphere.transform.position = hit.point;
                sphere.transform.parent = hit.collider.transform;
            }
            else
            {
                Debug.Log("No object hit.");
            }
        }
    }
}
5 Likes

Using HandleUtility.GUIPointToWorldRay inside SceneView.duringSceneGui effectively solved the offset issue for me.

@SachinGanesh, shouldn’t you call Event.Use after using your event (as seen in the doc of Event.Use)? What consequence to expect from not calling it?

You don’t have to use (or consume) an event, by not calling use you let Unity environment further process the event as usual. In other words, you want to call use if you want it to not propagate any further, for example, to suppress the usual editor behavior like rotating a camera, or applying the currently selected tool in addition to whatever your script did. Sometimes this is desirable in custom tools, to prevent double action and stop Unity from responding to custom-tailored behaviors.

1 Like