Last active
July 17, 2021 02:01
-
-
Save empika/dc5b650fb8516c2f9489eae50aac4c1b to your computer and use it in GitHub Desktop.
SceneViewEditorBoilerplate
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using UnityEditor; | |
using UnityEditor.SceneManagement; | |
using UnityEngine; | |
using UnityEngine.SceneManagement; | |
public class SceneViewEditorBoilerplate : ScriptableObject | |
{ | |
public const string SVE_IS_ENABLED = "SVE_IS_ENABLED"; | |
private bool isLeftMouseDragging = false; | |
private bool isRightMouseDragging = false; | |
private bool isMiddleMouseDragging = false; | |
private List<Vector2> mousePositions = new List<Vector2>(); | |
private GUIStyle layerLabelStyle; | |
// Some singleton business so that we only ever have one editor tool initialised at once | |
public static SceneViewEditorBoilerplate Instance | |
{ | |
get | |
{ | |
if(instance == null) | |
{ | |
SceneViewEditorBoilerplate[] editor = Resources.FindObjectsOfTypeAll<SceneViewEditorBoilerplate>(); | |
if(editor != null && editor.Length > 0) | |
{ | |
instance = editor[0]; | |
for(int i = 1; i < editor.Length; i++) | |
{ | |
GameObject.DestroyImmediate(editor[i]); | |
} | |
} | |
} | |
return instance; | |
} | |
set | |
{ | |
instance = value; | |
} | |
} | |
private static SceneViewEditorBoilerplate instance; | |
[MenuItem("Tools/My Scene View Editor %#o", false, 2005)] | |
public static void InitTool() | |
{ | |
if (instance == null) | |
{ | |
EditorPrefs.SetBool(SVE_IS_ENABLED, true); | |
instance = ScriptableObject.CreateInstance<SceneViewEditorBoilerplate>(); | |
instance.hideFlags = HideFlags.DontSave; | |
EditorApplication.delayCall += instance.Initialize; | |
} | |
else | |
{ | |
CloseTool(); | |
} | |
SceneView.RepaintAll(); | |
} | |
public static void CloseTool() | |
{ | |
foreach(SceneViewEditorBoilerplate editor in Resources.FindObjectsOfTypeAll<SceneViewEditorBoilerplate>()) | |
editor.Close(); | |
Tools.current = Tool.Move; | |
} | |
public void Close() | |
{ | |
removeListeners(); | |
EditorPrefs.SetBool(SVE_IS_ENABLED, false); | |
DestroyImmediate(this); | |
} | |
public void Initialize() | |
{ | |
Debug.Log("Initializing SceneViewEditor"); | |
// Set up all our callbacks | |
SceneView.duringSceneGui -= OnSceneGUI; | |
SceneView.duringSceneGui += OnSceneGUI; | |
EditorApplication.update -= Update; | |
EditorApplication.update += Update; | |
Undo.undoRedoPerformed -= RepaintSceneView; | |
Undo.undoRedoPerformed += RepaintSceneView; | |
EditorApplication.playModeStateChanged -= playmodeStateChanged; | |
EditorApplication.playModeStateChanged += playmodeStateChanged; | |
EditorSceneManager.sceneClosing -= sceneClosing; | |
EditorSceneManager.sceneClosing += sceneClosing; | |
EditorSceneManager.sceneOpened -= sceneOpened; | |
EditorSceneManager.sceneOpened += sceneOpened; | |
layerLabelStyle = new GUIStyle(); | |
layerLabelStyle.alignment = TextAnchor.LowerCenter; | |
layerLabelStyle.fontSize = 18; | |
layerLabelStyle.normal.textColor = Color.green; | |
// Set our tool to None... this is only a visual thing in the toolbar | |
// See the click handling below on how to actually bypass unity's tools | |
Tools.current = Tool.None; | |
instance = this; | |
Update(); | |
RepaintSceneView(); | |
} | |
private void OnDestroy() | |
{ | |
removeListeners(); | |
} | |
private void removeListeners() | |
{ | |
SceneView.duringSceneGui -= OnSceneGUI; | |
EditorApplication.update -= Update; | |
Undo.undoRedoPerformed -= RepaintSceneView; | |
EditorSceneManager.sceneClosing -= sceneClosing; | |
EditorSceneManager.sceneOpened -= sceneOpened; | |
} | |
private void playmodeStateChanged(PlayModeStateChange state) | |
{ | |
if (state == PlayModeStateChange.EnteredEditMode) | |
{ | |
// Do some stuff here. For example I have a bunch of objects in the sceneview that I set to hidden (tile brush etc) | |
// I need to respawn those when we are editing | |
Update(); | |
RepaintSceneView(); | |
} | |
else if (state == PlayModeStateChange.ExitingEditMode) | |
{ | |
// Do some stuff here. For example I have a bunch of objects in the sceneview that I set to hidden (tile brush etc) | |
// I need to remove those when we are playing | |
} | |
} | |
private void sceneClosing(Scene scene, bool removingScene) | |
{ | |
OnDestroy(); | |
} | |
private void sceneOpened(Scene scene, OpenSceneMode mode) | |
{ | |
Initialize(); | |
} | |
void RepaintSceneView() | |
{ | |
SceneView.RepaintAll(); | |
} | |
public void Update() | |
{ | |
if (EditorApplication.isPlaying) | |
{ | |
return; | |
} | |
// Do some updating stuff here if you need to | |
} | |
public void OnSceneGUI(SceneView sceneView) | |
{ | |
if (EditorApplication.isPlaying) | |
{ | |
return; | |
} | |
bool isCurrentView = sceneView == SceneView.lastActiveSceneView; | |
if (isCurrentView) | |
{ | |
Handles.BeginGUI(); | |
DrawSceneGUI(); | |
Handles.EndGUI(); | |
} | |
HandleUtility.Repaint(); | |
} | |
public void DrawSceneGUI() | |
{ | |
Rect screenRect = SceneView.lastActiveSceneView.position; | |
GUI.Label(new Rect(screenRect.width / 2, 10, 200, 40), "Scene View Editor", layerLabelStyle); | |
// This is kinda like the update in the normal monobehaviours, but for the sceneview | |
// The mouse handling gets quite complicated so we just handle them all, all the time | |
handleClick(screenRect); | |
handleRightClick(); | |
} | |
private void handleClick(Rect screenRect) | |
{ | |
Event e = Event.current; | |
if (screenRect.Contains(e.mousePosition)) | |
{ | |
// This control id stuff is pretty confusing and i still don't 100% understand it | |
// We get a control id from unity, to say we're doing something | |
// Don't know why we check GUIUtility.hotControl != 0, but if it is then we want to tell it our | |
int controlId = GUIUtility.GetControlID (FocusType.Passive); | |
if (e.type == EventType.MouseDown && Event.current.button == 0 && GUIUtility.hotControl != 0) | |
{ | |
GUIUtility.hotControl = controlId; | |
// This is the start of the drag, for whatever reason unity's EventType.MouseDrag doesnt quite work | |
// how you'd expect so we want to track this ourselves | |
isLeftMouseDragging = true; | |
} | |
else if (e.type == EventType.MouseUp && Event.current.button == 0 && GUIUtility.hotControl == controlId) | |
{ | |
if (mousePositions.Count == 0) | |
{ | |
// We got a click! We're not dragging or anything so just do something normal! | |
// ... | |
// or we can check for modifiers first... | |
if (e.control) | |
{ | |
// we got a ctrl click | |
} | |
else | |
{ | |
// just a normal click | |
} | |
// We need to set the hotcontrol to 0 to let unity know we're all done | |
GUIUtility.hotControl = 0; | |
} | |
// We're no longer dragging | |
isLeftMouseDragging = false; | |
mousePositions.Clear(); | |
// This is where we consume the mouse click, so that unity doesnt do it's normal stuff with selecting or moving etc | |
e.Use(); | |
RepaintSceneView(); | |
} | |
else if (e.type == EventType.MouseDrag && Event.current.button == 0 && GUIUtility.hotControl == controlId) | |
{ | |
// Are we dragging... like we set earlier | |
if (isLeftMouseDragging) | |
{ | |
// Yes we are, so add this mouse position to the list | |
mousePositions.Add(e.mousePosition); | |
// We're dragging so we can do stuff here. In my tile map, I create a tile here based on the position. | |
// clear those mouse positions | |
mousePositions.Clear(); | |
// This is where we consume the mouse drag, so that unity doesnt do it's normal stuff with selecting or moving etc | |
e.Use(); | |
RepaintSceneView(); | |
} | |
} | |
} | |
} | |
private void handleRightClick() | |
{ | |
Event e = Event.current; | |
if (e.type == EventType.MouseDown && Event.current.button == 1) | |
{ | |
isRightMouseDragging = false; | |
} | |
else if (e.type == EventType.MouseDrag && Event.current.button == 1 && e.command ) | |
{ | |
isRightMouseDragging = true; | |
// Note that here the command key is pressed and we are dragging, I don't consume the event with e.Use(). | |
// This is so that can do the normal camera orbit in the scene view, but it requires the command key to be pressed, | |
// I want a right click to select the tile in the tile map without affecting the camera at all | |
} | |
else if (e.type == EventType.MouseDrag && Event.current.button == 1) | |
{ | |
isRightMouseDragging = true; | |
// This is where we consume the mouse drag, so that unity doesnt do it's normal stuff with selecting or moving etc | |
e.Use(); | |
} | |
else if (e.type == EventType.MouseUp && Event.current.button == 1) | |
{ | |
if (isRightMouseDragging == false) | |
{ | |
// This is just a normal right click | |
} | |
isRightMouseDragging = false; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment