Extending Scene View functionality in Unity3D. Event interception
- Tutorial
Scene View in Unity3D is one of the most necessary interface elements. Everyone who has ever run Unity3D has used Scene View to visually arrange objects on the stage, as well as to configure them. An extension of the Scene View functionality may be needed to create your own level editor, edit a mesh, create your own gizmos, and much more. It is worth noting that when using Terrain in your project, its editing (drawing textures, changing heights, as well as planting trees and vegetation) is carried out using Scene View.
In order to be able to write scripts working in the Scene view, first of all, the class you are working with must be inherited from Editor or EditorWindow, which means connecting the namespace UnityEditor. This gives access to several Unity3D magic methods, such as OnGUI () and OnSceneGUI (). The OnSceneGUI method enables the Editor to manage Scene View events.
A slight lyrical digression. Among developers, there is an opinion that the “magic” methods (Update, Start, OnSceneGUI and others) are implemented using System.Reflection in C #, however, there is information that this is not so, and Unity3D core C ++ should be thanked for their work. In general, the article will be useful to anyone who sins multiple Update () in their code.
Let's go back to the interception of events. The idea is to designate the object for which we are developing the functionality, as an element of the Unity interface, on a par with the Label or Button from IMGUI.
The api has an Event class that is used for various GUI events, such as keystrokes, mouse buttons, rendering events, and Unity layout.
In addition to Event, to catch clicks you will need such a thing as control. As control in Unity any element of IMGUI can act. For example Button or Label. In order for your object to intercept a click, you must tell Unity that it is also a control and is going to participate in event processing. To do this, use the GUIUtility class .
Each control must have its own unique id, with the help of which Unity will receive all the necessary information about it. id is an int, however it is worth noting that it must be unique. You can generate a random number (other than 0) and read it as the id of your control, but there is no guarantee that the system no longer has control with the same id, and in this case the click will go to the wrong element for which you were counting. To create a unique id, use the method from the GUIUtility class
The parameter received by this method is responsible for the ability of control to receive any input from the keyboard. Information about it can be obtained in the documentation . Since keyboard input is not needed when using Scene View, the value FocusType.Passive is suitable.
Now that we’ve figured out getting an id, it's time to deal with event hooking. We can get information about the current event using the Event.current property. To get the type of event that happened in Unity do the following
This call will return an EventType value for our control. Next, you just need to decide which events you need and intercept them. The event is directly intercepted as follows
In the case of a mouse click, you say that your control is "hot", that is, it will primarily respond to mouse events, and they will not go further to other conltrol.
This aspect is described in detail in the documentation . To return access to mouse events to other control, you need to do
This will tell you that any other control can intercept the following mouse events. Events will also be available for other control if you do not use them. That is, do not mark as Used
I’ll give a complete example of a script that processes clicks on a Scene View
Someone may have a question about the need for the material described above. An example is the creation of a 2d tile level editor. You can write a similar custom editor for the map class, and when intercepting events, get the coordinates of the mouse to determine the specific child element that was clicked on. You can identify a specific element by making Raycast, which in Scene View will work exactly the same as during the course of your game.
Let me take a leave here, I hope that this article will be useful to someone.
In order to be able to write scripts working in the Scene view, first of all, the class you are working with must be inherited from Editor or EditorWindow, which means connecting the namespace UnityEditor. This gives access to several Unity3D magic methods, such as OnGUI () and OnSceneGUI (). The OnSceneGUI method enables the Editor to manage Scene View events.
A slight lyrical digression. Among developers, there is an opinion that the “magic” methods (Update, Start, OnSceneGUI and others) are implemented using System.Reflection in C #, however, there is information that this is not so, and Unity3D core C ++ should be thanked for their work. In general, the article will be useful to anyone who sins multiple Update () in their code.
Let's go back to the interception of events. The idea is to designate the object for which we are developing the functionality, as an element of the Unity interface, on a par with the Label or Button from IMGUI.
The api has an Event class that is used for various GUI events, such as keystrokes, mouse buttons, rendering events, and Unity layout.
In addition to Event, to catch clicks you will need such a thing as control. As control in Unity any element of IMGUI can act. For example Button or Label. In order for your object to intercept a click, you must tell Unity that it is also a control and is going to participate in event processing. To do this, use the GUIUtility class .
Each control must have its own unique id, with the help of which Unity will receive all the necessary information about it. id is an int, however it is worth noting that it must be unique. You can generate a random number (other than 0) and read it as the id of your control, but there is no guarantee that the system no longer has control with the same id, and in this case the click will go to the wrong element for which you were counting. To create a unique id, use the method from the GUIUtility class
public static int GUIUtility.GetControlID(FocusType focus)
The parameter received by this method is responsible for the ability of control to receive any input from the keyboard. Information about it can be obtained in the documentation . Since keyboard input is not needed when using Scene View, the value FocusType.Passive is suitable.
int controlId = GUIUtility.GetControlID(FocusType.Passive); //получение уникального id для нашего элемента интерфейса.
Now that we’ve figured out getting an id, it's time to deal with event hooking. We can get information about the current event using the Event.current property. To get the type of event that happened in Unity do the following
int controlId = GUIUtility.GetControlID(FocusType.Passive);
Event.current.GetTypeForControl(controlId)
This call will return an EventType value for our control. Next, you just need to decide which events you need and intercept them. The event is directly intercepted as follows
GUIUtility.hotControl = controlId;
In the case of a mouse click, you say that your control is "hot", that is, it will primarily respond to mouse events, and they will not go further to other conltrol.
This aspect is described in detail in the documentation . To return access to mouse events to other control, you need to do
GUIUtility.hotControl = 0;
This will tell you that any other control can intercept the following mouse events. Events will also be available for other control if you do not use them. That is, do not mark as Used
Event.current.Use();
I’ll give a complete example of a script that processes clicks on a Scene View
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(MyCustomComponent))]
public class MyCustomEditor : Editor
{
void OnSceneGUI()
{
int controlId = GUIUtility.GetControlID(FocusType.Passive);
switch (Event.current.GetTypeForControl(controlId))
{
case EventType.MouseDown:
GUIUtility.hotControl = controlId;
//Ваша логика использования события MouseDown
//Левая кнопка мыши
if(Event.current.button == 0)
{
// . . .
}
//Правая кнопка мыши
if(Event.current.button == 1)
{
// . . .
}
//Используем событие
Event.current.Use();
break;
case EventType.MouseUp:
//Возвращаем другим control доступ к событиям мыши
GUIUtility.hotControl = 0;
Event.current.Use();
break;
}
}
Someone may have a question about the need for the material described above. An example is the creation of a 2d tile level editor. You can write a similar custom editor for the map class, and when intercepting events, get the coordinates of the mouse to determine the specific child element that was clicked on. You can identify a specific element by making Raycast, which in Scene View will work exactly the same as during the course of your game.
Let me take a leave here, I hope that this article will be useful to someone.