├── README.md ├── SceneCameraTool.cs └── readmeRes └── record.gif /README.md: -------------------------------------------------------------------------------- 1 | A Unity editor tool to sync scene view camera and the game camera. 2 | 3 | I think you can get the idea pretty quick from the gif. 4 | 5 | ![](readmeRes/record.gif) 6 | 7 | 8 | 9 | ## How to 10 | 11 | 1. Copy the script into any of your Editor folder in the project. 12 | 2. Open menu item "Window/SceneView Cam Tool". 13 | 14 | ## Compatibility 15 | The tool has been tested in Unity 2019.1.7f1. ( If you want compatible for previous version please check out the older commit. ) 16 | -------------------------------------------------------------------------------- /SceneCameraTool.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using System; 4 | using System.Reflection; 5 | 6 | #if !UNITY_EDITOR 7 | #error This script must be placed under "Editor/" directory. 8 | #endif 9 | 10 | public class SceneCameraTool : EditorWindow 11 | { 12 | [SerializeField] 13 | Camera targetCam; 14 | [SerializeField] 15 | bool syncCam = false; 16 | [SerializeField] 17 | bool showSceneCamData = true; 18 | [SerializeField] 19 | CamProperties controledProperties = new CamProperties(); 20 | 21 | bool resetFovOnce = false; 22 | 23 | EditorWindow _gameView = null; 24 | EditorWindow gameView 25 | { 26 | get 27 | { 28 | if (_gameView != null) 29 | return _gameView; 30 | 31 | System.Reflection.Assembly assembly = typeof(UnityEditor.EditorWindow).Assembly; 32 | System.Type type = assembly.GetType("UnityEditor.GameView"); 33 | _gameView = EditorWindow.GetWindow(type); 34 | return gameView; 35 | } 36 | } 37 | 38 | Transform targetParent 39 | { 40 | get 41 | { 42 | if (targetCam != null) 43 | return targetCam.transform.parent; 44 | else 45 | return null; 46 | } 47 | } 48 | 49 | static SceneCameraTool instance = null; 50 | 51 | [MenuItem("Window/SceneView Cam Tool")] 52 | static void Init() 53 | { 54 | SceneCameraTool window = (SceneCameraTool)EditorWindow.GetWindow(typeof(SceneCameraTool), false, "Cam Tool"); 55 | window.Show(); 56 | instance = window; 57 | } 58 | 59 | void OnEnable() 60 | { 61 | instance = this; 62 | targetCam = Camera.main; 63 | 64 | UnityEditor.SceneView.beforeSceneGui-=OnPreSceneGUI; 65 | UnityEditor.SceneView.beforeSceneGui+=OnPreSceneGUI; 66 | } 67 | 68 | private void OnDisable() 69 | { 70 | UnityEditor.SceneView.beforeSceneGui-=OnPreSceneGUI; 71 | } 72 | 73 | public static void SRepaint() 74 | { 75 | if (instance != null) 76 | instance.Repaint(); 77 | } 78 | 79 | static void OnPreSceneGUI(SceneView sceneView) 80 | { 81 | if (SceneView.lastActiveSceneView != sceneView) 82 | return; 83 | 84 | instance.OnPreSceneGUI(); 85 | } 86 | 87 | void OnPreSceneGUI() 88 | { 89 | var sceneViewCam = SceneView.lastActiveSceneView.camera; 90 | 91 | if (resetFovOnce) 92 | resetFovOnce = false; 93 | else 94 | sceneViewCam.fieldOfView = controledProperties.fov; // Trick to control the scene cam fov. We need to override it every time or it will get reset by the Unity Editor 95 | 96 | if (sceneViewCam.transform.hasChanged) 97 | SRepaint(); 98 | 99 | 100 | // Update the position changes from scene view control 101 | controledProperties.Copy(sceneViewCam, targetParent); 102 | 103 | if (syncCam && targetCam != null) 104 | { 105 | controledProperties.Paste(targetCam); 106 | gameView.Repaint(); 107 | } 108 | } 109 | 110 | void OnGUI() 111 | { 112 | if (SceneView.lastActiveSceneView == null) 113 | return; 114 | 115 | // Camera data transfer controls 116 | EditorGUILayout.BeginHorizontal(); 117 | { 118 | GUI.enabled = false; 119 | 120 | EditorGUILayout.ObjectField(SceneView.lastActiveSceneView.camera, typeof(Camera), true, GUILayout.MaxWidth(150)); 121 | 122 | GUI.enabled = (targetCam != null) && !syncCam; 123 | if (GUILayout.Button("<-")) 124 | { 125 | controledProperties.Copy(targetCam); 126 | SetSceneCamTransformData(); 127 | } 128 | 129 | GUI.enabled = targetCam != null; 130 | syncCam = EditorGUILayout.Toggle(syncCam, "IN LockButton", GUILayout.MaxWidth(15)); 131 | 132 | GUI.enabled = (targetCam != null) && !syncCam; 133 | if (GUILayout.Button("->")) 134 | controledProperties.Paste(targetCam); 135 | 136 | GUI.enabled = true; 137 | EditorGUIUtility.labelWidth = 80; 138 | targetCam = (Camera)EditorGUILayout.ObjectField(targetCam, typeof(Camera), true, GUILayout.MaxWidth(150)); 139 | EditorGUIUtility.labelWidth = -1; 140 | } 141 | EditorGUILayout.EndHorizontal(); 142 | 143 | if (GUI.changed) 144 | SceneView.lastActiveSceneView.Repaint(); 145 | 146 | // Scene camera Inspector 147 | HorizontalLine.Draw(); 148 | EditorGUILayout.LabelField(EditorGUIUtility.ObjectContent(SceneView.lastActiveSceneView.camera, typeof(Camera))); 149 | 150 | EditorGUI.indentLevel = EditorGUI.indentLevel + 1; 151 | if (showSceneCamData) //TODO foldout arrow 152 | { 153 | GUI.changed = false; 154 | 155 | EditorGUIUtility.wideMode = true; 156 | 157 | DrawTransformData(); 158 | 159 | GUILayout.BeginHorizontal(); 160 | controledProperties.fov = EditorGUILayout.Slider(new GUIContent("Field Of View"), controledProperties.fov, 0.1f, 200f, GUILayout.ExpandWidth(true)); 161 | 162 | if (GUILayout.Button("Reset", GUILayout.MaxWidth(50f))) 163 | resetFovOnce = true; 164 | GUILayout.EndHorizontal(); 165 | 166 | if (GUI.changed) 167 | { 168 | SetSceneCamTransformData(); 169 | SceneView.lastActiveSceneView.Repaint(); 170 | } 171 | } 172 | } 173 | 174 | void DrawTransformData() 175 | { 176 | controledProperties.localPosition = EditorGUILayout.Vector3Field("Position", controledProperties.localPosition); 177 | Vector3 newLocalRotEuler = EditorGUILayout.Vector3Field("Rotation", controledProperties.localRotEuler); 178 | if (newLocalRotEuler != controledProperties.localRotEuler) 179 | { 180 | controledProperties.localRotEuler = newLocalRotEuler; 181 | controledProperties.localRotation = Quaternion.Euler(newLocalRotEuler); 182 | } 183 | } 184 | 185 | void SetSceneCamTransformData() 186 | { 187 | Vector3 globalPosition = controledProperties.localPosition; 188 | if (targetParent != null) 189 | globalPosition = targetParent.TransformPoint(controledProperties.localPosition); 190 | 191 | Quaternion globalRotation = controledProperties.localRotation; 192 | if (targetParent != null) 193 | globalRotation = targetParent.transform.rotation * globalRotation; 194 | 195 | SetSceneCamTransformData(globalPosition, globalRotation); 196 | } 197 | 198 | void SetSceneCamTransformData(Vector3 position, Quaternion rotation) 199 | { 200 | // Can't set transform of camera :( 201 | // It internally updates every frame: 202 | // cam.position = pivot + rotation * new Vector3(0, 0, -cameraDistance) 203 | // Info: https://forum.unity.com/threads/moving-scene-view-camera-from-editor-script.64920/#post-3388397 204 | 205 | var scene_view = UnityEditor.SceneView.lastActiveSceneView; 206 | 207 | scene_view.rotation = rotation; 208 | scene_view.pivot = position + rotation * new Vector3(0, 0, scene_view.cameraDistance); 209 | } 210 | 211 | [System.Serializable] 212 | private class CamProperties 213 | { 214 | public float fov = 55f; 215 | public Vector3 localPosition = Vector3.zero; 216 | public Quaternion localRotation = Quaternion.identity; 217 | public Vector3 localRotEuler = Vector3.zero; 218 | 219 | SerializedObject serializedTargetTransform; 220 | SerializedProperty serializedEulerHintProp; 221 | 222 | public void Copy(Camera sceneCamera, Transform relativeParent) 223 | { 224 | fov = sceneCamera.fieldOfView; 225 | 226 | Transform targetTransform = sceneCamera.transform; 227 | 228 | Quaternion newLocalRotation; 229 | if (relativeParent != null) 230 | { 231 | localPosition = relativeParent.InverseTransformPoint(targetTransform.position); 232 | newLocalRotation = Quaternion.Inverse(relativeParent.rotation) * targetTransform.rotation; 233 | } 234 | else 235 | { 236 | localPosition = targetTransform.position; 237 | newLocalRotation = targetTransform.rotation; 238 | } 239 | 240 | if (localRotation != newLocalRotation) 241 | { 242 | Vector3 newLocalEuler = newLocalRotation.eulerAngles; 243 | 244 | localRotEuler.x += Mathf.DeltaAngle(localRotEuler.x, newLocalEuler.x); 245 | localRotEuler.y += Mathf.DeltaAngle(localRotEuler.y, newLocalEuler.y); 246 | localRotEuler.z += Mathf.DeltaAngle(localRotEuler.z, newLocalEuler.z); 247 | 248 | localRotation = newLocalRotation; 249 | } 250 | } 251 | 252 | public void Copy(Camera target) 253 | { 254 | fov = target.fieldOfView; 255 | 256 | Transform targetTransform = target.transform; 257 | localPosition = targetTransform.localPosition; 258 | 259 | prepareProperty(targetTransform); 260 | serializedTargetTransform.UpdateIfRequiredOrScript(); 261 | localRotEuler = serializedEulerHintProp.vector3Value; 262 | 263 | localRotation = targetTransform.localRotation; 264 | } 265 | 266 | public void Paste(Camera target) 267 | { 268 | target.fieldOfView = fov; 269 | 270 | Transform targetTransform = target.transform; 271 | targetTransform.localPosition = localPosition; 272 | 273 | prepareProperty(targetTransform); 274 | serializedEulerHintProp.vector3Value = localRotEuler; 275 | serializedTargetTransform.ApplyModifiedProperties(); 276 | 277 | targetTransform.localEulerAngles = localRotEuler; 278 | } 279 | 280 | void prepareProperty(Transform targetTransform) 281 | { 282 | if (serializedTargetTransform != null && serializedTargetTransform.targetObject == targetTransform) 283 | return; 284 | 285 | serializedTargetTransform = new SerializedObject(targetTransform); 286 | serializedEulerHintProp = serializedTargetTransform.FindProperty("m_LocalEulerAnglesHint"); 287 | } 288 | } 289 | 290 | public static class HorizontalLine 291 | { 292 | private static GUIStyle m_line = null; 293 | 294 | //constructor 295 | static HorizontalLine() 296 | { 297 | 298 | m_line = new GUIStyle("box"); 299 | m_line.border.top = m_line.border.bottom = 1; 300 | m_line.margin.top = m_line.margin.bottom = 1; 301 | m_line.padding.top = m_line.padding.bottom = 1; 302 | m_line.border.left = m_line.border.right = 1; 303 | m_line.margin.left = m_line.margin.right = 1; 304 | m_line.padding.left = m_line.padding.right = 1; 305 | } 306 | 307 | public static void Draw() 308 | { 309 | GUILayout.Box(GUIContent.none, m_line, GUILayout.ExpandWidth(true), GUILayout.Height(1f)); 310 | } 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /readmeRes/record.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShirenY/Unity-Scene-Camera-Tool/92ca197e00b863adcac2b1ca5c6c95cf738d61df/readmeRes/record.gif --------------------------------------------------------------------------------