Feature Highlight: Scene View Context Menu

Today, we want to highlight a feature introduced in Unity 6 Preview: The Scene View Context Menu

This update is designed to enhance your workflow and provide quicker access to essential tools and actions. In fact, the context menu in the Scene view offers customizable and context-sensitive options that adapt to your current selection.

This feature has been highly requested, and some resourceful users have already created their own custom menus in the Scene view. However, we aim to provide a simpler, standardized method for all users to customize the menu, including both pre-made menu items, through the ContextMenuUtility class, and entirely custom options, as demonstrated below.

Here’s an overview of the default Scene View context menu when a GameObject is actively selected.

Customizing the Scene view context menu with the prefix CONTEXT

It’s as easy as adding a MenuItem with the prefix CONTEXT to the menu item path.

For an EditorToolContext, you can do the following:

// Add a menu item called "Custom Editor Tool Context Item" to the Scene view context menu when the
// EditorToolContext "TestToolContext" is engaged. 
[MenuItem("CONTEXT/TestToolContext/Custom Editor Tool Context Item")]
static void TestToolContextMenuItem()
{
    Debug.Log("TestToolContext Menu Item");
}

In the context of an EditorTool, you can achieve this with the following:

// Add a menu item called "Custom Editor Tool Item" to the Scene view context menu when the 
// EditorTool "TestTool" is engaged.
[MenuItem("CONTEXT/TestTool/Custom Editor Tool Item")]
static void TestToolMenuItem()
{
    Debug.Log("TestTool Menu Item");
}

When working with components, you can proceed as follows:

// Add a menu item called "Custom Mesh Filter Item" to the Scene view context menu when the 
// GameObject selected has a MeshFilter component.
[MenuItem("CONTEXT/MeshFilter/Custom Mesh Filter Item")]
static void TestMeshFilterMenuItem()
{
    Debug.Log("MeshFilter Menu Item");
}

Customizing the Scene view context menu with PopulateMenu

Another way of adding custom items to the Scene view context menu is by utilizing the new PopulateMenu method in the EditorToolContext and EditorTool classes.

In fact, custom tools created using EditorToolContext can now add their own menu items to the Scene view context menu with the PopulateMenu method. This integration ensures that when you switch to a custom tool, relevant actions and settings are immediately accessible.

The following code example adds clipboard entries to the Scene view context menu when the CustomEditorToolContext is active.

[EditorToolContext("Custom Editor Tool Context")]
public class CustomEditorToolContext : EditorToolContext
{
    public override void OnToolGUI(EditorWindow _) { ... }
    protected override Type GetEditorToolType(Tool tool) { ... }

    public override void PopulateMenu(DropdownMenu menu)
    {
        ContextMenuUtility.AddClipboardEntriesTo(menu);
    }
}

Similarly, the PopulateMenu method in EditorTool allows you to extend your custom tools to support context-specific menus. This means that your tools can be more intuitive and tailored to different scenarios within the Scene View.

The following code example adds the Edit/Select All menu item to the Scene view context menu as Select/Select All when the CustomEditorTool is active.

class CustomEditorTool : EditorTool
{
    public override void OnToolGUI(EditorWindow _) { ... }

    public override void PopulateMenu(DropdownMenu menu)
    {
        ContextMenuUtility.AddMenuItem(menu, "Edit/Select All", "Select/Select All");
    }
}

Leveraging EditorAction for enhanced interactivity

The EditorAction class allows you to define custom actions that can be included in the context menu. These actions can be finely controlled and provide a direct way to trigger functionalities precisely when needed.

The code example below defines an EditorAction that spans multiple frames, enabling you to create a cube. Additionally, this EditorAction is added to the Scene view context menu for easy access.

// Add a menu item to the Scene View context menu that creates a cube where-ever the mouse clicks.
public class CreateCube : EditorAction
{
    GameObject m_GameObject;
    int m_UndoStart;

