Skip to content

Commit

Permalink
CMCL-0000: Camera selection bugfixes and various tweaks (#1014)
Browse files Browse the repository at this point in the history
* add CinemachineVirtualCameraBaseEditor

* use [ChildCameraPropertyAttribute], layout tweaks

* layout tweaking for inspectors

* update doc

* update samples

* Update package.json

* Update SimplePlayerAnimator.cs

* Update SimplePlayerAnimator.cs

* Make sure object is selected when activating tool
  • Loading branch information
glabute authored Jul 30, 2024
1 parent ae3c2ad commit 09068c8
Show file tree
Hide file tree
Showing 27 changed files with 280 additions and 204 deletions.
6 changes: 6 additions & 0 deletions com.unity.cinemachine/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this package will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [3.1.2] - 2025-01-01

### Added
- Added CinemachineVirtualCameraBaseEditor, to make it easier to make conformant inspectors for custom virtual cameras and virtual camera managers.


## [3.1.1] - 2024-06-15

### Added
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Cinemachine Spline Dolly LookAt Targets

![Spline Dolly LookAt Targets Inspector](images/SplineDollyLookAtTargetsInspector.png)

This CinemachineCamera __Rotation Control__ behaviour lets you assign LookAt targets to points on a spline, so that as the camera arrives at the position on the spline, it looks at the specified place.

It's useful for creating curated dolly shots with specified aim targets along the way. This behaviour eliminates the need to provide rotation animations for the camera that are synchronized with the spline position animation. LookAt points are anchored to specific spline positions, and because they specify a LookAt target point, the appropriate rotation angles get computed dynamically. As a result, the rotation animation is more robust and less likely to break if the spline is modified.
Expand All @@ -18,7 +20,7 @@ When the LookAtDataOnSpline is selected in the inspector, a Scene View tool is p
| Property | Field | Description |
| --- | --- | --- |
| __Index Unit__ | | Defines how to interpret the _Index_ field for each data point. _Knot_ is the recommended value because it remains robust if the spline points change. |
| __Targets__ | | The list of LookAt target on the spline. As the camera approaches these positions on the spline, the camera will look at the corresponding targets. |
| __Data Points__ | | The list of LookAt target on the spline. As the camera approaches these positions on the spline, the camera will look at the corresponding targets. |
| | _Index_ | The position on the Spline where the camera should look at the supplied point. The value is interpreted according to the _Index Unit_ setting. |
| | _Look At_ | The target object to look at. It may be None, in which case the Offset will specify a point in world space. |
| | _Offset_ | The offset (in local coords) from the LookAt target's origin. If LookAt target is None, this will specify a world-space point. |
Expand Down
12 changes: 11 additions & 1 deletion com.unity.cinemachine/Documentation~/CinemachineSplineRoll.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
# Cinemachine Spline Roll

![Spline Roll Inspector](images/CinemachineSplineRollInspector.png)

This behavior adds Roll to a Spline. Roll is the rotation about the spline's tangent. Add data points to set the roll at specific points along the spline. Roll will be interpolated between those points. This behavior will also draw a railroad-track Gizmo in the Scene view, to help visualize the roll.

If you add this behavior to the Spline itself, then any [Cm Camera](CinemachineCamera.md) or [Cinemachine Spline Cart](CinemachineSplineCart.md) that follows the path will respect the roll. If instead you add this behavior to the CinemachineCamera itself, then the roll will be visible only to that CinemachineCamera.
If you add this behavior to the Spline itself, then any [Cinemachine Camera](CinemachineCamera.md) or [Cinemachine Spline Cart](CinemachineSplineCart.md) that follows the path will respect the roll. If instead you add this behavior to the CinemachineCamera itself, then the roll will be visible only to that CinemachineCamera.

### Properties

| Property | Field | Description |
| --- | --- | --- |
| __Index Unit__ | | Defines how to interpret the _Index_ field for each data point. _Knot_ is the recommended value because it remains robust if the spline points change. |
| __Data Points__ | | The list of Roll points on the spline. At these postions on the spline, it will take on the specified roll value. |
| | _Index_ | The position on the Spline where it should assume the specified roll. The value is interpreted according to the _Index Unit_ setting. |
| | _Roll_ | The roll value for the spline. This is specified in degrees, and the axis of rotation is the spline tangent at that point. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 3 additions & 11 deletions com.unity.cinemachine/Editor/Editors/CinemachineCameraEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Unity.Cinemachine.Editor
{
[CustomEditor(typeof(CinemachineCamera))]
[CanEditMultipleObjects]
class CinemachineCameraEditor : UnityEditor.Editor
class CinemachineCameraEditor : CinemachineVirtualCameraBaseEditor
{
CinemachineCamera Target => target as CinemachineCamera;

Expand All @@ -34,12 +34,9 @@ static void AdoptSceneViewCameraSettings(MenuCommand command)
void OnEnable() => Undo.undoRedoPerformed += ResetTarget;
void OnDisable() => Undo.undoRedoPerformed -= ResetTarget;

public override VisualElement CreateInspectorGUI()
protected override void AddInspectorProperties(VisualElement ux)
{
var ux = new VisualElement();

this.AddCameraStatus(ux);
this.AddTransitionsSection(ux, new () { serializedObject.FindProperty(() => Target.BlendHint) });
ux.Add(new PropertyField(serializedObject.FindProperty(() => Target.BlendHint)));
ux.Add(new PropertyField(serializedObject.FindProperty(() => Target.Lens)));

var defaultTargetLabel = new ObjectField("");
Expand All @@ -58,9 +55,6 @@ public override VisualElement CreateInspectorGUI()
ux.AddHeader("Procedural Components");
this.AddPipelineDropdowns(ux);

ux.AddSpace();
this.AddExtensionsDropdown(ux);

ux.TrackAnyUserActivity(() =>
{
if (Target == null)
Expand All @@ -74,8 +68,6 @@ public override VisualElement CreateInspectorGUI()
defaultTargetLabel.value = Target.Follow;
CmCameraInspectorUtility.SortComponents(target as CinemachineVirtualCameraBase);
});

return ux;
}

[EditorTool("Field of View Tool", typeof(CinemachineCamera))]
Expand Down
17 changes: 4 additions & 13 deletions com.unity.cinemachine/Editor/Editors/CinemachineClearShotEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Unity.Cinemachine.Editor
{
[CustomEditor(typeof(CinemachineClearShot))]
[CanEditMultipleObjects]
class CinemachineClearShotEditor : UnityEditor.Editor
class CinemachineClearShotEditor : CinemachineVirtualCameraBaseEditor
{
CinemachineClearShot Target => target as CinemachineClearShot;
EvaluatorState m_EvaluatorState;
Expand All @@ -22,17 +22,13 @@ static string GetAvailableQualityEvaluatorNames()
return "Available Shot Quality Evaluators are: " + names;
}

public override VisualElement CreateInspectorGUI()
protected override void AddInspectorProperties(VisualElement ux)
{
var ux = new VisualElement();

var helpBox = ux.AddChild(new HelpBox());
this.AddCameraStatus(ux);
this.AddTransitionsSection(ux);

ux.AddHeader("Global Settings");
this.AddGlobalControls(ux);

var helpBox = ux.AddChild(new HelpBox());

ux.AddHeader("Clear Shot");
ux.Add(new PropertyField(serializedObject.FindProperty(() => Target.DefaultTarget)));
ux.Add(new PropertyField(serializedObject.FindProperty(() => Target.ActivateAfter)));
Expand All @@ -41,10 +37,6 @@ public override VisualElement CreateInspectorGUI()
ux.Add(new PropertyField(serializedObject.FindProperty(() => Target.DefaultBlend)));
ux.Add(new PropertyField(serializedObject.FindProperty(() => Target.CustomBlends)));

ux.AddSpace();
this.AddChildCameras(ux, GetChildWarningMessage);
this.AddExtensionsDropdown(ux);

ux.TrackAnyUserActivity(() =>
{
if (Target == null)
Expand Down Expand Up @@ -80,7 +72,6 @@ public override VisualElement CreateInspectorGUI()
break;
}
});
return ux;
}

enum EvaluatorState
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,14 @@
namespace Unity.Cinemachine.Editor
{
[CustomEditor(typeof(CinemachineMixingCamera))]
class CinemachineMixingCameraEditor : UnityEditor.Editor
class CinemachineMixingCameraEditor : CinemachineVirtualCameraBaseEditor
{
CinemachineMixingCamera Target => target as CinemachineMixingCamera;

static string WeightPropertyName(int i) => "Weight" + i;

public override VisualElement CreateInspectorGUI()
protected override void AddInspectorProperties(VisualElement ux)
{
var ux = new VisualElement();

this.AddCameraStatus(ux);
this.AddTransitionsSection(ux);

ux.AddHeader("Global Settings");
this.AddGlobalControls(ux);

Expand Down Expand Up @@ -47,9 +42,6 @@ public override VisualElement CreateInspectorGUI()
DrawProportionIndicator(children, numCameras, totalWeight);
}));

ux.AddSpace();
this.AddExtensionsDropdown(ux);

ux.TrackAnyUserActivity(() =>
{
if (Target == null)
Expand All @@ -67,8 +59,6 @@ public override VisualElement CreateInspectorGUI()
for (int i = 0; i < weights.Count; ++i)
weights[i].SetVisible(i < numCameras);
});

return ux;
}

void DrawProportionIndicator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,12 @@ namespace Unity.Cinemachine.Editor
{
[CustomEditor(typeof(CinemachineSequencerCamera))]
[CanEditMultipleObjects]
class CinemachineSequencerCameraEditor : UnityEditor.Editor
class CinemachineSequencerCameraEditor : CinemachineVirtualCameraBaseEditor
{
CinemachineSequencerCamera Target => target as CinemachineSequencerCamera;

public override VisualElement CreateInspectorGUI()
protected override void AddInspectorProperties(VisualElement ux)
{
var ux = new VisualElement();

this.AddCameraStatus(ux);
this.AddTransitionsSection(ux);

ux.AddHeader("Global Settings");
this.AddGlobalControls(ux);

Expand All @@ -32,6 +27,7 @@ public override VisualElement CreateInspectorGUI()
HelpBoxMessageType.Info));

var container = ux.AddChild(new VisualElement());
container.AddHeader("Instructions");
var vcam = Target;
var header = container.AddChild(new VisualElement { style = { flexDirection = FlexDirection.Row, marginBottom = -2 } });
FormatInstructionElement(true,
Expand All @@ -53,21 +49,12 @@ public override VisualElement CreateInspectorGUI()
var instructions = serializedObject.FindProperty(() => Target.Instructions);
list.BindProperty(instructions);

// Available camera candidates
var availableCameras = new List<Object>();

list.makeItem = () =>
{
var row = new BindableElement { style = { flexDirection = FlexDirection.Row }};

var def = new CinemachineSequencerCamera.Instruction();
var vcamSel = row.AddChild(new PopupField<Object>
{
bindingPath = SerializedPropertyHelper.PropertyName(() => def.Camera),
choices = availableCameras,
formatListItemCallback = (obj) => obj == null ? "(null)" : obj.name,
formatSelectedValueCallback = (obj) => obj == null ? "(null)" : obj.name
});
var vcamSel = row.AddChild(new PropertyField(null, "") { bindingPath = SerializedPropertyHelper.PropertyName(() => def.Camera) });

var blend = row.AddChild(new PropertyField(null, "") { bindingPath = SerializedPropertyHelper.PropertyName(() => def.Blend), name = "blendSelector" });
var hold = row.AddChild(InspectorUtility.CreateDraggableField(() => def.Hold, row.AddChild(new Label(" ")), out _));
Expand All @@ -77,9 +64,6 @@ public override VisualElement CreateInspectorGUI()
return row;
};

container.AddSpace();
this.AddChildCameras(container, null);

container.TrackAnyUserActivity(() =>
{
if (Target == null || list.itemsSource == null)
Expand All @@ -93,14 +77,7 @@ public override VisualElement CreateInspectorGUI()
var index = 0;
list.Query<VisualElement>().Where((e) => e.name == "blendSelector").ForEach((e)
=> e.style.visibility = (index++ == 0 && !Target.Loop) ? Visibility.Hidden : Visibility.Visible);

// Gather the camera candidates
availableCameras.Clear();
availableCameras.AddRange(Target.ChildCameras);
});
this.AddExtensionsDropdown(ux);

return ux;

// Local function
static void FormatInstructionElement(bool isHeader, VisualElement e1, VisualElement e2, VisualElement e3)
Expand All @@ -110,10 +87,11 @@ static void FormatInstructionElement(bool isHeader, VisualElement e1, VisualElem
e1.style.marginLeft = isHeader ? 2 * InspectorUtility.SingleLineHeight - 3 : 0;
e1.style.flexBasis = floatFieldWidth + InspectorUtility.SingleLineHeight;
e1.style.flexGrow = 1;
e1.style.flexShrink = 0;

e2.style.marginLeft = isHeader ? 4 * InspectorUtility.SingleLineHeight - 3 : 0;
e2.style.flexBasis = floatFieldWidth + InspectorUtility.SingleLineHeight;
e2.style.flexGrow = 1;
e2.style.flexShrink = 0;

floatFieldWidth += isHeader ? InspectorUtility.SingleLineHeight/2 - 1 : 0;
e3.style.flexBasis = floatFieldWidth;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,13 @@ public override VisualElement CreateInspectorGUI()
"This component requires a CinemachineSplineDolly component referencing a nonempty Spline",
HelpBoxMessageType.Warning);
ux.Add(invalidHelp);
var toolButton = ux.AddChild(new Button(() => ToolManager.SetActiveTool(typeof(LookAtDataOnSplineTool)))
{ text = "Edit Targets in Scene View" });

var tooltip = "Use the Scene View tool to Edit the LookAt targets on the spline";
var buttonRow = ux.AddChild(new InspectorUtility.LabeledRow("Edit in Scene View", tooltip));
var toolButton = buttonRow.Contents.AddChild(
CinemachineSceneToolHelpers.CreateSceneToolActivationButtonForInspector(
typeof(LookAtDataOnSplineTool), target, LookAtDataOnSplineTool.IconPath, tooltip));

ux.TrackAnyUserActivity(() =>
{
var haveSpline = splineData != null && splineData.GetGetSplineAndDolly(out _, out _);
Expand Down Expand Up @@ -309,11 +314,13 @@ class LookAtDataOnSplineTool : EditorTool
public static Action<CinemachineSplineDollyLookAtTargets, int> s_OnDataIndexDragged;
public static Action<CinemachineSplineDollyLookAtTargets, int> s_OnDataLookAtDragged;

public static string IconPath => $"{CinemachineSceneToolHelpers.IconPath}/[email protected]";

void OnEnable()
{
m_IconContent = new ()
{
image = AssetDatabase.LoadAssetAtPath<Texture2D>($"{CinemachineSceneToolHelpers.IconPath}/[email protected]"),
image = AssetDatabase.LoadAssetAtPath<Texture2D>(IconPath),
tooltip = "Assign LookAt targets to positions on the spline."
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@ public override VisualElement CreateInspectorGUI()
"This component should be associated with a non-empty spline",
HelpBoxMessageType.Warning);
ux.Add(invalidHelp);
var toolButton = ux.AddChild(new Button(() => ToolManager.SetActiveTool(typeof(SplineRollTool)))
{ text = "Edit Data Points in Scene View" });

var tooltip = "Use the Scene View tool to adjust the roll data points";
var buttonRow = ux.AddChild(new InspectorUtility.LabeledRow("Edit in Scene View", tooltip));
var toolButton = buttonRow.Contents.AddChild(
CinemachineSceneToolHelpers.CreateSceneToolActivationButtonForInspector(
typeof(SplineRollTool), target, SplineRollTool.IconPath, tooltip));

ux.TrackAnyUserActivity(() =>
{
var haveSpline = splineData != null && splineData.SplineContainer != null;
Expand Down Expand Up @@ -244,11 +248,13 @@ sealed class SplineRollTool : EditorTool
public static Action<CinemachineSplineRoll, int> s_OnDataIndexDragged;
public static Action<CinemachineSplineRoll, int> s_OnDataLookAtDragged;

public static string IconPath => $"{CinemachineSceneToolHelpers.IconPath}/[email protected]";

void OnEnable()
{
m_IconContent = new GUIContent
{
image = AssetDatabase.LoadAssetAtPath<Texture2D>($"{CinemachineSceneToolHelpers.IconPath}/[email protected]"),
image = AssetDatabase.LoadAssetAtPath<Texture2D>(IconPath),
tooltip = "Adjust the roll data points along the spline"
};
}
Expand Down
Loading

0 comments on commit 09068c8

Please sign in to comment.