UnityPreviewEditor
UnityPreviewEditor: use the previewrendertutility to create a preview window.
Unity version: Unity 2020.3.18f1c1
brief introduction
- Example - > previewrenderwindow opens
- design sketch:
code
view code// PreviewRenderWindow.cs is responsible for customizing the management interface using System.Collections; using System.Collections.Generic; using UnityEditor; using UnityEngine; public class PreviewRenderWindow : EditorWindow { [MenuItem("Example/PreviewRenderWindow")] static void ShowWindow() { GetWindow<PreviewRenderWindow>("PreviewRenderWindow").Show(); } GameObject _gameObject; GameObject _lastGameObject; PreviewRenderEditor _editor; bool _load = true; Vector2 _lightRot; Vector2 _lastLightRot; void OnGUI() { _gameObject = (GameObject) EditorGUILayout.ObjectField("Preview preform", _gameObject, typeof(GameObject), true); _lightRot = EditorGUILayout.Vector2Field("Light source direction", _lightRot); if (_editor == null) { _editor = Editor.CreateEditor(this, typeof(PreviewRenderEditor)) as PreviewRenderEditor; } if(_editor) { if (_lastLightRot != _lightRot) { _lastLightRot = _lightRot; _editor.RefreshLightRot(_lightRot); } _editor.DrawPreview(GUILayoutUtility.GetRect(400, 400)); } if (_gameObject && _load) { _editor.RefreshPreviewInstance(_gameObject); _load = false; _lastGameObject = _gameObject; } if (_lastGameObject != _gameObject) { _load = true; } } }
// PreviewRenderEditor.cs is responsible for previewing the interface using System.Collections; using System.Collections.Generic; using UnityEditor; using UnityEngine; public class PreviewRenderEditor : Editor { private PreviewRenderUtility _previewRenderUtility; private GameObject _previewInstance; private GameObject _targetObj; private static bool _loaded = true; private Vector2 _drag = new Vector2(250f, -30f); private Vector2 _lightRot = new Vector2(180f, 0); public void RefreshLightRot(Vector2 rot) { _lightRot = rot; } public void RefreshPreviewInstance(GameObject obj) { _targetObj = obj; if (_previewInstance) UnityEngine.Object.DestroyImmediate(_previewInstance); _previewInstance = null; _loaded = true; } private void OnEnable() { if (_previewRenderUtility == null) { _previewRenderUtility = new PreviewRenderUtility(); } } private void OnDisable() { if (_previewRenderUtility != null) { // Cleaning must be carried out, otherwise there will be residual objects _previewInstance = null; _previewRenderUtility.Cleanup(); _previewRenderUtility = null; } } public override void OnPreviewGUI(Rect r, GUIStyle background) { // _ loaded Make sure the object is loaded only once if (_loaded && _targetObj) { _previewInstance = Instantiate(_targetObj as GameObject, Vector3.zero, Quaternion.identity); // AddSingleGO add object _previewRenderUtility.AddSingleGO(_previewInstance); _loaded = false; } // Get drag vector _drag = Drag2D(_drag, r); // Only when the event is drawing if (Event.current.type == EventType.Repaint) { _previewRenderUtility.BeginPreview(r, background); //Adjust camera position and angle Camera camera = _previewRenderUtility.camera; var cameraTran = camera.transform; cameraTran.position = Vector2.zero; cameraTran.rotation = Quaternion.Euler(new Vector3(-_drag.y, -_drag.x, 0)); cameraTran.position = cameraTran.forward * -6f; var pos = cameraTran.position; cameraTran.position = new Vector3(pos.x, pos.y + 0.6f, pos.z); EditorUtility.SetCameraAnimateMaterials(camera, true); camera.cameraType = CameraType.Preview; camera.enabled = false; camera.clearFlags = CameraClearFlags.Skybox; camera.fieldOfView = 30; camera.farClipPlane = 10.0f; camera.nearClipPlane = 2.0f; camera.backgroundColor = new Color(49.0f / 255.0f, 77.0f / 255.0f, 121.0f / 255.0f, 0f); // //Set lighting data _previewRenderUtility.lights[0].intensity = 0.7f; _previewRenderUtility.lights[0].transform.rotation = Quaternion.Euler(_lightRot.x, _lightRot.y, 0f); _previewRenderUtility.lights[1].intensity = 0.7f; _previewRenderUtility.lights[1].transform.rotation = Quaternion.Euler(_lightRot.x, _lightRot.y, 0f); _previewRenderUtility.ambientColor = new Color(0.3f, 0.3f, 0.3f, 0f); // camera.transform.LookAt(_previewInstance.transform); // Camera rendering camera.Render(); // End and draw _previewRenderUtility.EndAndDrawPreview(r); } } // Drag2D from source code private static int sliderHash = "Slider".GetHashCode(); public static Vector2 Drag2D(Vector2 scrollPosition, Rect position) { // Get a unique controlID each time int controlID = GUIUtility.GetControlID(sliderHash, FocusType.Passive); Event current = Event.current; // Get the event corresponding to the controlID switch (current.GetTypeForControl(controlID)) { case EventType.MouseDown: { bool flag = position.Contains(current.mousePosition) && position.width > 50f; if (flag) { // Press and drag the mouse out of the preview window, and the preview object can still rotate GUIUtility.hotControl = controlID; // Adopt event current.Use(); // Let the mouse drag out of the screen and come out from the other side EditorGUIUtility.SetWantsMouseJumping(1); } break; } case EventType.MouseUp: { bool flag2 = GUIUtility.hotControl == controlID; if (flag2) { GUIUtility.hotControl = 0; } EditorGUIUtility.SetWantsMouseJumping(0); break; } case EventType.MouseDrag: { bool flag3 = GUIUtility.hotControl == controlID; if (flag3) { // shift acceleration scrollPosition -= current.delta * (float) (current.shift ? 3 : 1) / Mathf.Min(position.width, position.height) * 140f; // If either of the following two items is missing, the update will be delayed and cannot be updated in real time during the drag process // It is not redrawn until the repaint event is triggered current.Use(); GUI.changed = true; } break; } } return scrollPosition; } }
Unity preview interface
Preview interface
On the Unity editor interface, you can see that in addition to the Game view and Scene view, other views will also appear where 3D objects are drawn, such as the preview window of the viewer. When the grid is selected, the grid will be previewed, as shown below:
The methods of drawing are performed using the previewrendertutility class of unpublished documents of UnityEditor.
Viewer preview interface
For the Preview window of asset or script implementation, refer to the documentation of Editor class and overload the interface with Preview keyword.
Enable preview function
The viewer window of the default script object does not have a preview window, as shown below:
To open the preview window, you must create your own viewer window class, and then reload the HasPreviewGUI interface. The complete code is as follows:
using UnityEngine; public class PreviewExample : MonoBehaviour { } using UnityEditor; using UnityEngine; [CustomEditor(typeof(PreviewExample))] public class PreviewExampleInspector : Editor { public override bool HasPreviewGUI() { return true; } }
You can see a black preview window, as shown below:
Title block drawing
The name of the object is displayed by default. You can change the title name by overloading the GetPreviewTitle interface:
public override GUIContent GetPreviewTitle() { return new GUIContent("preview"); }
Other information or buttons can be drawn on the right side of the title bar. Overload the OnPreviewSettings interface to control the preview window:
public override void OnPreviewSettings() { GUILayout.Label("text", "preLabel"); GUILayout.Button("Button", "preButton"); }
Drawing of preview content
Finally, the drawing of preview content only needs to overload OnPreviewGUI interface:
public override void OnPreviewGUI(Rect r, GUIStyle background) { GUI.Box(r, "Preview"); }
The final display is as follows:
Camera rendering
Not only can the drawing control be carried out in the preview window, but also three-dimensional objects can be drawn. In essence, it is to draw the information illuminated by an independent camera, such as the animation clip preview window:
The mouse can drag and rotate, and can also see other directions, just like operating the camera.
This is achieved through the previewrendertutility. There is no official document for this class, which can be learned through the sharing of others on the Internet and the internal use of UnityEditor.
Basic drawing
The construction and destruction of previewrendertutility, as well as the construction and destruction of objects to be previewed, and call drawing, surrounded by BeginPreview and EndAndDrawPreview, in which the camera is rendered Render is called with the following code:
view codeusing UnityEditor; using UnityEngine; [CustomEditor(typeof(PreviewExample))] public class PreviewExampleInspector : Editor { private GameObject _lastGameObj; private bool _canRefreshPreviewGo = false; public override void OnInspectorGUI() { // target the object of the current operation PreviewExample pe = (PreviewExample) target; pe.previewGo = EditorGUILayout.ObjectField("Preview target", pe.previewGo, typeof(GameObject)) as GameObject; if (pe.previewGo != _lastGameObj) { _lastGameObj = pe.previewGo; _canRefreshPreviewGo = true; } serializedObject.ApplyModifiedProperties(); } public override bool HasPreviewGUI() { return true; } public override GUIContent GetPreviewTitle() { return new GUIContent("preview"); } public override void OnPreviewSettings() { GUILayout.Label("text", "preLabel"); GUILayout.Button("Button", "preButton"); } public override void OnPreviewGUI(Rect r, GUIStyle background) { InitPreview(); if (Event.current.type != EventType.Repaint) { return; } _previewRenderUtility.BeginPreview(r, background); Camera camera = _previewRenderUtility.camera; if (_previewInstance) { camera.transform.position = _previewInstance.transform.position + new Vector3(0, 5f, 3f); camera.transform.LookAt(_previewInstance.transform); } camera.Render(); _previewRenderUtility.EndAndDrawPreview(r); } private PreviewRenderUtility _previewRenderUtility; private GameObject _previewInstance; private void InitPreview() { if (_previewRenderUtility == null) { // The parameter true represents the game object in the painting scene _previewRenderUtility = new PreviewRenderUtility(true); // Set some parameters of the camera _previewRenderUtility.cameraFieldOfView = 30f; } if (_canRefreshPreviewGo) { _canRefreshPreviewGo = false; // Create preview GameObjects CreatePreviewInstances(); } } private void DestroyPreview() { if (_previewRenderUtility != null) { // Be sure to clean it so that the generated camera objects will not remain _previewRenderUtility.Cleanup(); _previewRenderUtility = null; } } private void CreatePreviewInstances() { DestroyPreviewInstances(); // Draw preview GameObject if (_lastGameObj) { _previewInstance = Instantiate(_lastGameObj); _previewRenderUtility.AddSingleGO(_previewInstance); } } private void DestroyPreviewInstances() { if (_previewInstance) { DestroyImmediate(_previewInstance); } _previewInstance = null; } void OnDestroy() { DestroyPreviewInstances(); DestroyPreview(); } }
The final effect is as follows:
Drag rotation
Drag the mouse in the preview window to rotate for preview, just like the Cube object preview. To rotate the camera, you need to know the center of the game object before you can rotate around it.
view codeThe final effect is shown in the figure below:private static int sliderHash = "Slider".GetHashCode(); private static Vector2 Drag2D(Vector2 scrollPosition, Rect position) { // Get a unique controlID each time int controlID = GUIUtility.GetControlID(sliderHash, FocusType.Passive); Event current = Event.current; // Get the event corresponding to the controlID switch (current.GetTypeForControl(controlID)) { case EventType.MouseDown: { bool flag = position.Contains(current.mousePosition) && position.width > 50f; if (flag) { // Press and drag the mouse out of the preview window, and the preview object can still rotate GUIUtility.hotControl = controlID; // Adopt event current.Use(); // Let the mouse drag out of the screen and come out from the other side EditorGUIUtility.SetWantsMouseJumping(1); } break; } case EventType.MouseUp: { bool flag2 = GUIUtility.hotControl == controlID; if (flag2) { GUIUtility.hotControl = 0; } EditorGUIUtility.SetWantsMouseJumping(0); break; } case EventType.MouseDrag: { bool flag3 = GUIUtility.hotControl == controlID; if (flag3) { // shift acceleration scrollPosition -= current.delta * (float) (current.shift ? 3 : 1) / Mathf.Min(position.width, position.height) * 140f; // If either of the following two items is missing, the update will be delayed and cannot be updated in real time during the drag process // It is not redrawn until the repaint event is triggered current.Use(); GUI.changed = true; } break; } } return scrollPosition; }
Preview of custom view
The preview on the user-defined view can be drawn in a way similar to the above, or you can create a corresponding viewer class and directly call the drawing preview interface. The code is as follows:
view codeOpen the test window, as shown in the following figure:using UnityEngine; using UnityEditor; public class PreviewExampleWindow : EditorWindow { private Editor m_Editor; [MenuItem("Example/PreviewExample")] static void ShowWindow() { GetWindow<PreviewExampleWindow>("PreviewExample"); } private void OnDestroy() { if (m_Editor != null) { DestroyImmediate(m_Editor); } m_Editor = null; } void OnGUI() { if (m_Editor == null) { // The first parameter doesn't matter here for the time being, because the editor doesn't take the target object m_Editor = Editor.CreateEditor(this, typeof(PreviewExampleInspector)); } m_Editor.DrawPreview(GUILayoutUtility.GetRect(300, 200)); } }