298 lines
14 KiB
C#
298 lines
14 KiB
C#
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<CinemachineBlenderSettings>();
|
|
}
|
|
|
|
[MenuItem(m_CinemachineAssetsRootMenu + "NoiseSettings")]
|
|
static void CreateNoiseSettingAsset()
|
|
{
|
|
ScriptableObjectUtility.Create<NoiseSettings>();
|
|
}
|
|
|
|
[MenuItem(m_CinemachineAssetsRootMenu + "Fixed Signal Definition")]
|
|
static void CreateFixedSignalDefinition()
|
|
{
|
|
ScriptableObjectUtility.Create<CinemachineFixedSignal>();
|
|
}
|
|
|
|
// 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<CinemachineFreeLook>("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<CinemachineBlendListCamera>(
|
|
"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<CinemachineStateDrivenCamera>(
|
|
"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<CinemachineClearShot>(
|
|
"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<CinemachineCollider>(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<CinemachineSmoothPath>(
|
|
"Dolly Track", command.context as GameObject, false);
|
|
var vcam = CreateCinemachineObject<CinemachineVirtualCamera>(
|
|
"Virtual Camera", command.context as GameObject, true);
|
|
vcam.m_Lens = MatchSceneViewCamera(vcam.transform);
|
|
|
|
AddCinemachineComponent<CinemachineComposer>(vcam);
|
|
AddCinemachineComponent<CinemachineTrackedDolly>(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<CinemachineSmoothPath>(
|
|
"Dolly Track", command.context as GameObject, false);
|
|
CreateCinemachineObject<CinemachineDollyCart>(
|
|
"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<CinemachineVirtualCamera>(
|
|
"Virtual Camera", command.context as GameObject, false);
|
|
vcam.m_Lens = MatchSceneViewCamera(vcam.transform);
|
|
|
|
AddCinemachineComponent<CinemachineGroupComposer>(vcam);
|
|
AddCinemachineComponent<CinemachineTransposer>(vcam);
|
|
|
|
var targetGroup = CreateCinemachineObject<CinemachineTargetGroup>(
|
|
"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<CinemachineMixingCamera>(
|
|
"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<CinemachineVirtualCamera>(
|
|
"Virtual Camera", command.context as GameObject, true);
|
|
vcam.m_Lens = MatchSceneViewCamera(vcam.transform);
|
|
|
|
AddCinemachineComponent<CinemachineFramingTransposer>(vcam);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the specified <see cref="Transform"/> to match the position and
|
|
/// rotation of the <see cref="SceneView"/> camera, and returns the scene view
|
|
/// camera's lens settings.
|
|
/// </summary>
|
|
/// <param name="sceneObject">The <see cref="Transform"/> to match with the
|
|
/// <see cref="SceneView"/> camera.</param>
|
|
/// <returns>A <see cref="LensSettings"/> representing the scene view camera's lens</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a <see cref="CinemachineVirtualCamera"/> with standard procedural components.
|
|
/// </summary>
|
|
public static CinemachineVirtualCamera CreateDefaultVirtualCamera(
|
|
string name = "Virtual Camera", GameObject parentObject = null, bool select = false)
|
|
{
|
|
var vcam = CreateCinemachineObject<CinemachineVirtualCamera>(name, parentObject, select);
|
|
vcam.m_Lens = MatchSceneViewCamera(vcam.transform);
|
|
|
|
AddCinemachineComponent<CinemachineComposer>(vcam);
|
|
AddCinemachineComponent<CinemachineTransposer>(vcam);
|
|
|
|
return vcam;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a <see cref="CinemachineVirtualCamera"/> with no procedural components.
|
|
/// </summary>
|
|
public static CinemachineVirtualCamera CreatePassiveVirtualCamera(
|
|
string name = "Virtual Camera", GameObject parentObject = null, bool select = false)
|
|
{
|
|
var vcam = CreateCinemachineObject<CinemachineVirtualCamera>(name, parentObject, select);
|
|
vcam.m_Lens = MatchSceneViewCamera(vcam.transform);
|
|
return vcam;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a Cinemachine <see cref="GameObject"/> in the scene with a specified component.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of <see cref="Component"/> to add to the new <see cref="GameObject"/>.</typeparam>
|
|
/// <param name="name">The name of the new <see cref="GameObject"/>.</param>
|
|
/// <param name="parentObject">The <see cref="GameObject"/> to parent the new <see cref="GameObject"/> to.</param>
|
|
/// <param name="select">Whether the new <see cref="GameObject"/> should be selected.</param>
|
|
/// <returns>The instance of the component that is added to the new <see cref="GameObject"/>.</returns>
|
|
static T CreateCinemachineObject<T>(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<T>();
|
|
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the first loaded <see cref="CinemachineBrain"/>. Creates one on
|
|
/// the <see cref="Camera.main"/> if none were found.
|
|
/// </summary>
|
|
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<Camera>(FindObjectsInactive.Exclude);
|
|
#else
|
|
cam = Object.FindObjectOfType<Camera>();
|
|
#endif
|
|
if (cam != null)
|
|
return Undo.AddComponent<CinemachineBrain>(cam.gameObject);
|
|
|
|
// No camera, just create a brain on an empty object
|
|
return ObjectFactory.CreateGameObject("CinemachineBrain").AddComponent<CinemachineBrain>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an component to the specified <see cref="CinemachineVirtualCamera"/>'s hidden
|
|
/// component owner, that supports undo.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of <see cref="Component"/> to add to the cinemachine pipeline.</typeparam>
|
|
/// <param name="vcam">The <see cref="CinemachineVirtualCamera"/> to add components to.</param>
|
|
/// <returns>The instance of the componented that was added.</returns>
|
|
static T AddCinemachineComponent<T>(CinemachineVirtualCamera vcam) where T : CinemachineComponentBase
|
|
{
|
|
// We can't use the CinemachineVirtualCamera.AddCinemachineComponent<T>()
|
|
// 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<T>(componentOwner);
|
|
vcam.InvalidateComponentPipeline();
|
|
return component;
|
|
}
|
|
}
|
|
}
|