#if !CINEMACHINE_NO_CM2_SUPPORT
using UnityEngine;
using UnityEditor;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
#if CINEMACHINE_UNITY_INPUTSYSTEM
using UnityEngine.InputSystem;
#endif
namespace Unity.Cinemachine.Editor
{
///
/// Base class for virtual camera editors.
/// Handles drawing the header and the basic properties.
///
/// The type of CinemachineVirtualCameraBase being edited
[Obsolete]
class CinemachineVirtualCameraBaseEditor : BaseEditor where T : CinemachineVirtualCameraBase
{
static GUIContent s_AddExtensionLabel = new ("Add Extension", "Add a Cinemachine Extension behaviour");
static Type[] sExtensionTypes; // First entry is null
static string[] sExtensionNames;
bool IsPrefabBase { get; set; }
/// Update state information on undo/redo
void UpdateCameraState()
{
if (Target != null)
Target.InternalUpdateCameraState(Vector3.up, -1);
}
/// Inspector panel is being enabled.
/// Implementation should call the base class implementation
protected virtual void OnEnable()
{
Undo.undoRedoPerformed += UpdateCameraState;
IsPrefabBase = Target.gameObject.scene.name == null; // causes a small GC alloc
if (sExtensionTypes == null)
{
// Populate the extension list
List exts = new List();
List names = new List();
exts.Add(null);
names.Add("(select)");
var allExtensions
= ReflectionHelpers.GetTypesInAllDependentAssemblies(
(Type t) => typeof(CinemachineExtension).IsAssignableFrom(t)
&& !t.IsAbstract && t.GetCustomAttribute() == null);
foreach (Type t in allExtensions)
{
exts.Add(t);
names.Add(t.Name);
}
sExtensionTypes = exts.ToArray();
sExtensionNames = names.ToArray();
}
}
/// Inspector panel is being disabled.
/// Implementation should call the base class implementation
protected virtual void OnDisable()
{
Undo.undoRedoPerformed -= UpdateCameraState;
if (CinemachineCore.SoloCamera == Target)
{
CinemachineCore.SoloCamera = null;
InspectorUtility.RepaintGameView();
}
}
protected virtual void DrawStandardInspectorTopSection()
{
DrawCameraStatusInInspector();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(serializedObject.FindProperty(() => Target.StandbyUpdate));
EditorGUILayout.PropertyField(serializedObject.FindProperty(() => Target.Priority));
EditorGUILayout.PropertyField(serializedObject.FindProperty(() => Target.OutputChannel));
if (EditorGUI.EndChangeCheck())
serializedObject.ApplyModifiedProperties();
DrawGlobalControlsInInspector();
}
#if CINEMACHINE_UNITY_INPUTSYSTEM
///
/// Draw a message prompting the user to add a CinemachineInputProvider.
/// Does nothing if Input package not installed.
///
protected void DrawInputProviderButtonInInspector()
{
bool needsButton = false;
for (int i = 0; !needsButton && i < targets.Length; ++i)
{
var vcam = (CinemachineVirtualCameraBase)targets[i];
var requirer = vcam as AxisState.IRequiresInput;
if (requirer != null && requirer.RequiresInput() && !vcam.TryGetComponent(out _))
needsButton = true;
}
if (!needsButton)
return;
EditorGUILayout.Space();
InspectorUtility.HelpBoxWithButton(
"The InputSystem package is installed, but it is not used to control this vcam.",
MessageType.Info,
new GUIContent("Add Input\nProvider"), () =>
{
Undo.SetCurrentGroupName("Add CinemachineInputProvider");
for (int i = 0; i < targets.Length; ++i)
{
var vcam = (CinemachineVirtualCameraBase)targets[i];
if (vcam.TryGetComponent(out var _))
continue;
var inputProvider = Undo.AddComponent(vcam.gameObject);
inputProvider.XYAxis = (InputActionReference)AssetDatabase.LoadAllAssetsAtPath(
"Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/DefaultInputActions.inputactions").FirstOrDefault(
x => x.name == "Player/Look");
}
});
EditorGUILayout.Space();
}
#else
///
/// Draw a message prompting the user to add a CinemachineInputProvider.
/// Does nothing if Input package not installed.
///
protected void DrawInputProviderButtonInInspector() {}
#endif
///
/// Draw the LookAt and Follow targets in the inspector
///
/// Follow target SerializedProperty
/// LookAt target SerializedProperty
protected void DrawTargetsInInspector(
SerializedProperty followTarget, SerializedProperty lookAtTarget)
{
EditorGUI.BeginChangeCheck();
var parentVcam = Target.ParentCamera as CinemachineVirtualCameraBase;
if (!IsPropertyExcluded(followTarget.name))
{
if (parentVcam == null || parentVcam.Follow == null)
EditorGUILayout.PropertyField(followTarget);
else
EditorGUILayout.PropertyField(followTarget,
new GUIContent(followTarget.displayName + " Override"));
ExcludeProperty(followTarget.name);
}
if (!IsPropertyExcluded(lookAtTarget.name))
{
if (parentVcam == null || parentVcam.LookAt == null)
EditorGUILayout.PropertyField(lookAtTarget);
else
EditorGUILayout.PropertyField(lookAtTarget,
new GUIContent(lookAtTarget.displayName + " Override"));
ExcludeProperty(lookAtTarget.name);
}
if (EditorGUI.EndChangeCheck())
serializedObject.ApplyModifiedProperties();
}
///
/// Draw the Extensions dropdown in the inspector
///
protected void DrawExtensionsWidgetInInspector()
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("Extensions", EditorStyles.boldLabel);
Rect rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight);
rect = EditorGUI.PrefixLabel(rect, s_AddExtensionLabel);
int selection = EditorGUI.Popup(rect, 0, sExtensionNames);
if (selection > 0)
{
Type extType = sExtensionTypes[selection];
for (int i = 0; i < targets.Length; i++)
{
var targetGO = (targets[i] as CinemachineVirtualCameraBase).gameObject;
if (targetGO != null && targetGO.GetComponent(extType) == null)
Undo.AddComponent(targetGO, extType);
}
}
}
///
/// Draw the Live status in the inspector, and the Solo button
///
protected void DrawCameraStatusInInspector()
{
if (Selection.objects.Length > 1)
return;
// Is the camera navel-gazing?
CameraState state = Target.State;
if (state.HasLookAt() && (state.ReferenceLookAt - state.GetCorrectedPosition()).AlmostZero())
EditorGUILayout.HelpBox(
"The camera is positioned on the same point at which it is trying to look.",
MessageType.Warning);
// No status and Solo for prefabs
if (IsPrefabBase)
return;
// Active status and Solo button
Rect rect = EditorGUILayout.GetControlRect(true);
Rect rectLabel = new Rect(rect.x, rect.y, EditorGUIUtility.labelWidth, rect.height);
rect.width -= rectLabel.width;
rect.x += rectLabel.width;
Color color = GUI.color;
bool isSolo = (CinemachineCore.SoloCamera == Target);
if (isSolo)
GUI.color = CinemachineCore.SoloGUIColor();
bool isLive = CinemachineCore.IsLive(Target);
GUI.enabled = isLive;
GUI.Label(rectLabel, isLive ? "Status: Live"
: (Target.isActiveAndEnabled ? "Status: Standby" : "Status: Disabled"));
GUI.enabled = true;
float labelWidth = 0;
GUIContent updateText = GUIContent.none;
UpdateTracker.UpdateClock updateMode = CameraUpdateManager.GetVcamUpdateStatus(Target);
if (Application.isPlaying)
{
updateText = new GUIContent(
updateMode == UpdateTracker.UpdateClock.Fixed ? " Fixed Update" : " Late Update");
var textDimensions = GUI.skin.label.CalcSize(updateText);
labelWidth = textDimensions.x;
}
rect.width -= labelWidth;
if (GUI.Button(rect, "Solo", "Button"))
{
isSolo = !isSolo;
CinemachineCore.SoloCamera = isSolo ? Target : null;
InspectorUtility.RepaintGameView();
}
GUI.color = color;
if (isSolo && !Application.isPlaying)
InspectorUtility.RepaintGameView();
if (labelWidth > 0)
{
GUI.enabled = false;
rect.x += rect.width; rect.width = labelWidth;
GUI.Label(rect, updateText);
GUI.enabled = true;
}
}
GUIContent m_GuidesLabel;
static GUIContent[] s_GuidesChoices = new [] { new GUIContent("Disabled"), new GUIContent("Passive"), new GUIContent("Interactive") };
///
/// Draw the global settings controls in the inspector
///
protected void DrawGlobalControlsInInspector()
{
if (m_GuidesLabel == null)
m_GuidesLabel = new ("Game View Guides", CinemachineCorePrefs.s_ShowInGameGuidesLabel.tooltip);
SaveDuringPlay.Enabled = EditorGUILayout.Toggle(
CinemachineCorePrefs.s_SaveDuringPlayLabel, SaveDuringPlay.Enabled);
int index = CinemachineCorePrefs.ShowInGameGuides.Value
? (CinemachineCorePrefs.DraggableComposerGuides.Value ? 2 : 1) : 0;
var newIndex = EditorGUILayout.Popup(m_GuidesLabel, index, s_GuidesChoices);
if (index != newIndex)
{
CinemachineCorePrefs.ShowInGameGuides.Value = newIndex != 0;
CinemachineCorePrefs.DraggableComposerGuides.Value = newIndex == 2;
InspectorUtility.RepaintGameView();
}
if (Application.isPlaying && SaveDuringPlay.Enabled)
EditorGUILayout.HelpBox(
"CinemachineCamera settings changes made during Play Mode will be "
+ "propagated back to the scene when Play Mode is exited.",
MessageType.Info);
}
internal bool IsHorizontalFOVUsed()
{
// This should be a global UX setting, but the best we can do now is to query
// the associated camera (if any) for the lens FOV display mode
Camera camera = null;
var brain = CinemachineCore.FindPotentialTargetBrain(Target);
if (brain != null)
camera = brain.OutputCamera;
if (camera != null)
{
var p = new SerializedObject(camera).FindProperty("m_FOVAxisMode");
if (p != null && p.intValue == (int)Camera.FieldOfViewAxis.Horizontal)
return true;
}
return false;
}
}
}
#endif