Created
December 1, 2024 21:19
Revisions
-
ashblue created this gist
Dec 1, 2024 .There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,170 @@ using UnityEditor; using UnityEngine; namespace CleverCrow.ColdIronCity.QuickCursor { [InitializeOnLoad] public class GlobalCursor { const float normalizationFactor = 1.2f; static Vector3 cursorPosition = Vector3.zero; static Quaternion cursorRotation = Quaternion.identity; static readonly Color lineColor = Color.black; static readonly Color circleColor = Color.red; static bool shortcutSetPosition = true; static bool shortcutSetRotation; public static bool IsCursorVisible { get; private set; } = true; public static float CursorSizeMultiplier => CursorSizeFactor * normalizationFactor; public static float CursorSizeFactor { get; private set; } = 1.0f; static GlobalCursor () { SceneView.duringSceneGui += OnScene; LoadCursorData(); } static void OnScene (SceneView sceneView) { if (!IsCursorVisible) return; UpdateCursorPositionShortcut(); DrawCursor(sceneView.camera); } static void UpdateCursorPositionShortcut () { if (Event.current.shift && Event.current.type == EventType.MouseDown && Event.current.button == 1) { var ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); if (Physics.Raycast(ray, out var hit)) { var pos = hit.point; if (!shortcutSetPosition) pos = cursorPosition; var rotation = Quaternion.FromToRotation(Vector3.up, hit.normal); if (!shortcutSetRotation) rotation = cursorRotation; SetPosition(pos, rotation); } } } static void DrawCursor (Camera sceneCamera) { var distanceToCursor = Vector3.Distance(sceneCamera.transform.position, cursorPosition); var lineLength = distanceToCursor * 0.05f * CursorSizeMultiplier; var gapSize = lineLength * 0.2f * CursorSizeMultiplier; var whiteCircleOffset = -0.001f * distanceToCursor; // Adjust this value to change the distance between the circles. Handles.color = lineColor; // Draw lines with a gap in the middle for the left/right axes Handles.DrawLine(cursorPosition + cursorRotation * (Vector3.left * gapSize), cursorPosition + cursorRotation * (Vector3.left * lineLength)); Handles.DrawLine(cursorPosition + cursorRotation * (Vector3.right * gapSize), cursorPosition + cursorRotation * (Vector3.right * lineLength)); // Highlight Y-axis in yellow Handles.color = Color.green; Handles.DrawLine(cursorPosition + cursorRotation * (Vector3.up * gapSize), cursorPosition + cursorRotation * (Vector3.up * lineLength)); Handles.color = lineColor; // Revert to original color Handles.DrawLine(cursorPosition + cursorRotation * (Vector3.down * gapSize), cursorPosition + cursorRotation * (Vector3.down * lineLength)); // Draw x axis Handles.DrawLine(cursorPosition + cursorRotation * (Vector3.forward * gapSize), cursorPosition + cursorRotation * (Vector3.forward * lineLength)); Handles.DrawLine(cursorPosition + cursorRotation * (Vector3.back * gapSize), cursorPosition + cursorRotation * (Vector3.back * lineLength)); // Draw red circle outline in the center Handles.color = circleColor; Handles.DrawWireDisc(cursorPosition, sceneCamera.transform.forward, gapSize); // Draw white circle outline next to the red one Handles.color = Color.white; Handles.DrawWireDisc(cursorPosition, sceneCamera.transform.forward, gapSize + whiteCircleOffset); } public static void SetPosition (Vector3 newPosition, Quaternion? newRotation = null) { cursorPosition = newPosition; if (newRotation.HasValue) cursorRotation = newRotation.Value; SceneView.RepaintAll(); SaveCursorData(); } public static Vector3 GetCursorPosition () { return cursorPosition; } public static Quaternion GetCursorRotation () { return cursorRotation; } public static bool GetUpdatePosition () { return shortcutSetPosition; } public static bool GetUpdateRotation () { return shortcutSetRotation; } public static void SetUpdatePosition (bool value) { shortcutSetPosition = value; SaveCursorData(); } public static void SetUpdateRotation (bool value) { shortcutSetRotation = value; SaveCursorData(); } public static void SetIsCursorVisible (bool value) { IsCursorVisible = value; SaveCursorData(); } public static void SetCursorSizeFactor (float value) { CursorSizeFactor = value; SaveCursorData(); } static void SaveCursorData () { EditorPrefs.SetFloat("GlobalCursorPosX", cursorPosition.x); EditorPrefs.SetFloat("GlobalCursorPosY", cursorPosition.y); EditorPrefs.SetFloat("GlobalCursorPosZ", cursorPosition.z); EditorPrefs.SetFloat("GlobalCursorRotX", cursorRotation.x); EditorPrefs.SetFloat("GlobalCursorRotY", cursorRotation.y); EditorPrefs.SetFloat("GlobalCursorRotZ", cursorRotation.z); EditorPrefs.SetFloat("GlobalCursorRotW", cursorRotation.w); EditorPrefs.SetBool("QuickCursorShortcutSetPosition", shortcutSetPosition); EditorPrefs.SetBool("QuickCursorShortcutSetRotation", shortcutSetRotation); EditorPrefs.SetBool("GlobalCursorIsVisible", IsCursorVisible); EditorPrefs.SetFloat("GlobalCursorSizeMultiplier", CursorSizeFactor); } static void LoadCursorData () { if (EditorPrefs.HasKey("GlobalCursorPosX")) { cursorPosition = new Vector3( EditorPrefs.GetFloat("GlobalCursorPosX"), EditorPrefs.GetFloat("GlobalCursorPosY"), EditorPrefs.GetFloat("GlobalCursorPosZ")); cursorRotation = new Quaternion( EditorPrefs.GetFloat("GlobalCursorRotX"), EditorPrefs.GetFloat("GlobalCursorRotY"), EditorPrefs.GetFloat("GlobalCursorRotZ"), EditorPrefs.GetFloat("GlobalCursorRotW")); shortcutSetPosition = EditorPrefs.GetBool("QuickCursorShortcutSetPosition"); shortcutSetRotation = EditorPrefs.GetBool("QuickCursorShortcutSetRotation"); IsCursorVisible = EditorPrefs.GetBool("GlobalCursorIsVisible"); CursorSizeFactor = EditorPrefs.GetFloat("GlobalCursorSizeMultiplier"); } } } } 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,101 @@ using UnityEditor; using UnityEngine; namespace CleverCrow.ColdIronCity.QuickCursor { public class QuickCursorActionsWindow : EditorWindow { void OnGUI () { if (GUILayout.Button("Cursor to Origin")) CursorToOrigin(); if (GUILayout.Button("Cursor to Selected")) CursorToSelected(); if (GUILayout.Button("Selection to Cursor")) SelectionToCursor(); if (GUILayout.Button("Selection to Cursor (offset)")) SelectionToCursorOffset(); } [MenuItem("Tools/Quick Cursor/Actions/Window")] public static void ShowWindow () { GetWindow<QuickCursorActionsWindow>("Quick Cursor Actions"); } [MenuItem("Tools/Quick Cursor/Actions/Cursor to Origin %#p")] public static void CursorToOrigin () { var position = Vector3.zero; if (!GlobalCursor.GetUpdatePosition()) position = GlobalCursor.GetCursorPosition(); var rotation = Quaternion.identity; if (!GlobalCursor.GetUpdateRotation()) rotation = GlobalCursor.GetCursorRotation(); GlobalCursor.SetPosition(position, rotation); } [MenuItem("Tools/Quick Cursor/Actions/Cursor to Selected")] public static void CursorToSelected () { var avgPos = Vector3.zero; foreach (var obj in Selection.transforms) avgPos += obj.position; avgPos /= Selection.transforms.Length; if (!GlobalCursor.GetUpdatePosition()) avgPos = GlobalCursor.GetCursorPosition(); var rotation = Quaternion.identity; if (Selection.transforms.Length > 0) rotation = Selection.transforms[0].rotation; if (!GlobalCursor.GetUpdateRotation()) rotation = GlobalCursor.GetCursorRotation(); GlobalCursor.SetPosition(avgPos, rotation); } [MenuItem("Tools/Quick Cursor/Actions/Selection to Cursor")] public static void SelectionToCursor () { var cursorPos = GlobalCursor.GetCursorPosition(); var cursorRot = GlobalCursor.GetCursorRotation(); if (GlobalCursor.GetUpdatePosition() || GlobalCursor.GetUpdateRotation()) Undo.RecordObjects(Selection.transforms, "Selection to Cursor"); foreach (var obj in Selection.transforms) { if (GlobalCursor.GetUpdateRotation()) obj.rotation = cursorRot; if (GlobalCursor.GetUpdatePosition()) obj.position = cursorPos; } } [MenuItem("Tools/Quick Cursor/Actions/Selection to Cursor (offset)")] public static void SelectionToCursorOffset () { var cursorPos = GlobalCursor.GetCursorPosition(); if (GlobalCursor.GetUpdatePosition() || GlobalCursor.GetUpdateRotation()) Undo.RecordObjects(Selection.transforms, "Selection to Cursor (offset)"); // Calculate the average position of selected objects var selectionCenter = Vector3.zero; foreach (var obj in Selection.transforms) selectionCenter += obj.position; selectionCenter /= Selection.transforms.Length; foreach (var obj in Selection.transforms) { var offset = obj.position - selectionCenter; obj.position = cursorPos + offset; if (GlobalCursor.GetUpdateRotation()) ApplyRelativeRotationToTargetAroundOrigin( obj.transform, GlobalCursor.GetCursorRotation(), cursorPos); } } public static void ApplyRelativeRotationToTargetAroundOrigin (Transform target, Quaternion rotation, Vector3 origin) { var relativeRotation = target.rotation * Quaternion.Inverse(target.rotation) * rotation; // Calculate the vector from the origin to the target var toTarget = target.position - origin; // Rotate this vector by our relative rotation var rotatedVector = relativeRotation * toTarget; // Update the target's position target.position = origin + rotatedVector; // Apply the relative rotation to the target's rotation target.rotation = relativeRotation * target.rotation; } } } 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,51 @@ using UnityEditor; using UnityEngine; namespace CleverCrow.ColdIronCity.QuickCursor { public class QuickCursorPositionWindow : EditorWindow { void OnGUI () { var cursorPosition = GlobalCursor.GetCursorPosition(); var rotationEulerAngles = GlobalCursor.GetCursorRotation().eulerAngles; EditorGUILayout.LabelField("Quick Cursor Position and Rotation", EditorStyles.boldLabel); cursorPosition = EditorGUILayout.Vector3Field("Position", cursorPosition); rotationEulerAngles = EditorGUILayout.Vector3Field("Rotation (Euler Angles)", rotationEulerAngles); // Apply precision correction cursorPosition = CorrectForFloatPointPrecision(cursorPosition); if (rotationEulerAngles != GlobalCursor.GetCursorRotation().eulerAngles) { var cursorRotation = Quaternion.Euler(rotationEulerAngles); GlobalCursor.SetPosition(cursorPosition, cursorRotation); } else { GlobalCursor.SetPosition(cursorPosition); } EditorGUILayout.Space(); EditorGUILayout.LabelField("Updates", EditorStyles.boldLabel); EditorGUILayout.HelpBox( "Hold Shift and right-click to set the cursor position and rotation to the point under the mouse cursor.", MessageType.Info); var pos = EditorGUILayout.Toggle("Set Position", GlobalCursor.GetUpdatePosition()); var rotation = EditorGUILayout.Toggle("Set Rotation", GlobalCursor.GetUpdateRotation()); if (pos != GlobalCursor.GetUpdatePosition()) GlobalCursor.SetUpdatePosition(pos); if (rotation != GlobalCursor.GetUpdateRotation()) GlobalCursor.SetUpdateRotation(rotation); } Vector3 CorrectForFloatPointPrecision (Vector3 vector) { return new Vector3( Mathf.Approximately(vector.x, 0) ? 0 : vector.x, Mathf.Approximately(vector.y, 0) ? 0 : vector.y, Mathf.Approximately(vector.z, 0) ? 0 : vector.z ); } [MenuItem("Tools/Quick Cursor/Position")] public static void ShowWindow () { GetWindow<QuickCursorPositionWindow>("Quick Cursor Position"); } } } 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,53 @@ using System; using UnityEditor; using UnityEngine; namespace CleverCrow.ColdIronCity.QuickCursor { public class QuickCursorWindow : EditorWindow { void OnGUI () { EditorGUILayout.LabelField("Quick Cursor Settings", EditorStyles.boldLabel); var visible = EditorGUILayout.Toggle("Cursor Visible", GlobalCursor.IsCursorVisible); if (GlobalCursor.IsCursorVisible != visible) { GlobalCursor.SetIsCursorVisible(visible); SceneView.RepaintAll(); } var size = EditorGUILayout.Slider("Cursor Size", GlobalCursor.CursorSizeFactor, 0.1f, 3.0f); if (Math.Abs(GlobalCursor.CursorSizeFactor - size) > 0.01f) { GlobalCursor.SetCursorSizeFactor(size); SceneView.RepaintAll(); } } /** * You will need to create an override profile to rebind the default GameObject creation shortcuts */ [MenuItem("Tools/Quick Cursor/Create Empty %#n")] public static void CreateGameObjectGlobally () { var newObject = new GameObject("GameObject"); newObject.transform.position = GlobalCursor.GetCursorPosition(); newObject.transform.rotation = GlobalCursor.GetCursorRotation(); // Select the newly created GameObject Selection.activeGameObject = newObject; } [MenuItem("Tools/Quick Cursor/Create Empty Child &#n")] public static void CreateGameObjectLocally () { var newObject = new GameObject("GameObject"); newObject.transform.position = GlobalCursor.GetCursorPosition(); newObject.transform.rotation = GlobalCursor.GetCursorRotation(); if (Selection.activeTransform != null) newObject.transform.SetParent(Selection.activeTransform); // Select the newly created GameObject Selection.activeGameObject = newObject; } [MenuItem("Tools/Quick Cursor/Settings")] public static void ShowWindow () { GetWindow<QuickCursorWindow>("Quick Cursor"); } } } 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,33 @@ # Quick Cursor A faithful adaptation of Blender's cursor system for Unity. ## Getting Started 1. Add the `QuickCursor` folder to your project. 2. Open the `QuickCursor` window from the `Tools/Quick Cursor/Settings` menu. ### Updating the cursor position You can snap the cursor to any "collider" in the scene. If you don't have a collider on your objects it wont work. 1. Hold shift and right click 2. The cursor will auto update the position 3. Open the position window from the `Tools/Quick Cursor/Position` for more details ### Creating new GameObjects at the cursor You can create new GameObjects at the cursor. But you have to override Unity's default inputs. 1. Use the default GameObject shortcut `Ctrl+Shift+N` 1. Click "Resolve Conflict..." 1. Add a new profile and activate it. Call it "Custom" if you don't already have one 1. Use the default GameObject shortcut `Ctrl+Shift+N` again 1. Choose the "Quick Cursor" shortcut 1. Select "Rebind ..." 1. Click "Perform Selected 1. Do the same for the `Alt+Shift+N` shortcut ### Action Menu You can use the action menu from `Tools/Quick Cursor/Actions` to quickly move objects around the scene.