using UnityEngine; using UnityEditor; namespace Cinemachine.Editor { internal static class CinemachineMenu { const string m_CinemachineAssetsRootMenu = "Assets/Create/Cinemachine/"; const string m_CinemachineGameObjectRootMenu = "GameObject/Cinemachine/"; const int m_GameObjectMenuPriority = 11; // Right after Camera. // Assets Menu [MenuItem(m_CinemachineAssetsRootMenu + "BlenderSettings")] static void CreateBlenderSettingAsset() { ScriptableObjectUtility.Create(); } [MenuItem(m_CinemachineAssetsRootMenu + "NoiseSettings")] static void CreateNoiseSettingAsset() { ScriptableObjectUtility.Create(); } [MenuItem(m_CinemachineAssetsRootMenu + "Fixed Signal Definition")] static void CreateFixedSignalDefinition() { ScriptableObjectUtility.Create(); } // GameObject Menu [MenuItem(m_CinemachineGameObjectRootMenu + "Virtual Camera", false, m_GameObjectMenuPriority)] static void CreateVirtualCamera(MenuCommand command) { CinemachineEditorAnalytics.SendCreateEvent("Virtual Camera"); CreateDefaultVirtualCamera(parentObject: command.context as GameObject, select: true); } [MenuItem(m_CinemachineGameObjectRootMenu + "FreeLook Camera", false, m_GameObjectMenuPriority)] static void CreateFreeLookCamera(MenuCommand command) { CinemachineEditorAnalytics.SendCreateEvent("FreeLook Camera"); CreateCinemachineObject("FreeLook Camera", command.context as GameObject, true); } [MenuItem(m_CinemachineGameObjectRootMenu + "Blend List Camera", false, m_GameObjectMenuPriority)] static void CreateBlendListCamera(MenuCommand command) { CinemachineEditorAnalytics.SendCreateEvent("Blend List Camera"); var blendListCamera = CreateCinemachineObject( "Blend List Camera", command.context as GameObject, true); // We give the camera a couple of children as an example of setup var childVcam1 = CreateDefaultVirtualCamera(parentObject: blendListCamera.gameObject); var childVcam2 = CreateDefaultVirtualCamera(parentObject: blendListCamera.gameObject); childVcam2.m_Lens.FieldOfView = 10; // Set up initial instruction set blendListCamera.m_Instructions = new CinemachineBlendListCamera.Instruction[2]; blendListCamera.m_Instructions[0].m_VirtualCamera = childVcam1; blendListCamera.m_Instructions[0].m_Hold = 1f; blendListCamera.m_Instructions[1].m_VirtualCamera = childVcam2; blendListCamera.m_Instructions[1].m_Blend.m_Style = CinemachineBlendDefinition.Style.EaseInOut; blendListCamera.m_Instructions[1].m_Blend.m_Time = 2f; } #if CINEMACHINE_UNITY_ANIMATION [MenuItem(m_CinemachineGameObjectRootMenu + "State-Driven Camera", false, m_GameObjectMenuPriority)] static void CreateStateDivenCamera(MenuCommand command) { CinemachineEditorAnalytics.SendCreateEvent("State-Driven Camera"); var stateDrivenCamera = CreateCinemachineObject( "State-Driven Camera", command.context as GameObject, true); // We give the camera a child as an example setup CreateDefaultVirtualCamera(parentObject: stateDrivenCamera.gameObject); } #endif #if CINEMACHINE_PHYSICS [MenuItem(m_CinemachineGameObjectRootMenu + "ClearShot Camera", false, m_GameObjectMenuPriority)] static void CreateClearShotVirtualCamera(MenuCommand command) { CinemachineEditorAnalytics.SendCreateEvent("ClearShot Camera"); var clearShotCamera = CreateCinemachineObject( "ClearShot Camera", command.context as GameObject, true); // We give the camera a child as an example setup var childVcam = CreateDefaultVirtualCamera(parentObject: clearShotCamera.gameObject); Undo.AddComponent(childVcam.gameObject).m_AvoidObstacles = false; } #endif [MenuItem(m_CinemachineGameObjectRootMenu + "Dolly Camera with Track", false, m_GameObjectMenuPriority)] static void CreateDollyCameraWithPath(MenuCommand command) { CinemachineEditorAnalytics.SendCreateEvent("Dolly Camera with Track"); var path = CreateCinemachineObject( "Dolly Track", command.context as GameObject, false); var vcam = CreateCinemachineObject( "Virtual Camera", command.context as GameObject, true); vcam.m_Lens = MatchSceneViewCamera(vcam.transform); AddCinemachineComponent(vcam); AddCinemachineComponent(vcam).m_Path = path; } [MenuItem(m_CinemachineGameObjectRootMenu + "Dolly Track with Cart", false, m_GameObjectMenuPriority)] static void CreateDollyTrackWithCart(MenuCommand command) { CinemachineEditorAnalytics.SendCreateEvent("Dolly Track with Cart"); var path = CreateCinemachineObject( "Dolly Track", command.context as GameObject, false); CreateCinemachineObject( "Dolly Cart", command.context as GameObject, true).m_Path = path; } [MenuItem(m_CinemachineGameObjectRootMenu + "Target Group Camera", false, m_GameObjectMenuPriority)] static void CreateTargetGroupCamera(MenuCommand command) { CinemachineEditorAnalytics.SendCreateEvent("Target Group Camera"); var vcam = CreateCinemachineObject( "Virtual Camera", command.context as GameObject, false); vcam.m_Lens = MatchSceneViewCamera(vcam.transform); AddCinemachineComponent(vcam); AddCinemachineComponent(vcam); var targetGroup = CreateCinemachineObject( "Target Group", command.context as GameObject, true); vcam.LookAt = targetGroup.transform; vcam.Follow = targetGroup.transform; } [MenuItem(m_CinemachineGameObjectRootMenu + "Mixing Camera", false, m_GameObjectMenuPriority)] static void CreateMixingCamera(MenuCommand command) { CinemachineEditorAnalytics.SendCreateEvent("Mixing Camera"); var mixingCamera = CreateCinemachineObject( "Mixing Camera", command.context as GameObject, true); // We give the camera a couple of children as an example of setup CreateDefaultVirtualCamera(parentObject: mixingCamera.gameObject); CreateDefaultVirtualCamera(parentObject: mixingCamera.gameObject); } [MenuItem(m_CinemachineGameObjectRootMenu + "2D Camera", false, m_GameObjectMenuPriority)] static void Create2DCamera(MenuCommand command) { CinemachineEditorAnalytics.SendCreateEvent("2D Camera"); var vcam = CreateCinemachineObject( "Virtual Camera", command.context as GameObject, true); vcam.m_Lens = MatchSceneViewCamera(vcam.transform); AddCinemachineComponent(vcam); } /// /// Sets the specified to match the position and /// rotation of the camera, and returns the scene view /// camera's lens settings. /// /// The to match with the /// camera. /// A representing the scene view camera's lens public static LensSettings MatchSceneViewCamera(Transform sceneObject) { var lens = LensSettings.Default; // Take initial settings from the GameView camera, because we don't want to override // things like ortho vs perspective - we just want position and FOV var brain = GetOrCreateBrain(); if (brain != null && brain.OutputCamera != null) lens = LensSettings.FromCamera(brain.OutputCamera); if (SceneView.lastActiveSceneView != null) { var src = SceneView.lastActiveSceneView.camera; sceneObject.SetPositionAndRotation(src.transform.position, src.transform.rotation); if (lens.Orthographic == src.orthographic) { if (src.orthographic) lens.OrthographicSize = src.orthographicSize; else lens.FieldOfView = src.fieldOfView; } } return lens; } /// /// Creates a with standard procedural components. /// public static CinemachineVirtualCamera CreateDefaultVirtualCamera( string name = "Virtual Camera", GameObject parentObject = null, bool select = false) { var vcam = CreateCinemachineObject(name, parentObject, select); vcam.m_Lens = MatchSceneViewCamera(vcam.transform); AddCinemachineComponent(vcam); AddCinemachineComponent(vcam); return vcam; } /// /// Creates a with no procedural components. /// public static CinemachineVirtualCamera CreatePassiveVirtualCamera( string name = "Virtual Camera", GameObject parentObject = null, bool select = false) { var vcam = CreateCinemachineObject(name, parentObject, select); vcam.m_Lens = MatchSceneViewCamera(vcam.transform); return vcam; } /// /// Creates a Cinemachine in the scene with a specified component. /// /// The type of to add to the new . /// The name of the new . /// The to parent the new to. /// Whether the new should be selected. /// The instance of the component that is added to the new . static T CreateCinemachineObject(string name, GameObject parentObject, bool select) where T : Component { // We always enforce the existence of the CM brain GetOrCreateBrain(); // We use ObjectFactory to create a new GameObject as it automatically supports undo/redo var go = ObjectFactory.CreateGameObject(name); T component = go.AddComponent(); if (parentObject != null) Undo.SetTransformParent(go.transform, parentObject.transform, "Set parent of " + name); // We ensure that the new object has a unique name, for example "Camera (1)". // This must be done after setting the parent in order to get an accurate unique name GameObjectUtility.EnsureUniqueNameForSibling(go); // We set the new object to be at the current pivot of the scene. // GML TODO: Support the "Place Objects At World Origin" preference option in 2020.3+, see GOCreationCommands.cs if (SceneView.lastActiveSceneView != null) go.transform.position = SceneView.lastActiveSceneView.pivot; if (select) Selection.activeGameObject = go; return component; } /// /// Gets the first loaded . Creates one on /// the if none were found. /// static CinemachineBrain GetOrCreateBrain() { if (CinemachineCore.Instance.BrainCount > 0) return CinemachineCore.Instance.GetActiveBrain(0); // Create a CinemachineBrain on the main camera var cam = Camera.main; if (cam == null) #if UNITY_2023_1_OR_NEWER cam = Object.FindFirstObjectByType(FindObjectsInactive.Exclude); #else cam = Object.FindObjectOfType(); #endif if (cam != null) return Undo.AddComponent(cam.gameObject); // No camera, just create a brain on an empty object return ObjectFactory.CreateGameObject("CinemachineBrain").AddComponent(); } /// /// Adds an component to the specified 's hidden /// component owner, that supports undo. /// /// The type of to add to the cinemachine pipeline. /// The to add components to. /// The instance of the componented that was added. static T AddCinemachineComponent(CinemachineVirtualCamera vcam) where T : CinemachineComponentBase { // We can't use the CinemachineVirtualCamera.AddCinemachineComponent() // because we want to support undo/redo var componentOwner = vcam.GetComponentOwner().gameObject; if (componentOwner == null) return null; // maybe it's an invalid prefab instance var component = Undo.AddComponent(componentOwner); vcam.InvalidateComponentPipeline(); return component; } } }