Last active
December 8, 2023 09:50
-
-
Save calderarchinuk/16d5b5332b9f1fba2e9d6917d6c82cc5 to your computer and use it in GitHub Desktop.
Unity Editor Assistant. Adds a context-sensitive right-click menu, automatically disables auto-generate lightmaps
This file contains 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; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using UnityEditor; | |
using System.Reflection; | |
using UnityEditor.SceneManagement; | |
[InitializeOnLoad] | |
public class UnityAssistant | |
{ | |
//IMPROVEMENT custom editor preferences should have field for 'new scene' prefab to spawn | |
static UnityAssistant() | |
{ | |
SceneView.onSceneGUIDelegate += OnSceneGUI; | |
UnityEditor.SceneManagement.EditorSceneManager.newSceneCreated += EditorSceneManager_newSceneCreated; | |
UnityEditor.EditorApplication.playModeStateChanged += UnityEditor_EditorApplication_playModeStateChanged; | |
ShowSelectionLine = EditorPrefs.GetBool("selectionLine"); | |
ShowGizmoToGround = EditorPrefs.GetBool("gizmoToGround"); | |
} | |
enum PlayFromHereMode | |
{ | |
None = 0, | |
FindByTag = 1, | |
FindByName = 2, | |
Spawn = 3 | |
} | |
static GameObject playerPrefab; | |
static PlayFromHereMode playFromHereMode; | |
static string playerPrefabPath; | |
private static bool prefsLoaded = false; | |
static string findString; | |
static bool ShowSelectionLine; | |
static bool ShowGizmoToGround; | |
[PreferenceItem("Assistant")] | |
public static void PreferencesGUI() | |
{ | |
if (!prefsLoaded) | |
{ | |
prefsLoaded = true; | |
playFromHereMode = (PlayFromHereMode)EditorPrefs.GetInt("playFromHereMode"); | |
playerPrefabPath = EditorPrefs.GetString("playerPrefabPath"); | |
findString = EditorPrefs.GetString("findString"); | |
ShowGizmoToGround = EditorPrefs.GetBool("gizmoToGround"); | |
ShowSelectionLine = EditorPrefs.GetBool("selectionLine"); | |
if (!string.IsNullOrEmpty(playerPrefabPath)) | |
{ | |
playerPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(playerPrefabPath); | |
} | |
} | |
playFromHereMode = (PlayFromHereMode)EditorGUILayout.EnumPopup("Play From Here Type",playFromHereMode); | |
if (playFromHereMode == PlayFromHereMode.Spawn) | |
{ | |
playerPrefab = (GameObject)EditorGUILayout.ObjectField("Player Prefab",playerPrefab,typeof(GameObject),false); | |
} | |
else if (playFromHereMode == PlayFromHereMode.FindByName || playFromHereMode == PlayFromHereMode.FindByTag) | |
{ | |
findString = EditorGUILayout.TextField("Find String",findString); | |
} | |
ShowGizmoToGround = EditorGUILayout.Toggle("Show Gizmo To Ground",ShowGizmoToGround); | |
ShowSelectionLine = EditorGUILayout.Toggle("Show Selection Line",ShowSelectionLine); | |
if (GUILayout.Button("Save Changes")) | |
{ | |
EditorPrefs.SetBool("gizmoToGround",ShowGizmoToGround); | |
EditorPrefs.SetBool("selectionLine",ShowSelectionLine); | |
EditorPrefs.SetInt("playFromHereMode",(int)playFromHereMode); | |
EditorPrefs.SetString("playerPrefabPath",AssetDatabase.GetAssetPath(playerPrefab)); | |
EditorPrefs.SetString("findString",findString); | |
} | |
} | |
static void UnityEditor_EditorApplication_playModeStateChanged (PlayModeStateChange obj) | |
{ | |
if (obj == PlayModeStateChange.EnteredPlayMode) | |
{ | |
var startpos = SessionState.GetVector3("startpos",Vector3.zero); | |
if (Vector3.Distance(startpos,Vector3.zero)<0.1f){return;} | |
SessionState.EraseVector3("startpos"); | |
Debug.Log("Unity Assistant: Entered Play"); | |
PlayFromHereMode loadMode = (PlayFromHereMode)EditorPrefs.GetInt("playFromHereMode"); | |
if (loadMode == PlayFromHereMode.Spawn) | |
{ | |
string path = EditorPrefs.GetString("playerPrefabPath"); | |
Debug.Log("spawn things " + path); | |
var playerPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(path); | |
if (playerPrefab == null){return;} | |
GameObject.Instantiate(playerPrefab,startpos,Quaternion.identity); | |
} | |
else if (loadMode == PlayFromHereMode.FindByTag) | |
{ | |
var go = GameObject.FindGameObjectWithTag(EditorPrefs.GetString("findString")); | |
if (go != null) | |
{ | |
go.transform.position = startpos; | |
} | |
} | |
else if (loadMode == PlayFromHereMode.FindByName) | |
{ | |
var go = GameObject.Find(EditorPrefs.GetString("findString")); | |
if (go != null) | |
{ | |
go.transform.position = startpos; | |
} | |
} | |
} | |
} | |
//disables auto-generate lighting when making a new scene | |
static void EditorSceneManager_newSceneCreated (UnityEngine.SceneManagement.Scene scene, NewSceneSetup setup, NewSceneMode mode) | |
{ | |
var lightModeUtilityType = System.Type.GetType("UnityEditor.LightModeUtil, UnityEditor"); | |
var method_GetInstance = lightModeUtilityType.GetMethod("Get", BindingFlags.Public | BindingFlags.Static); | |
var instance = method_GetInstance.Invoke(null, new object[] { }); | |
var method_SetWorkflow = lightModeUtilityType.GetMethod("SetWorkflow", BindingFlags.Public | BindingFlags.Instance); | |
method_SetWorkflow.Invoke(instance, new object[] { false }); | |
var method_flush = lightModeUtilityType.GetMethod("Flush", BindingFlags.Public | BindingFlags.Instance); | |
method_flush.Invoke(instance, new object[] { }); | |
} | |
class NewObjectCommand | |
{ | |
public GameObject obj; | |
public string name; | |
public Vector3 position; | |
public System.Type component; | |
} | |
class SimpleCommand | |
{ | |
public Vector3 position; | |
public Transform transform; | |
} | |
static Vector3 startCamPos; | |
static Quaternion startCamRot; | |
static Vector2 startMousePos; | |
static void OnSceneGUI(SceneView sceneView) | |
{ | |
Event e = Event.current; | |
Ray r = HandleUtility.GUIPointToWorldRay( e.mousePosition ); | |
RaycastHit hit = new RaycastHit(); | |
Plane zeroplane = new Plane(Vector3.up,Vector2.zero); | |
float zeroplaneDistance = 0; | |
Vector3 hitPoint = Vector3.zero; | |
Vector3 normal = Vector3.up; | |
Color hitColor = Color.white; | |
if (Physics.Raycast(r,out hit, 1000)) | |
{ | |
hitPoint = hit.point; | |
normal = hit.normal; | |
hitColor = Color.red; | |
} | |
else if (zeroplane.Raycast(r,out zeroplaneDistance)) | |
{ | |
hitPoint = r.GetPoint(zeroplaneDistance); | |
} | |
Handles.color = hitColor; | |
Handles.DrawLine(hitPoint,hitPoint+normal); | |
DrawHandleCircle(hitPoint,normal,1,hitColor); | |
Handles.color = Color.white; | |
if (ShowSelectionLine) | |
{ | |
foreach(var v in Selection.transforms) | |
{ | |
Handles.DrawDottedLine(hitPoint,v.position,5f); | |
} | |
} | |
if (ShowGizmoToGround) | |
{ | |
RaycastHit rhit = new RaycastHit(); | |
foreach(var v in Selection.transforms) | |
{ | |
if (Physics.Raycast(v.position,Vector3.down,out rhit,20)) | |
{ | |
Handles.DrawLine(v.position,rhit.point); | |
Handles.DrawWireCube(rhit.point,new Vector3(0.5f,0,0.5f)); | |
Handles.DrawLine(rhit.point,rhit.point + v.forward*0.5f); | |
} | |
} | |
} | |
/*if (ShowPrefabPlacement) | |
{ | |
var selectedAsset = Selection.GetFiltered<GameObject>(SelectionMode.Assets); | |
if (selectedAsset != null && selectedAsset.Length > 0) | |
{ | |
var mf = selectedAsset[0].GetComponentInChildren<MeshFilter>(); | |
if (mf != null) | |
{ | |
var DefaultMaterial = new Material(Shader.Find("Diffuse")); | |
Graphics.DrawMesh(mf.sharedMesh,hitPoint,Quaternion.identity,DefaultMaterial,0); | |
} | |
} | |
}*/ | |
if (e.button == 1 && e.type == EventType.MouseDown) | |
{ | |
startCamPos = sceneView.camera.transform.position; | |
startCamRot = sceneView.camera.transform.rotation; | |
startMousePos = e.mousePosition; | |
} | |
if (e.button == 1 && e.type == EventType.MouseUp) | |
{ | |
if (TransformApproximately(sceneView.camera.transform,startCamPos,startCamRot) && Vector2Approximately(e.mousePosition,startMousePos) ) | |
{ | |
DisplayMenu(hitPoint,hit); | |
e.Use(); | |
} | |
} | |
} | |
static void DisplayMenu(Vector3 hitPoint, RaycastHit hit) | |
{ | |
GenericMenu menu = new GenericMenu(); | |
bool hasSelection = Selection.activeGameObject != null; | |
bool selectionIsInScene = Selection.activeTransform != null; | |
int selectionCount = Selection.gameObjects.Length; | |
bool selectionIsPrefab = hasSelection && PrefabUtility.GetPrefabType(Selection.activeGameObject) != PrefabType.None; | |
bool cursorHitSomething = hit.collider != null; | |
bool hitIsPrefab = cursorHitSomething && PrefabUtility.GetPrefabType(hit.collider) != PrefabType.None; | |
if (cursorHitSomething && hitIsPrefab) | |
{ | |
menu.AddItem(new GUIContent("Select Prefab"), false, SelectPrefab, hit.collider.gameObject); | |
} | |
if (selectionCount == 1 && hasSelection && selectionIsPrefab && !selectionIsInScene) | |
{ | |
NewObjectCommand newGameObjectCommand = new NewObjectCommand(); | |
newGameObjectCommand.obj = Selection.activeGameObject; | |
newGameObjectCommand.position = hitPoint; | |
menu.AddItem(new GUIContent("Place "+ Selection.activeGameObject.name + " Here"), false, PlacePrefab, newGameObjectCommand); | |
} | |
if (selectionCount == 1 && hasSelection && selectionIsPrefab && selectionIsInScene) | |
{ | |
NewObjectCommand newGameObjectCommand = new NewObjectCommand(); | |
newGameObjectCommand.obj = Selection.activeTransform.gameObject; | |
newGameObjectCommand.position = hitPoint; | |
menu.AddItem(new GUIContent("Place "+ Selection.activeTransform.gameObject.name + " Here"), false, PlaceScenePrefab, newGameObjectCommand); | |
} | |
if (cursorHitSomething && hasSelection && selectionIsInScene) | |
{ | |
menu.AddSeparator("- Hierarchy"); | |
if (hit.collider.gameObject != Selection.activeTransform) | |
menu.AddItem(new GUIContent("Parent to " + Selection.activeTransform.gameObject.name), false, MakeParent, hit.collider.gameObject); | |
else | |
menu.AddDisabledItem(new GUIContent("parent to self")); | |
} | |
if (hasSelection) | |
{ | |
NewObjectCommand newParent = new NewObjectCommand(); | |
newParent.position = hitPoint; | |
menu.AddItem(new GUIContent("Parent To Empty Here"), false, NewParent,newParent); | |
} | |
if (hasSelection) | |
{ | |
NewObjectCommand newParent = new NewObjectCommand(); | |
Vector3 averageSelectionPosition = Vector3.zero; | |
foreach(var v in Selection.transforms) | |
{ | |
averageSelectionPosition += v.position; | |
} | |
averageSelectionPosition /= Selection.transforms.Length; | |
newParent.position = averageSelectionPosition; | |
menu.AddItem(new GUIContent("Parent To Empty At Selection"), false, NewParent,newParent); | |
} | |
if (hasSelection) | |
{ | |
menu.AddItem(new GUIContent("Clear Selection Parent"), false, ClearParent); | |
} | |
else | |
{ | |
menu.AddDisabledItem(new GUIContent("Clear Parent")); | |
} | |
if (hasSelection && !selectionIsPrefab) | |
{ | |
NewObjectCommand newGameObjectCommand = new NewObjectCommand(); | |
newGameObjectCommand.obj = Selection.activeGameObject; | |
newGameObjectCommand.position = hitPoint; | |
menu.AddItem(new GUIContent("Copy "+ Selection.activeGameObject.name + " Here"), false, PlaceGameObject, newGameObjectCommand); | |
} | |
//new gameobjects with components | |
NewObjectCommand newLightCommand = new NewObjectCommand(); | |
newLightCommand.position = hitPoint; | |
newLightCommand.component = typeof(Light); | |
newLightCommand.name = "Light"; | |
menu.AddItem(new GUIContent("Place Light Here"), false, NewGameObject, newLightCommand); | |
NewObjectCommand newAudioSourceCommand = new NewObjectCommand(); | |
newAudioSourceCommand.position = hitPoint; | |
newAudioSourceCommand.component = typeof(AudioSource); | |
newAudioSourceCommand.name = "Audio Source"; | |
menu.AddItem(new GUIContent("Place Audiosource Here"), false, NewGameObject, newAudioSourceCommand); | |
if (hasSelection || cursorHitSomething) | |
menu.AddSeparator(""); | |
if (hasSelection) | |
{ | |
menu.AddItem(new GUIContent("Snap Selection To Ground"),false,SnapToGround); | |
SimpleCommand sc = new SimpleCommand(); | |
sc.position = hitPoint; | |
menu.AddItem(new GUIContent("Teleport Here"),false,TeleportHere,sc); | |
menu.AddItem(new GUIContent("Look Here"),false,LookHere,sc); | |
} | |
else if (cursorHitSomething) | |
{ | |
SimpleCommand sc = new SimpleCommand(); | |
sc.transform = hit.collider.transform; | |
menu.AddItem(new GUIContent("Snap "+hit.collider.gameObject.name+" To Ground"),false,SnapHitToGround,sc); | |
} | |
Vector3 startFromHereInfo = hitPoint; | |
menu.AddSeparator(""); | |
menu.AddItem(new GUIContent("Play From Here"),false,PlayFromHere,startFromHereInfo); | |
menu.ShowAsContext(); | |
} | |
#region MenuCommands | |
static void PlaceGameObject(object obj) | |
{ | |
NewObjectCommand newCommand = (NewObjectCommand)obj; | |
var go = Object.Instantiate(newCommand.obj,newCommand.position,Quaternion.identity); | |
Undo.RegisterCreatedObjectUndo(go,"Copy GameObject"); | |
} | |
static void PlaceScenePrefab(object obj) | |
{ | |
NewObjectCommand newCommand = (NewObjectCommand)obj; | |
var prefab = (GameObject)PrefabUtility.GetPrefabParent((GameObject)newCommand.obj); | |
var instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab); | |
instance.transform.position = newCommand.position; | |
Undo.RegisterCreatedObjectUndo(instance,"Create Prefab"); | |
} | |
static void PlacePrefab(object obj) | |
{ | |
NewObjectCommand newCommand = (NewObjectCommand)obj; | |
var instance = (GameObject)PrefabUtility.InstantiatePrefab(newCommand.obj); | |
if (instance != null) | |
{ | |
instance.transform.position = newCommand.position; | |
Undo.RegisterCreatedObjectUndo(instance,"Create Prefab"); | |
} | |
else | |
{ | |
Debug.Log("null"); | |
} | |
} | |
static void SelectPrefab(object gameobject) | |
{ | |
Selection.activeGameObject = (GameObject)PrefabUtility.GetPrefabParent((GameObject)gameobject); | |
} | |
static void NewGameObject(object typeOfComponent) | |
{ | |
NewObjectCommand t = (NewObjectCommand)typeOfComponent; | |
GameObject go = new GameObject(t.name); | |
go.transform.position = t.position; | |
go.AddComponent(t.component); | |
Undo.RegisterCreatedObjectUndo(go,"Undo Create GameObject"); | |
Selection.activeGameObject = go; | |
} | |
static void MakeParent(object go) | |
{ | |
Undo.RegisterCompleteObjectUndo((GameObject)go,"Make Parent"); | |
Selection.activeGameObject.transform.SetParent(((GameObject)go).transform); | |
} | |
static void NewParent(object parentInfo) | |
{ | |
NewObjectCommand t = (NewObjectCommand)parentInfo; | |
GameObject go = new GameObject("New Group"); | |
go.transform.position = t.position; | |
foreach(var v in Selection.transforms) | |
{ | |
v.SetParent(go.transform); | |
} | |
Selection.activeTransform = go.transform; | |
} | |
static void ClearParent() | |
{ | |
Undo.RecordObjects(Selection.transforms,"Clear Parent"); | |
foreach (Transform t in Selection.transforms) | |
{ | |
t.SetParent(null); | |
} | |
} | |
static void TeleportHere(object info) | |
{ | |
var sc = (SimpleCommand)info; | |
Undo.RecordObjects(Selection.transforms,"Teleport Here"); | |
foreach (Transform t in Selection.transforms) | |
{ | |
t.position = sc.position; | |
} | |
} | |
static void LookHere(object info) | |
{ | |
var sc = (SimpleCommand)info; | |
Undo.RecordObjects(Selection.transforms,"Look Here"); | |
foreach (Transform t in Selection.transforms) | |
{ | |
t.LookAt(sc.position); | |
} | |
} | |
static void SnapToGround() | |
{ | |
Undo.RecordObjects(Selection.transforms,"Snap to Ground"); | |
foreach (Transform t in Selection.transforms) | |
{ | |
RaycastHit hit; | |
if (Physics.Raycast(t.position,Vector3.down,out hit, 100f)) | |
{ | |
t.position = hit.point; | |
} | |
} | |
} | |
static void SnapHitToGround(object simpleCommand) | |
{ | |
var sc = (SimpleCommand)simpleCommand; | |
Undo.RegisterCompleteObjectUndo(sc.transform,"Snap to Ground"); | |
RaycastHit hit; | |
if (Physics.Raycast(sc.transform.position,Vector3.down,out hit, 100f)) | |
{ | |
sc.transform.position = hit.point; | |
} | |
} | |
static void PlayFromHere(object startFromHereInfo) | |
{ | |
SessionState.SetVector3("startpos",(Vector3)startFromHereInfo); | |
EditorApplication.ExecuteMenuItem("Edit/Play"); | |
} | |
#endregion | |
#region Utility | |
static bool TransformApproximately(Transform transform, Vector3 pos, Quaternion rot) | |
{ | |
if (!Mathf.Approximately(transform.position.x,pos.x)){return false;} | |
if (!Mathf.Approximately(transform.position.y,pos.y)){return false;} | |
if (!Mathf.Approximately(transform.position.z,pos.z)){return false;} | |
if (!Mathf.Approximately(transform.rotation.x,rot.x)){return false;} | |
if (!Mathf.Approximately(transform.rotation.y,rot.y)){return false;} | |
if (!Mathf.Approximately(transform.rotation.z,rot.z)){return false;} | |
if (!Mathf.Approximately(transform.rotation.w,rot.w)){return false;} | |
return true; | |
} | |
static bool Vector2Approximately(Vector2 a, Vector2 b) | |
{ | |
if (!Mathf.Approximately(a.x,b.x)){return false;} | |
if (!Mathf.Approximately(a.y,b.y)){return false;} | |
return true; | |
} | |
static void DrawHandleCircle(Vector3 position, Vector3 normal, float radius, Color color) | |
{ | |
Vector3 left = Vector3.Cross(normal, Vector3.up).normalized; | |
var r = left * radius; | |
var q = Quaternion.AngleAxis(360f / 32, normal); | |
Handles.color = color; | |
for (int i = 0; i < 32; i++) { | |
var tempR = q * r; | |
Handles.DrawLine(position+r,position+tempR); | |
r = tempR; | |
} | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment