Skip to content

Instantly share code, notes, and snippets.

@calderarchinuk
Last active December 8, 2023 09:50
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save calderarchinuk/16d5b5332b9f1fba2e9d6917d6c82cc5 to your computer and use it in GitHub Desktop.
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
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