diff --git a/com.unity.cinemachine/CHANGELOG.md b/com.unity.cinemachine/CHANGELOG.md index 60b94f4c5..be445ee28 100644 --- a/com.unity.cinemachine/CHANGELOG.md +++ b/com.unity.cinemachine/CHANGELOG.md @@ -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 diff --git a/com.unity.cinemachine/Documentation~/CinemachineSplineDollyLookAtTargets.md b/com.unity.cinemachine/Documentation~/CinemachineSplineDollyLookAtTargets.md index de91b0eae..6f1990222 100644 --- a/com.unity.cinemachine/Documentation~/CinemachineSplineDollyLookAtTargets.md +++ b/com.unity.cinemachine/Documentation~/CinemachineSplineDollyLookAtTargets.md @@ -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. @@ -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. | diff --git a/com.unity.cinemachine/Documentation~/CinemachineSplineRoll.md b/com.unity.cinemachine/Documentation~/CinemachineSplineRoll.md index cd3543c03..7b599f12d 100644 --- a/com.unity.cinemachine/Documentation~/CinemachineSplineRoll.md +++ b/com.unity.cinemachine/Documentation~/CinemachineSplineRoll.md @@ -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. | diff --git a/com.unity.cinemachine/Documentation~/images/CinemachineSplineRollInspector.png b/com.unity.cinemachine/Documentation~/images/CinemachineSplineRollInspector.png new file mode 100644 index 000000000..4288510f8 Binary files /dev/null and b/com.unity.cinemachine/Documentation~/images/CinemachineSplineRollInspector.png differ diff --git a/com.unity.cinemachine/Documentation~/images/SplineDollyLookAtTargetsInspector.png b/com.unity.cinemachine/Documentation~/images/SplineDollyLookAtTargetsInspector.png new file mode 100644 index 000000000..dd6dbad8f Binary files /dev/null and b/com.unity.cinemachine/Documentation~/images/SplineDollyLookAtTargetsInspector.png differ diff --git a/com.unity.cinemachine/Editor/Editors/CinemachineCameraEditor.cs b/com.unity.cinemachine/Editor/Editors/CinemachineCameraEditor.cs index eadfb8e50..f7bb7d892 100644 --- a/com.unity.cinemachine/Editor/Editors/CinemachineCameraEditor.cs +++ b/com.unity.cinemachine/Editor/Editors/CinemachineCameraEditor.cs @@ -8,7 +8,7 @@ namespace Unity.Cinemachine.Editor { [CustomEditor(typeof(CinemachineCamera))] [CanEditMultipleObjects] - class CinemachineCameraEditor : UnityEditor.Editor + class CinemachineCameraEditor : CinemachineVirtualCameraBaseEditor { CinemachineCamera Target => target as CinemachineCamera; @@ -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(""); @@ -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) @@ -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))] diff --git a/com.unity.cinemachine/Editor/Editors/CinemachineClearShotEditor.cs b/com.unity.cinemachine/Editor/Editors/CinemachineClearShotEditor.cs index d4c7b0a4e..6cd51e4a9 100644 --- a/com.unity.cinemachine/Editor/Editors/CinemachineClearShotEditor.cs +++ b/com.unity.cinemachine/Editor/Editors/CinemachineClearShotEditor.cs @@ -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; @@ -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))); @@ -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) @@ -80,7 +72,6 @@ public override VisualElement CreateInspectorGUI() break; } }); - return ux; } enum EvaluatorState diff --git a/com.unity.cinemachine/Editor/Editors/CinemachineExternalCameraEditor.cs b/com.unity.cinemachine/Editor/Editors/CinemachineExternalCameraEditor.cs deleted file mode 100644 index 2e0179908..000000000 --- a/com.unity.cinemachine/Editor/Editors/CinemachineExternalCameraEditor.cs +++ /dev/null @@ -1,24 +0,0 @@ -using UnityEditor; -using UnityEngine.UIElements; -using UnityEditor.UIElements; - -namespace Unity.Cinemachine.Editor -{ - [CustomEditor(typeof(CinemachineExternalCamera))] - [CanEditMultipleObjects] - class CinemachineExternalCameraEditor : UnityEditor.Editor - { - CinemachineExternalCamera Target => target as CinemachineExternalCamera; - - public override VisualElement CreateInspectorGUI() - { - var ux = new VisualElement(); - - this.AddCameraStatus(ux); - this.AddTransitionsSection(ux, new () { serializedObject.FindProperty(() => Target.BlendHint) }); - ux.Add(new PropertyField(serializedObject.FindProperty(() => Target.LookAtTarget))); - - return ux; - } - } -} diff --git a/com.unity.cinemachine/Editor/Editors/CinemachineMixingCameraEditor.cs b/com.unity.cinemachine/Editor/Editors/CinemachineMixingCameraEditor.cs index 02a366cbe..a5847bf1a 100644 --- a/com.unity.cinemachine/Editor/Editors/CinemachineMixingCameraEditor.cs +++ b/com.unity.cinemachine/Editor/Editors/CinemachineMixingCameraEditor.cs @@ -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); @@ -47,9 +42,6 @@ public override VisualElement CreateInspectorGUI() DrawProportionIndicator(children, numCameras, totalWeight); })); - ux.AddSpace(); - this.AddExtensionsDropdown(ux); - ux.TrackAnyUserActivity(() => { if (Target == null) @@ -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( diff --git a/com.unity.cinemachine/Editor/Editors/CinemachineSequencerCameraEditor.cs b/com.unity.cinemachine/Editor/Editors/CinemachineSequencerCameraEditor.cs index 5a0366e77..487d20dae 100644 --- a/com.unity.cinemachine/Editor/Editors/CinemachineSequencerCameraEditor.cs +++ b/com.unity.cinemachine/Editor/Editors/CinemachineSequencerCameraEditor.cs @@ -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); @@ -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, @@ -53,21 +49,12 @@ public override VisualElement CreateInspectorGUI() var instructions = serializedObject.FindProperty(() => Target.Instructions); list.BindProperty(instructions); - // Available camera candidates - var availableCameras = new List(); - list.makeItem = () => { var row = new BindableElement { style = { flexDirection = FlexDirection.Row }}; var def = new CinemachineSequencerCamera.Instruction(); - var vcamSel = row.AddChild(new PopupField - { - 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 _)); @@ -77,9 +64,6 @@ public override VisualElement CreateInspectorGUI() return row; }; - container.AddSpace(); - this.AddChildCameras(container, null); - container.TrackAnyUserActivity(() => { if (Target == null || list.itemsSource == null) @@ -93,14 +77,7 @@ public override VisualElement CreateInspectorGUI() var index = 0; list.Query().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) @@ -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; diff --git a/com.unity.cinemachine/Editor/Editors/CinemachineSplineDollyLookAtTargetsEditor.cs b/com.unity.cinemachine/Editor/Editors/CinemachineSplineDollyLookAtTargetsEditor.cs index d2a097255..451e78532 100644 --- a/com.unity.cinemachine/Editor/Editors/CinemachineSplineDollyLookAtTargetsEditor.cs +++ b/com.unity.cinemachine/Editor/Editors/CinemachineSplineDollyLookAtTargetsEditor.cs @@ -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 _); @@ -309,11 +314,13 @@ class LookAtDataOnSplineTool : EditorTool public static Action s_OnDataIndexDragged; public static Action s_OnDataLookAtDragged; + public static string IconPath => $"{CinemachineSceneToolHelpers.IconPath}/CmSplineLookAtTargetsTool@256.png"; + void OnEnable() { m_IconContent = new () { - image = AssetDatabase.LoadAssetAtPath($"{CinemachineSceneToolHelpers.IconPath}/CmSplineLookAtTargetsTool@256.png"), + image = AssetDatabase.LoadAssetAtPath(IconPath), tooltip = "Assign LookAt targets to positions on the spline." }; } diff --git a/com.unity.cinemachine/Editor/Editors/CinemachineSplineRollEditor.cs b/com.unity.cinemachine/Editor/Editors/CinemachineSplineRollEditor.cs index 523f580d1..96ce088ff 100644 --- a/com.unity.cinemachine/Editor/Editors/CinemachineSplineRollEditor.cs +++ b/com.unity.cinemachine/Editor/Editors/CinemachineSplineRollEditor.cs @@ -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; @@ -244,11 +248,13 @@ sealed class SplineRollTool : EditorTool public static Action s_OnDataIndexDragged; public static Action s_OnDataLookAtDragged; + public static string IconPath => $"{CinemachineSceneToolHelpers.IconPath}/CmSplineRollTool@256.png"; + void OnEnable() { m_IconContent = new GUIContent { - image = AssetDatabase.LoadAssetAtPath($"{CinemachineSceneToolHelpers.IconPath}/CmSplineRollTool@256.png"), + image = AssetDatabase.LoadAssetAtPath(IconPath), tooltip = "Adjust the roll data points along the spline" }; } diff --git a/com.unity.cinemachine/Editor/Editors/CinemachineStateDrivenCameraEditor.cs b/com.unity.cinemachine/Editor/Editors/CinemachineStateDrivenCameraEditor.cs index 53fab70bb..5af0a2da8 100644 --- a/com.unity.cinemachine/Editor/Editors/CinemachineStateDrivenCameraEditor.cs +++ b/com.unity.cinemachine/Editor/Editors/CinemachineStateDrivenCameraEditor.cs @@ -11,7 +11,7 @@ namespace Unity.Cinemachine.Editor { [CanEditMultipleObjects] [CustomEditor(typeof(CinemachineStateDrivenCamera))] - class CinemachineStateDrivenCameraEditor : UnityEditor.Editor + class CinemachineStateDrivenCameraEditor : CinemachineVirtualCameraBaseEditor { CinemachineStateDrivenCamera Target => target as CinemachineStateDrivenCamera; @@ -20,15 +20,8 @@ class CinemachineStateDrivenCameraEditor : UnityEditor.Editor List m_TargetStateNames = new(); Dictionary m_StateIndexLookup; - public override VisualElement CreateInspectorGUI() + protected override void AddInspectorProperties(VisualElement ux) { - var ux = new VisualElement(); - - var noTargetHelp = ux.AddChild(new HelpBox("An Animated Target is required.", HelpBoxMessageType.Warning)); - - this.AddCameraStatus(ux); - this.AddTransitionsSection(ux); - ux.AddHeader("Global Settings"); this.AddGlobalControls(ux); @@ -36,8 +29,8 @@ public override VisualElement CreateInspectorGUI() ux.Add(new PropertyField(serializedObject.FindProperty(() => Target.DefaultTarget))); ux.Add(new PropertyField(serializedObject.FindProperty(() => Target.DefaultBlend))); ux.Add(new PropertyField(serializedObject.FindProperty(() => Target.CustomBlends))); - ux.Add(new PropertyField(serializedObject.FindProperty(() => Target.AnimatedTarget))); + ux.Add(new PropertyField(serializedObject.FindProperty(() => Target.AnimatedTarget))); var layerProp = serializedObject.FindProperty(() => Target.LayerIndex); var layerSel = ux.AddChild(new PopupField(layerProp.displayName) { tooltip = layerProp.tooltip }); layerSel.AddToClassList(InspectorUtility.AlignFieldClassName); @@ -46,6 +39,7 @@ public override VisualElement CreateInspectorGUI() layerProp.intValue = Mathf.Max(0, m_LayerNames.FindIndex(v => v == evt.newValue)); serializedObject.ApplyModifiedProperties(); }); + var noTargetHelp = ux.AddChild(new HelpBox("An Animated Target is required.", HelpBoxMessageType.Warning)); ux.TrackAnyUserActivity(() => { @@ -62,6 +56,7 @@ public override VisualElement CreateInspectorGUI() HelpBoxMessageType.Info)); var container = ux.AddChild(new VisualElement() { style = { marginTop = 6 }}); + container.AddHeader("Instructions"); var vcam = Target; var header = container.AddChild(new VisualElement { style = { flexDirection = FlexDirection.Row, marginBottom = -2 } }); FormatInstructionElement(true, @@ -84,16 +79,13 @@ public override VisualElement CreateInspectorGUI() var instructions = serializedObject.FindProperty(() => Target.Instructions); list.BindProperty(instructions); - // Available camera candidates - var availableCameras = new List(); - list.makeItem = () => { var row = new BindableElement { style = { flexDirection = FlexDirection.Row }}; var def = new CinemachineStateDrivenCamera.Instruction(); - // This is the real state field, but it's hiddes + // This is the real state field, but it's hidden var hashField = row.AddChild(new IntegerField() { bindingPath = SerializedPropertyHelper.PropertyName(() => def.FullHash) }); hashField.SetVisible(false); @@ -135,14 +127,7 @@ public override VisualElement CreateInspectorGUI() evt.StopPropagation(); }); - var vcamSel = row.AddChild(new PopupField - { - 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 wait = row.AddChild(InspectorUtility.CreateDraggableField(() => def.ActivateAfter, row.AddChild(new Label(" ")), out _)); wait.SafeSetIsDelayed(); var hold = row.AddChild(InspectorUtility.CreateDraggableField(() => def.MinDuration, row.AddChild(new Label(" ")), out _)); @@ -161,17 +146,7 @@ public override VisualElement CreateInspectorGUI() var isMultiSelect = targets.Length > 1; multiSelectMsg.SetVisible(isMultiSelect); container.SetVisible(!isMultiSelect); - - // Gather the camera candidates - availableCameras.Clear(); - availableCameras.AddRange(Target.ChildCameras); }); - container.AddSpace(); - this.AddChildCameras(container, null); - container.AddSpace(); - this.AddExtensionsDropdown(ux); - - return ux; // Local function static void FormatInstructionElement( @@ -182,9 +157,11 @@ static void FormatInstructionElement( 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.flexBasis = floatFieldWidth + InspectorUtility.SingleLineHeight; e2.style.flexGrow = 1; + e2.style.flexShrink = 0; floatFieldWidth += isHeader ? InspectorUtility.SingleLineHeight/2 - 1 : 0; e3.style.flexBasis = floatFieldWidth; diff --git a/com.unity.cinemachine/Editor/Editors/CinemachineVirtualCameraBaseEditor.cs b/com.unity.cinemachine/Editor/Editors/CinemachineVirtualCameraBaseEditor.cs new file mode 100644 index 000000000..08ac554d3 --- /dev/null +++ b/com.unity.cinemachine/Editor/Editors/CinemachineVirtualCameraBaseEditor.cs @@ -0,0 +1,54 @@ +using UnityEditor; +using UnityEditor.UIElements; +using UnityEngine.UIElements; + +namespace Unity.Cinemachine.Editor +{ + /// + /// Inspector for the CinemachineVirtualCameraBase class. It will be chosen by default + /// by Unity. You can inherit from it and customize it. + /// + [CustomEditor(typeof(CinemachineVirtualCameraBase), true, isFallback = true)] + [CanEditMultipleObjects] + public class CinemachineVirtualCameraBaseEditor : UnityEditor.Editor + { + //protected virtual void OnEnable() => Undo.undoRedoPerformed += ResetTarget; + //protected virtual void OnDisable() => Undo.undoRedoPerformed -= ResetTarget; + + /// + public override VisualElement CreateInspectorGUI() + { + var vcam = target as CinemachineVirtualCameraBase; + var ux = new VisualElement(); + + this.AddCameraStatus(ux); + ux.Add(new PropertyField(serializedObject.FindProperty(() => vcam.Priority))); + ux.Add(new PropertyField(serializedObject.FindProperty(() => vcam.OutputChannel))); + ux.Add(new PropertyField(serializedObject.FindProperty(() => vcam.StandbyUpdate))); + + AddInspectorProperties(ux); + + if (vcam is CinemachineCameraManagerBase manager && targets.Length == 1) + { + ux.AddSpace(); + this.AddChildCameras(ux, null); + } + ux.AddSpace(); + this.AddExtensionsDropdown(ux); + return ux; + } + + /// + /// Called by the default implementation of CreateInspectorGUI() to populate the inspector with the items that + /// are not specific to the base class. Default implementation iterates over the serialized properties and + /// adds an item for each. + /// + /// The VisualElement container to which to add items + protected virtual void AddInspectorProperties(VisualElement ux) + { + var p = serializedObject.FindProperty(() => (target as CinemachineVirtualCameraBase).StandbyUpdate); + if (p.NextVisible(false)) + InspectorUtility.AddRemainingProperties(ux, p); + } + } +} diff --git a/com.unity.cinemachine/Editor/Editors/CinemachineExternalCameraEditor.cs.meta b/com.unity.cinemachine/Editor/Editors/CinemachineVirtualCameraBaseEditor.cs.meta similarity index 69% rename from com.unity.cinemachine/Editor/Editors/CinemachineExternalCameraEditor.cs.meta rename to com.unity.cinemachine/Editor/Editors/CinemachineVirtualCameraBaseEditor.cs.meta index 97a0d8169..fccf1f9fc 100644 --- a/com.unity.cinemachine/Editor/Editors/CinemachineExternalCameraEditor.cs.meta +++ b/com.unity.cinemachine/Editor/Editors/CinemachineVirtualCameraBaseEditor.cs.meta @@ -1,8 +1,7 @@ fileFormatVersion: 2 -guid: 3226d9f88577be74197693bd17cff8c3 -timeCreated: 1506455627 -licenseType: Pro +guid: e7d72dd62f9cbd947958c4a7482ab605 MonoImporter: + externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 diff --git a/com.unity.cinemachine/Editor/PropertyDrawers/ChildCameraPropertyDrawer.cs b/com.unity.cinemachine/Editor/PropertyDrawers/ChildCameraPropertyDrawer.cs index 9d702c426..625caae4a 100644 --- a/com.unity.cinemachine/Editor/PropertyDrawers/ChildCameraPropertyDrawer.cs +++ b/com.unity.cinemachine/Editor/PropertyDrawers/ChildCameraPropertyDrawer.cs @@ -10,14 +10,25 @@ class ChildCameraPropertyDrawer : PropertyDrawer { public override VisualElement CreatePropertyGUI(SerializedProperty property) { - if (property.serializedObject.targetObject is CinemachineCameraManagerBase m) + if (property.serializedObject.targetObject is CinemachineCameraManagerBase manager) { - var row = new InspectorUtility.LabeledRow(preferredLabel, property.tooltip); - var vcamSel = row.Contents.AddChild(new PopupField() { style = {flexBasis = 10, flexGrow = 1}}); + var vcamSel = new PopupField() { style = {flexBasis = 10, flexGrow = 1}}; vcamSel.BindProperty(property); vcamSel.formatListItemCallback = (obj) => obj == null ? "(null)" : obj.name; vcamSel.formatSelectedValueCallback = (obj) => obj == null ? "(null)" : obj.name; - vcamSel.TrackAnyUserActivity(() => vcamSel.choices = m == null ? null : m.ChildCameras.Cast().ToList()); + vcamSel.TrackAnyUserActivity(() => + { + if (manager != null && manager.ChildCameras != null) + { + vcamSel.choices = manager.ChildCameras.Cast().ToList(); + vcamSel.SetValueWithoutNotify(property.objectReferenceValue); + } + }); + if (preferredLabel.Length == 0) + return vcamSel; + + var row = new InspectorUtility.LabeledRow(preferredLabel, property.tooltip); + row.Contents.AddChild(vcamSel); return row; } return new PropertyField(property, preferredLabel); diff --git a/com.unity.cinemachine/Editor/Utility/CinemachineSceneToolHelpers.cs b/com.unity.cinemachine/Editor/Utility/CinemachineSceneToolHelpers.cs index 7d99ad2fe..d3d98bf42 100644 --- a/com.unity.cinemachine/Editor/Utility/CinemachineSceneToolHelpers.cs +++ b/com.unity.cinemachine/Editor/Utility/CinemachineSceneToolHelpers.cs @@ -1,6 +1,8 @@ using System; using UnityEditor; +using UnityEditor.EditorTools; using UnityEngine; +using UnityEngine.UIElements; namespace Unity.Cinemachine.Editor { @@ -25,6 +27,43 @@ static class CinemachineSceneToolHelpers static string ResourcePath => $"{CinemachineCore.kPackageRoot}/Editor/EditorResources"; public static string IconPath => $"{ResourcePath}/Icons/{SkinSuffix}"; + static Color s_NormalBkgColor = Color.black; + + /// Create a button for the inspector that activates a scene tool + public static VisualElement CreateSceneToolActivationButtonForInspector( + Type toolType, UnityEngine.Object target, string iconPath, string tooltip) + { + var toolButton = new Button(() => + { + if (Selection.activeObject != target) + Selection.activeObject = target; + EditorApplication.delayCall += () => ToolManager.SetActiveTool(toolType); + }) + { + tooltip = tooltip, + style = + { + flexGrow = 0, + flexBasis = 2 * InspectorUtility.SingleLineHeight, + height = InspectorUtility.SingleLineHeight + 3, + }, + }; + toolButton.Add(new Image { image = AssetDatabase.LoadAssetAtPath(iconPath) }); + + // Capture "normal" colors + if (s_NormalBkgColor == Color.black) + toolButton.OnInitialGeometry(() => s_NormalBkgColor = toolButton.resolvedStyle.backgroundColor); + + toolButton.ContinuousUpdate(() => + { + if (ToolManager.activeToolType == toolType) + toolButton.style.backgroundColor = Color.Lerp(s_NormalBkgColor, new Color(0.1f, 0.3f, 0.6f, 1), 0.5f); + else + toolButton.style.backgroundColor = s_NormalBkgColor; + }); + return toolButton; + } + public static float SliderHandleDelta(Vector3 newPos, Vector3 oldPos, Vector3 forward) { var delta = newPos - oldPos; diff --git a/com.unity.cinemachine/Editor/Utility/CmCameraInspectorUtility.cs b/com.unity.cinemachine/Editor/Utility/CmCameraInspectorUtility.cs index ae2d9ffc5..1d6d0b122 100644 --- a/com.unity.cinemachine/Editor/Utility/CmCameraInspectorUtility.cs +++ b/com.unity.cinemachine/Editor/Utility/CmCameraInspectorUtility.cs @@ -151,19 +151,6 @@ public static void AddCameraStatus(this UnityEditor.Editor editor, VisualElement }); } - public static void AddTransitionsSection( - this UnityEditor.Editor editor, VisualElement ux, - List otherProperties = null) - { - var serializedObject = editor.serializedObject; - var target = editor.target as CinemachineVirtualCameraBase; - ux.Add(new PropertyField(serializedObject.FindProperty(() => target.Priority))); - ux.Add(new PropertyField(serializedObject.FindProperty(() => target.OutputChannel))); - ux.Add(new PropertyField(serializedObject.FindProperty(() => target.StandbyUpdate))); - for (int i = 0; otherProperties != null && i < otherProperties.Count; ++i) - ux.Add(new PropertyField(otherProperties[i])); - } - /// Add the pipeline control dropdowns in the inspector public static void AddPipelineDropdowns(this UnityEditor.Editor editor, VisualElement ux) { @@ -532,7 +519,7 @@ public static void AddChildCameras( { name = "vcamSelector", objectType = typeof(CinemachineVirtualCameraBase), - style = { flexBasis = 20, flexGrow = 1 } + style = { flexBasis = floatFieldWidth * 2, flexGrow = 1, flexShrink = 0 } }).SetEnabled(false); var priorityField = row.AddChild(InspectorUtility.CreateDraggableField( @@ -540,6 +527,7 @@ public static void AddChildCameras( priorityField.name = "priorityField"; priorityField.style.flexBasis = floatFieldWidth; priorityField.style.flexGrow = 0; + priorityField.style.flexShrink = 0; priorityField.style.marginRight = 4; priorityField.SafeSetIsDelayed(); diff --git a/com.unity.cinemachine/Editor/Utility/InspectorUtility.cs b/com.unity.cinemachine/Editor/Utility/InspectorUtility.cs index ca2d7271e..5a89d3bc7 100644 --- a/com.unity.cinemachine/Editor/Utility/InspectorUtility.cs +++ b/com.unity.cinemachine/Editor/Utility/InspectorUtility.cs @@ -253,7 +253,7 @@ public static void AddHeader(this VisualElement ux, string text, string tooltip row.focusable = false; row.Label.style.flexGrow = 1; row.Label.style.paddingTop = verticalPad; - row.Label.style.paddingBottom = EditorGUIUtility.standardVerticalSpacing - 2; + row.Label.style.paddingBottom = EditorGUIUtility.standardVerticalSpacing; } /// @@ -360,9 +360,9 @@ public static Button MiniPopupButton(string tooltip = null, ContextualMenuManipu var button = new Button { tooltip = tooltip, style = { flexGrow = 0, - flexBasis = SingleLineHeight, + flexBasis = SingleLineHeight + 3, backgroundImage = (StyleBackground)EditorGUIUtility.IconContent("_Popup").image, - width = SingleLineHeight, height = SingleLineHeight, + width = SingleLineHeight, height = SingleLineHeight + 3, alignSelf = Align.Center, paddingRight = 0, marginRight = 0 }}; @@ -383,9 +383,9 @@ public static Button MiniDropdownButton(string tooltip = null, ContextualMenuMan var button = new Button { tooltip = tooltip, style = { flexGrow = 0, - flexBasis = SingleLineHeight, + flexBasis = SingleLineHeight + 3, backgroundImage = (StyleBackground)EditorGUIUtility.IconContent("dropdown").image, - width = SingleLineHeight, height = SingleLineHeight, + width = SingleLineHeight, height = SingleLineHeight + 3, alignSelf = Align.Center, paddingRight = 0, marginRight = 0 }}; diff --git a/com.unity.cinemachine/Editor/Utility/SplineDataInspectorUtility.cs b/com.unity.cinemachine/Editor/Utility/SplineDataInspectorUtility.cs index bb9719add..66ab45301 100644 --- a/com.unity.cinemachine/Editor/Utility/SplineDataInspectorUtility.cs +++ b/com.unity.cinemachine/Editor/Utility/SplineDataInspectorUtility.cs @@ -36,6 +36,7 @@ public static VisualElement CreatePathUnitField(SerializedProperty splineDataPro }); enumField.TrackPropertyValue(indexUnitProp, (p) => enumField.value = (PathIndexUnit)indexUnitProp.enumValueIndex); enumField.TrackAnyUserActivity(() => enumField.SetEnabled(getSpline?.Invoke() != null)); + enumField.AddToClassList(InspectorUtility.AlignFieldClassName); return enumField; } diff --git a/com.unity.cinemachine/Runtime/Behaviours/CinemachineExternalCamera.cs b/com.unity.cinemachine/Runtime/Behaviours/CinemachineExternalCamera.cs index 93976d47d..f48096a92 100644 --- a/com.unity.cinemachine/Runtime/Behaviours/CinemachineExternalCamera.cs +++ b/com.unity.cinemachine/Runtime/Behaviours/CinemachineExternalCamera.cs @@ -14,14 +14,6 @@ namespace Unity.Cinemachine [HelpURL(Documentation.BaseURL + "manual/CinemachineExternalCamera.html")] public class CinemachineExternalCamera : CinemachineVirtualCameraBase { - /// The object that the camera is looking at. Setting this may improve the - /// quality of the blends to and from this camera - [Tooltip("The object that the camera is looking at. Setting this may improve the " - + "quality of the blends to and from this camera")] - [NoSaveDuringPlay] - [FormerlySerializedAs("m_LookAt")] - public Transform LookAtTarget = null; - /// Hint for transitioning to and from this CinemachineCamera. Hints can be combined, although /// not all combinations make sense. In the case of conflicting hints, Cinemachine will /// make an arbitrary choice. @@ -32,6 +24,14 @@ public class CinemachineExternalCamera : CinemachineVirtualCameraBase [FormerlySerializedAs("m_BlendHint")] public CinemachineCore.BlendHints BlendHint = 0; + /// The object that the camera is looking at. Setting this may improve the + /// quality of the blends to and from this camera + [Tooltip("The object that the camera is looking at. Setting this may improve the " + + "quality of the blends to and from this camera")] + [NoSaveDuringPlay] + [FormerlySerializedAs("m_LookAt")] + public Transform LookAtTarget = null; + Camera m_Camera; CameraState m_State = CameraState.Default; diff --git a/com.unity.cinemachine/Runtime/Behaviours/CinemachineStateDrivenCamera.cs b/com.unity.cinemachine/Runtime/Behaviours/CinemachineStateDrivenCamera.cs index 182338fa4..579d8e0a0 100644 --- a/com.unity.cinemachine/Runtime/Behaviours/CinemachineStateDrivenCamera.cs +++ b/com.unity.cinemachine/Runtime/Behaviours/CinemachineStateDrivenCamera.cs @@ -52,6 +52,7 @@ public struct Instruction /// The virtual camera to activate when the animation state becomes active [Tooltip("The virtual camera to activate when the animation state becomes active")] [FormerlySerializedAs("m_VirtualCamera")] + [ChildCameraProperty] public CinemachineVirtualCameraBase Camera; /// How long to wait (in seconds) before activating the camera. /// This filters out very short state durations diff --git a/com.unity.cinemachine/Runtime/Core/CinemachineCameraManagerBase.cs b/com.unity.cinemachine/Runtime/Core/CinemachineCameraManagerBase.cs index 70b376f69..ecc470531 100644 --- a/com.unity.cinemachine/Runtime/Core/CinemachineCameraManagerBase.cs +++ b/com.unity.cinemachine/Runtime/Core/CinemachineCameraManagerBase.cs @@ -51,6 +51,7 @@ public struct DefaultTargetSettings public CinemachineBlenderSettings CustomBlends = null; List m_ChildCameras; + int m_ChildCountCache; // used for invalidating child cache readonly BlendManager m_BlendManager = new (); CameraState m_State = CameraState.Default; ICinemachineCamera m_TransitioningFrom; @@ -282,10 +283,12 @@ public void InvalidateCameraCache() /// True if a cache rebuild was performed, false if cache is up to date. protected virtual bool UpdateCameraCache() { - if (m_ChildCameras != null) + var childCount = transform.childCount; + if (m_ChildCameras != null && m_ChildCountCache == childCount) return false; PreviousStateIsValid = false; m_ChildCameras = new(); + m_ChildCountCache = childCount; GetComponentsInChildren(true, m_ChildCameras); for (int i = m_ChildCameras.Count-1; i >= 0; --i) if (m_ChildCameras[i].transform.parent != transform) diff --git a/com.unity.cinemachine/Runtime/Core/CinemachinePropertyAttribute.cs b/com.unity.cinemachine/Runtime/Core/CinemachinePropertyAttribute.cs index 5efe34d12..f5ef0e31b 100644 --- a/com.unity.cinemachine/Runtime/Core/CinemachinePropertyAttribute.cs +++ b/com.unity.cinemachine/Runtime/Core/CinemachinePropertyAttribute.cs @@ -163,7 +163,7 @@ public enum RequiredTargets } /// - /// Attribute applied to a CinemachineVirtualCameraBase property to produce + /// Attribute applied to a CinemachineCameraManagerBase property to produce /// a child camera selector in the inspector. /// public sealed class ChildCameraPropertyAttribute : PropertyAttribute {} diff --git a/com.unity.cinemachine/Samples~/Shared Assets/Cameron/Model/CameronSimpleController.controller b/com.unity.cinemachine/Samples~/Shared Assets/Cameron/Model/CameronSimpleController.controller index de449873a..e74f1a65d 100644 --- a/com.unity.cinemachine/Samples~/Shared Assets/Cameron/Model/CameronSimpleController.controller +++ b/com.unity.cinemachine/Samples~/Shared Assets/Cameron/Model/CameronSimpleController.controller @@ -227,9 +227,9 @@ AnimatorStateTransition: m_Mute: 0 m_IsExit: 0 serializedVersion: 3 - m_TransitionDuration: 0.06615287 + m_TransitionDuration: 0.09497708 m_TransitionOffset: 0 - m_ExitTime: 0.58234453 + m_ExitTime: 0.5823446 m_HasExitTime: 0 m_HasFixedDuration: 1 m_InterruptionSource: 0 @@ -615,9 +615,9 @@ AnimatorStateTransition: m_Mute: 0 m_IsExit: 0 serializedVersion: 3 - m_TransitionDuration: 0.25 - m_TransitionOffset: 0 - m_ExitTime: 0.9 + m_TransitionDuration: 0.13899994 + m_TransitionOffset: 0.17746145 + m_ExitTime: 0.9101155 m_HasExitTime: 0 m_HasFixedDuration: 1 m_InterruptionSource: 0 @@ -646,9 +646,9 @@ AnimatorStateTransition: m_Mute: 0 m_IsExit: 0 serializedVersion: 3 - m_TransitionDuration: 0.25 + m_TransitionDuration: 0.12810051 m_TransitionOffset: 0 - m_ExitTime: 0.5614035 + m_ExitTime: 0.56140345 m_HasExitTime: 0 m_HasFixedDuration: 1 m_InterruptionSource: 0 diff --git a/com.unity.cinemachine/Samples~/Shared Assets/Scripts/SimplePlayerAnimator.cs b/com.unity.cinemachine/Samples~/Shared Assets/Scripts/SimplePlayerAnimator.cs index 25789e0cc..491b9a611 100644 --- a/com.unity.cinemachine/Samples~/Shared Assets/Scripts/SimplePlayerAnimator.cs +++ b/com.unity.cinemachine/Samples~/Shared Assets/Scripts/SimplePlayerAnimator.cs @@ -19,7 +19,6 @@ namespace Unity.Cinemachine.Samples /// and expects to be driven by the SimplePlayerControllerBase using the STartJump, EndJump, /// and PostUpdate callbacks. /// - [RequireComponent(typeof(Animator))] public class SimplePlayerAnimator : MonoBehaviour { [Tooltip("Tune this to the animation in the model: feet should not slide when walking at this speed")] @@ -34,24 +33,49 @@ public class SimplePlayerAnimator : MonoBehaviour [Tooltip("Scale factor for the overall speed of the jump animation")] public float JumpAnimationScale = 0.65f; - Animator m_Animator; SimplePlayerControllerBase m_Controller; Vector3 m_PreviousPosition; // used if m_Controller == null or disabled - bool m_WasWalking; - bool m_WasRunning; + + protected struct AnimationParams + { + public bool IsWalking; + public bool IsRunning; + public bool IsJumping; + public bool LandTriggered; + public bool JumpTriggered; + public Vector3 Direction; // normalized direction of motion + public float MotionScale; // scale factor for the animation speed + public float JumpScale; // scale factor for the jump animation + } + AnimationParams m_AnimationParams; + const float k_IdleThreshold = 0.2f; - void Start() + public enum States { Idle, Walk, Run, Jump, RunJump } + + /// Current state of the player + public States State + { + get + { + if (m_AnimationParams.IsJumping) + return m_AnimationParams.IsRunning ? States.RunJump : States.Jump; + if (m_AnimationParams.IsRunning) + return States.Run; + return m_AnimationParams.IsWalking ? States.Walk : States.Idle; + } + } + + protected virtual void Start() { m_PreviousPosition = transform.position; - TryGetComponent(out m_Animator); m_Controller = GetComponentInParent(); if (m_Controller != null) { // Install our callbacks to handle jump and animation based on velocity - m_Controller.StartJump += () => OnJump(true); - m_Controller.EndJump += () => OnJump(false); - m_Controller.PostUpdate += (vel, jumpAnimationScale) => UpdateAnimation(vel, jumpAnimationScale); + m_Controller.StartJump += () => m_AnimationParams.JumpTriggered = true; + m_Controller.EndJump += () => m_AnimationParams.LandTriggered = true; + m_Controller.PostUpdate += (vel, jumpAnimationScale) => UpdateAnimationState(vel, jumpAnimationScale); } } @@ -59,7 +83,7 @@ void Start() /// LateUpdate is used to avoid having to worry about script execution order: /// it can be assumed that the player has already been moved. /// - virtual protected void LateUpdate() + protected virtual void LateUpdate() { // In no-controller mode, we monitor the player's motion and deduce the appropriate animation. // We don't support jumping in this mode. @@ -69,17 +93,10 @@ virtual protected void LateUpdate() var pos = transform.position; var vel = Quaternion.Inverse(transform.rotation) * (pos - m_PreviousPosition) / Time.deltaTime; m_PreviousPosition = pos; - UpdateAnimation(vel, 1); + UpdateAnimationState(vel, 1); } } - /// - /// Called by the SimplePlayerControllerBase when the player starts or ends a jump. - /// Override this to interact appropriately with your animation controller. - /// - /// True when jump starts, false when it ends. - virtual protected void OnJump(bool jumping) => m_Animator.SetTrigger(jumping ? "Jump" : "Land"); - /// /// Update the animation based on the player's velocity. /// Override this to interact appropriately with your animation controller. @@ -87,34 +104,62 @@ virtual protected void LateUpdate() /// Player's velocity, in player-local coordinates. /// Scale factor to apply to the jump animation. /// It can be used to slow down the jump animation for longer jumps. - virtual protected void UpdateAnimation(Vector3 vel, float jumpAnimationScale) + void UpdateAnimationState(Vector3 vel, float jumpAnimationScale) { vel.y = 0; // we don't consider vertical movement var speed = vel.magnitude; // Hysteresis reduction - bool isRunning = speed > NormalWalkSpeed * 2 + (m_WasRunning ? -0.15f : 0.15f); - bool isWalking = !isRunning && speed > k_IdleThreshold + (m_WasWalking ? -0.05f : 0.05f); - m_WasWalking = isWalking; - m_WasRunning = isRunning; + bool isRunning = speed > NormalWalkSpeed * 2 + (m_AnimationParams.IsRunning ? -0.15f : 0.15f); + bool isWalking = !isRunning && speed > k_IdleThreshold + (m_AnimationParams.IsWalking ? -0.05f : 0.05f); + m_AnimationParams.IsWalking = isWalking; + m_AnimationParams.IsRunning = isRunning; // Set the normalized direction of motion and scale the animation speed to match motion speed - var dir = speed > k_IdleThreshold ? vel / speed : Vector3.zero; - var motionScale = isWalking ? speed / NormalWalkSpeed : 1; + m_AnimationParams.Direction = speed > k_IdleThreshold ? vel / speed : Vector3.zero; + m_AnimationParams.MotionScale = isWalking ? speed / NormalWalkSpeed : 1; + m_AnimationParams.JumpScale = JumpAnimationScale * jumpAnimationScale; // We scale the sprint animation speed to loosely match the actual speed, but we cheat // at the high end to avoid making the animation look ridiculous if (isRunning) - motionScale = (speed < NormalSprintSpeed) + m_AnimationParams.MotionScale = (speed < NormalSprintSpeed) ? speed / NormalSprintSpeed : Mathf.Min(MaxSprintScale, 1 + (speed - NormalSprintSpeed) / (3 * NormalSprintSpeed)); - m_Animator.SetFloat("DirX", dir.x); - m_Animator.SetFloat("DirZ", dir.z); - m_Animator.SetFloat("MotionScale", motionScale); - m_Animator.SetBool("Walking", isWalking); - m_Animator.SetBool("Running", isRunning); - m_Animator.SetFloat("JumpScale", JumpAnimationScale * jumpAnimationScale); + UpdateAnimation(m_AnimationParams); + + if (m_AnimationParams.JumpTriggered) + m_AnimationParams.IsJumping = true; + if (m_AnimationParams.LandTriggered) + m_AnimationParams.IsJumping = false; + + m_AnimationParams.JumpTriggered = false; + m_AnimationParams.LandTriggered = false; + } + + /// + /// Update the animation based on the player's state. + /// Override this to interact appropriately with your animation controller. + /// + protected virtual void UpdateAnimation(AnimationParams animationParams) + { + if (!TryGetComponent(out Animator animator)) + { + Debug.LogError("SimplePlayerAnimator: An Animator component is required"); + return; + } + animator.SetFloat("DirX", animationParams.Direction.x); + animator.SetFloat("DirZ", animationParams.Direction.z); + animator.SetFloat("MotionScale", animationParams.MotionScale); + animator.SetBool("Walking", animationParams.IsWalking); + animator.SetBool("Running", animationParams.IsRunning); + animator.SetFloat("JumpScale", animationParams.JumpScale); + + if (m_AnimationParams.JumpTriggered) + animator.SetTrigger("Jump"); + if (m_AnimationParams.LandTriggered) + animator.SetTrigger("Land"); } } } diff --git a/com.unity.cinemachine/package.json b/com.unity.cinemachine/package.json index e05deae4a..3e65a92dd 100644 --- a/com.unity.cinemachine/package.json +++ b/com.unity.cinemachine/package.json @@ -1,7 +1,7 @@ { "name": "com.unity.cinemachine", "displayName": "Cinemachine", - "version": "3.1.1", + "version": "3.1.2", "unity": "2022.3", "description": "Smart camera tools for passionate creators. \n\nCinemachine 3 is a newer and better version of Cinemachine, but upgrading an existing project from 2.X will likely require some effort. If you're considering upgrading an older project, please see our upgrade guide in the user manual.", "keywords": [