    // Prefixing the menu item path with "CONTEXT" indicates that this is a context menu item. 
    // Context menu items are not added to the application menu bar. The second section of the menu
    // path is the name of the type that this
    // menu item is applicable to. The context menu in the Scene View for example will look for context
    // menu items for each of the following types:
    // 1. The active EditorToolContext type.
    // 2. The active EditorTool type.
    // 3. All component types in the selection. Ex, Transform, MeshRenderer, BoxCollider, etc...
    // As an example, to create a context menu item that is shown when context clicking in the Scene
    // View with a GameObject selected that has a MeshFilter component, 
    // use "CONTEXT/MeshFilter/MyMenu Item".
    [MenuItem("CONTEXT/GameObjectToolContext/Create Cube")]
    static void Init()
    {
        EditorAction.Start<CreateCube>();
    }

    public CreateCube()
    {
        // Show a preview cube at the cursor.
        m_GameObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
        // If the action is cancelled, we'll clean up the unused resource by calling Undo.PerformUndo().
        Undo.RegisterCreatedObjectUndo(m_GameObject, "Create Cube");
        // To avoid an unsightly "jump" when the cursor first moves, disable the preview until we have a
        // valid intersection point to place the object.
        m_GameObject.SetActive(false);
        m_UndoStart = Undo.GetCurrentGroup();
    }

    public override void OnSceneGUI(SceneView view)
    {
        var evt = Event.current;
        var id = GUIUtility.GetControlID(FocusType.Passive);

        if (evt.type == EventType.MouseMove)
        {
            HandleUtility.AddControl(id, 0);
            // Disable preview object so that we don't intersect with object placement ray.
            m_GameObject.SetActive(false);
            var intersected = HandleUtility.PlaceObject(evt.mousePosition, out var position, out var normal);
            m_GameObject.SetActive(true);
            if (intersected)
            {
                Undo.RecordObject(m_GameObject, "Create Cube");
                m_GameObject.transform.position = position;
                m_GameObject.transform.rotation = Quaternion.LookRotation(normal);
            }
        }

        // By checking that no mouse modifiers are active, we can allow for camera movement without 
        // breaking the action.
        if (evt.type == EventType.MouseDown && evt.modifiers == EventModifiers.None)
        {
            GUIUtility.hotControl = id;
            evt.Use();
        }

        if (GUIUtility.hotControl == id && evt.type == EventType.MouseUp)
        {
            evt.Use();
            GUIUtility.hotControl = 0;
            Finish(EditorActionResult.Success);
        }
    }

    // Since the object we want to instantiate is already in the scene, there is nothing more to do in the 
    // OnFinish function if the action exits successfully. If the action is cancelled however, we'll remove
    // the instantiated object from the scene by calling undo.
    protected override void OnFinish(EditorActionResult result)
    {
        if (result == EditorActionResult.Canceled)
        {
            Undo.PerformUndo();
            return;
        }

        Selection.activeObject = m_GameObject;
        // Merge the selection change and GameObject creation/placement to a single undo entry.
        Undo.CollapseUndoOperations(m_UndoStart);
    }
}

Probuilder 6

A prime example that leverages this new feature is ProBuilder 6!

The image below displays the Scene view context menu within the GameObject tool context for a ProBuilder Mesh.

The image below illustrates the Scene view context menu in the ProBuilder tool context for a ProBuilder Mesh face.

5 Likes

Though I probably won’t see it in action for quite some time (runtime fee), that looks like a great essential feature (same thoughts for Piercing Selection Menu)! Will it be possible to add a button to all context menus, regardless of the active tool?

1 Like

Lovely! :slight_smile:

Can’t wait to try this out now that I saw it!

Any chance you implement scene objects pipette in future ?

Hi @yasirkula :slightly_smiling_face:

You can add a menu item to the context menu regardless of which tool is active, as long as the tool falls under the GameObjectToolContext. Here’s how you can do it:

[MenuItem("CONTEXT/GameObjectToolContext/Custom GO Menu Item")]
static void TestGOMenuItem()
{
    Debug.Log("Custom GO Menu Item.");
}

If the active tool falls under a different EditorToolContext, you can achieve the same by replacing the context type in the MenuItem path.

1 Like

The runtime fee is canceled, you should be able to enjoy this new feature! :smile: (You can read about it in this blog post: Unity is Canceling the Runtime Fee)

1 Like