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.