// =============================================================================== // CameraBookmarksTool - Configurable Scene View Camera Save & Load // // Creator: Scove // Last Updated: 2024-05-08 // Version: 3.0 // // Purpose: // Allows saving and loading specific Scene View camera angles and positions. // Includes an Editor Window interface to completely customize the keyboard shortcuts. // // How to Use: // 1. Place this script in an 'Editor' folder in your project. // 2. Open Settings: Menu -> Tools -> Camera Bookmarks Settings. // 3. Customize your Save/Load modifiers (e.g., Control, Shift, Alt). // 4. Customize the shortcut keys for Slot 1 to 9. // 5. Focus the Scene View and use your assigned shortcuts to Save/Load camera angles. // =============================================================================== using UnityEditor; using UnityEngine; using System.Globalization; using System; namespace Editor { [InitializeOnLoad] public class CameraBookmarksTool : EditorWindow { // Customizable Shortcut Keys private static EventModifiers saveModifier; private static EventModifiers loadModifier; private static KeyCode[] slotKeys = new KeyCode[9]; // Static constructor runs automatically when Unity loads or recompiles static CameraBookmarksTool() { LoadSettings(); SceneView.duringSceneGui += OnSceneGUI; }[MenuItem("Tools/Camera Bookmarks Settings")] public static void ShowWindow() { GetWindow("Cam Bookmarks"); } private static void LoadSettings() { // Load settings from EditorPrefs, fallback to Control/Shift if not found saveModifier = (EventModifiers)EditorPrefs.GetInt("CamBM_SaveMod", (int)EventModifiers.Control); loadModifier = (EventModifiers)EditorPrefs.GetInt("CamBM_LoadMod", (int)EventModifiers.Shift); // Load key bindings for 9 slots, fallback to Alpha1-Alpha9 for (int i = 0; i < 9; i++) { int defaultKey = (int)(KeyCode.Alpha1 + i); slotKeys[i] = (KeyCode)EditorPrefs.GetInt($"CamBM_Key_{i}", defaultKey); } } private static void SaveSettings() { EditorPrefs.SetInt("CamBM_SaveMod", (int)saveModifier); EditorPrefs.SetInt("CamBM_LoadMod", (int)loadModifier); for (int i = 0; i < 9; i++) { EditorPrefs.SetInt($"CamBM_Key_{i}", (int)slotKeys[i]); } } private void OnGUI() { GUILayout.Label("Shortcut Configuration", EditorStyles.boldLabel); EditorGUILayout.Space(); EditorGUI.BeginChangeCheck(); // Modifier Keys Setup saveModifier = (EventModifiers)EditorGUILayout.EnumFlagsField("Save Modifier", saveModifier); loadModifier = (EventModifiers)EditorGUILayout.EnumFlagsField("Load Modifier", loadModifier); if (saveModifier == loadModifier) { EditorGUILayout.HelpBox("Warning: Save and Load modifiers are the SAME! This will cause conflicts.", MessageType.Warning); } EditorGUILayout.Space(); GUILayout.Label("Slot Keys Assignment", EditorStyles.boldLabel); // KeyCode Setup for 9 slots for (int i = 0; i < 9; i++) { EditorGUILayout.BeginHorizontal(); GUILayout.Label($"Slot {i + 1}", GUILayout.Width(100)); slotKeys[i] = (KeyCode)EditorGUILayout.EnumPopup(slotKeys[i]); EditorGUILayout.EndHorizontal(); } if (EditorGUI.EndChangeCheck()) { SaveSettings(); // Save immediately if anything changes } EditorGUILayout.Space(); EditorGUILayout.Space(); // Reset Button if (GUILayout.Button("Reset to Default Settings", GUILayout.Height(30))) { saveModifier = EventModifiers.Control; loadModifier = EventModifiers.Shift; for (int i = 0; i < 9; i++) slotKeys[i] = KeyCode.Alpha1 + i; SaveSettings(); GUI.FocusControl(null); // Remove focus to refresh UI correctly } } static void OnSceneGUI(SceneView view) { Event e = Event.current; if (e.type == EventType.KeyDown) { // Mask out CapsLock, NumLock, and Function keys. We only care about main modifiers. EventModifiers currentMods = e.modifiers & (EventModifiers.Shift | EventModifiers.Control | EventModifiers.Alt | EventModifiers.Command); // Check if the pressed key matches any of our custom assigned slot keys int pressedSlotIndex = -1; for (int i = 0; i < 9; i++) { if (e.keyCode == slotKeys[i] && e.keyCode != KeyCode.None) { pressedSlotIndex = i + 1; break; } } if (pressedSlotIndex != -1) { string prefsKey = $"CamBookmark_Slot_{pressedSlotIndex}"; // SAVE ACTION if (currentMods == saveModifier) { SaveBookmark(view, prefsKey, pressedSlotIndex); e.Use(); // Consume the event } // LOAD ACTION else if (currentMods == loadModifier) { LoadBookmark(view, prefsKey, pressedSlotIndex); e.Use(); // Consume the event } } } } private static void SaveBookmark(SceneView view, string key, int slotIndex) { Transform camTransform = view.camera.transform; float size = view.size; // Use InvariantCulture to ensure dot (.) is used for decimals, preventing regional bugs string data = string.Format(CultureInfo.InvariantCulture, "{0}|{1}|{2}|{3}|{4}|{5}|{6}|{7}", camTransform.position.x, camTransform.position.y, camTransform.position.z, camTransform.rotation.x, camTransform.rotation.y, camTransform.rotation.z, camTransform.rotation.w, size); EditorPrefs.SetString(key, data); // Show feedback in Scene View string message = $"Saved Camera Bookmark to Slot {slotIndex}"; view.ShowNotification(new GUIContent(message)); Debug.Log($"[CameraBookmarks] {message}"); } private static void LoadBookmark(SceneView view, string key, int slotIndex) { if (!EditorPrefs.HasKey(key)) { string emptyMsg = $"Slot {slotIndex} is empty!"; view.ShowNotification(new GUIContent(emptyMsg)); Debug.LogWarning($"[CameraBookmarks] {emptyMsg}"); return; } string[] parts = EditorPrefs.GetString(key).Split('|'); if (parts.Length == 8) { try { // Parse data back to floats using InvariantCulture Vector3 pos = new Vector3( float.Parse(parts[0], CultureInfo.InvariantCulture), float.Parse(parts[1], CultureInfo.InvariantCulture), float.Parse(parts[2], CultureInfo.InvariantCulture) ); Quaternion rot = new Quaternion( float.Parse(parts[3], CultureInfo.InvariantCulture), float.Parse(parts[4], CultureInfo.InvariantCulture), float.Parse(parts[5], CultureInfo.InvariantCulture), float.Parse(parts[6], CultureInfo.InvariantCulture) ); float size = float.Parse(parts[7], CultureInfo.InvariantCulture); // Apply the saved transform to the Scene View camera view.LookAtDirect(pos, rot, size); // Show feedback in Scene View string message = $"Loaded Camera Bookmark from Slot {slotIndex}"; view.ShowNotification(new GUIContent(message)); Debug.Log($"[CameraBookmarks] {message}"); } catch (Exception ex) { Debug.LogError($"[CameraBookmarks] Failed to parse data for Slot {slotIndex}. Error: {ex.Message}"); } } } } }