#if !CINEMACHINE_NO_CM2_SUPPORT using UnityEngine; using System; using UnityEngine.Serialization; namespace Unity.Cinemachine { /// <summary> /// This is a deprecated component. Use CinemachineSplineDolly instead. /// </summary> [Obsolete("CinemachineTrackedDolly has been deprecated. Use CinemachineSplineDolly instead.")] [AddComponentMenu("")] // Don't display in add component menu [SaveDuringPlay] [CameraPipeline(CinemachineCore.Stage.Body)] public class CinemachineTrackedDolly : CinemachineComponentBase { /// <summary>The path to which the camera will be constrained. This must be non-null.</summary> [Tooltip("The path to which the camera will be constrained. This must be non-null.")] public CinemachinePathBase m_Path; /// <summary>The position along the path at which the camera will be placed. /// This can be animated directly, or set automatically by the Auto-Dolly feature /// to get as close as possible to the Follow target.</summary> [Tooltip("The position along the path at which the camera will be placed. " + "This can be animated directly, or set automatically by the Auto-Dolly feature to " + "get as close as possible to the Follow target. The value is interpreted " + "according to the Position Units setting.")] public float m_PathPosition; /// <summary>How to interpret the Path Position</summary> [Tooltip("How to interpret Path Position. If set to Path Units, values are as follows: " + "0 represents the first waypoint on the path, 1 is the second, and so on. Values " + "in-between are points on the path in between the waypoints. If set to Distance, " + "then Path Position represents distance along the path.")] public CinemachinePathBase.PositionUnits m_PositionUnits = CinemachinePathBase.PositionUnits.PathUnits; /// <summary>Where to put the camera realtive to the path postion. X is perpendicular /// to the path, Y is up, and Z is parallel to the path.</summary> [Tooltip("Where to put the camera relative to the path position. X is perpendicular " + "to the path, Y is up, and Z is parallel to the path. This allows the camera to " + "be offset from the path itself (as if on a tripod, for example).")] public Vector3 m_PathOffset = Vector3.zero; /// <summary>How aggressively the camera tries to maintain the offset perpendicular to the path. /// Small numbers are more responsive, rapidly translating the camera to keep the target's /// x-axis offset. Larger numbers give a more heavy slowly responding camera. /// Using different settings per axis can yield a wide range of camera behaviors</summary> [Range(0f, 20f)] [Tooltip("How aggressively the camera tries to maintain its position in a direction " + "perpendicular to the path. Small numbers are more responsive, rapidly translating " + "the camera to keep the target's x-axis offset. Larger numbers give a more heavy " + "slowly responding camera. Using different settings per axis can yield a wide range " + "of camera behaviors.")] public float m_XDamping = 0f; /// <summary>How aggressively the camera tries to maintain the offset in the path-local up direction. /// Small numbers are more responsive, rapidly translating the camera to keep the target's /// y-axis offset. Larger numbers give a more heavy slowly responding camera. /// Using different settings per axis can yield a wide range of camera behaviors</summary> [Range(0f, 20f)] [Tooltip("How aggressively the camera tries to maintain its position in the path-local up direction. " + "Small numbers are more responsive, rapidly translating the camera to keep the target's " + "y-axis offset. Larger numbers give a more heavy slowly responding camera. Using different " + "settings per axis can yield a wide range of camera behaviors.")] public float m_YDamping = 0f; /// <summary>How aggressively the camera tries to maintain the offset parallel to the path. /// Small numbers are more responsive, rapidly translating the camera to keep the /// target's z-axis offset. Larger numbers give a more heavy slowly responding camera. /// Using different settings per axis can yield a wide range of camera behaviors</summary> [Range(0f, 20f)] [Tooltip("How aggressively the camera tries to maintain its position in a direction parallel to the path. " + "Small numbers are more responsive, rapidly translating the camera to keep the target's z-axis offset. " + "Larger numbers give a more heavy slowly responding camera. Using different settings per axis " + "can yield a wide range of camera behaviors.")] public float m_ZDamping = 1f; /// <summary>Different ways to set the camera's up vector</summary> public enum CameraUpMode { /// <summary>Leave the camera's up vector alone. It will be set according to the Brain's WorldUp.</summary> Default, /// <summary>Take the up vector from the path's up vector at the current point</summary> Path, /// <summary>Take the up vector from the path's up vector at the current point, but with the roll zeroed out</summary> PathNoRoll, /// <summary>Take the up vector from the Follow target's up vector</summary> FollowTarget, /// <summary>Take the up vector from the Follow target's up vector, but with the roll zeroed out</summary> FollowTargetNoRoll, }; /// <summary>How to set the virtual camera's Up vector. This will affect the screen composition.</summary> [Tooltip("How to set the virtual camera's Up vector. This will affect the screen composition, because " + "the camera Aim behaviours will always try to respect the Up direction.")] public CameraUpMode m_CameraUp = CameraUpMode.Default; /// <summary>"How aggressively the camera tries to track the target rotation's X angle. /// Small numbers are more responsive. Larger numbers give a more heavy slowly responding camera.</summary> [Range(0f, 20f)] [Tooltip("How aggressively the camera tries to track the target rotation's X angle. Small numbers are " + "more responsive. Larger numbers give a more heavy slowly responding camera.")] public float m_PitchDamping = 0; /// <summary>How aggressively the camera tries to track the target rotation's Y angle. /// Small numbers are more responsive. Larger numbers give a more heavy slowly responding camera.</summary> [Range(0f, 20f)] [Tooltip("How aggressively the camera tries to track the target rotation's Y angle. Small numbers are " + "more responsive. Larger numbers give a more heavy slowly responding camera.")] public float m_YawDamping = 0; /// <summary>How aggressively the camera tries to track the target rotation's Z angle. /// Small numbers are more responsive. Larger numbers give a more heavy slowly responding camera.</summary> [Range(0f, 20f)] [Tooltip("How aggressively the camera tries to track the target rotation's Z angle. Small numbers " + "are more responsive. Larger numbers give a more heavy slowly responding camera.")] public float m_RollDamping = 0f; /// <summary>Controls how automatic dollying occurs</summary> [Serializable] public struct AutoDolly { /// <summary>If checked, will enable automatic dolly, which chooses a path position /// that is as close as possible to the Follow target.</summary> [Tooltip("If checked, will enable automatic dolly, which chooses a path position that is as " + "close as possible to the Follow target. Note: this can have significant performance impact")] public bool m_Enabled; /// <summary>Offset, in current position units, from the closest point on the path to the follow target.</summary> [Tooltip("Offset, in current position units, from the closest point on the path to the follow target")] public float m_PositionOffset; /// <summary>Search up to this many waypoints on either side of the current position. Use 0 for Entire path</summary> [Tooltip("Search up to this many waypoints on either side of the current position. Use 0 for Entire path.")] public int m_SearchRadius; /// <summary>We search between waypoints by dividing the segment into this many straight pieces. /// The higher the number, the more accurate the result, but performance is /// proportionally slower for higher numbers</summary> [FormerlySerializedAs("m_StepsPerSegment")] [Tooltip("We search between waypoints by dividing the segment into this many straight pieces. " + "he higher the number, the more accurate the result, but performance is proportionally " + "slower for higher numbers")] public int m_SearchResolution; /// <summary>Constructor with specific field values</summary> /// <param name="enabled">Whether to enable automatic dolly</param> /// <param name="positionOffset">Offset, in current position units, from the closest point on the path to the follow target</param> /// <param name="searchRadius">Search up to this many waypoints on either side of the current position</param> /// <param name="stepsPerSegment">We search between waypoints by dividing the segment into this many straight pieces</param> public AutoDolly(bool enabled, float positionOffset, int searchRadius, int stepsPerSegment) { m_Enabled = enabled; m_PositionOffset = positionOffset; m_SearchRadius = searchRadius; m_SearchResolution = stepsPerSegment; } } /// <summary>Controls how automatic dollying occurs</summary> [Tooltip("Controls how automatic dollying occurs. A Follow target is necessary to use this feature.")] public AutoDolly m_AutoDolly = new AutoDolly(false, 0, 2, 5); /// <summary>True if component is enabled and has a path</summary> public override bool IsValid { get { return enabled && m_Path != null; } } /// <summary>Get the Cinemachine Pipeline stage that this component implements. /// Always returns the Body stage</summary> public override CinemachineCore.Stage Stage { get { return CinemachineCore.Stage.Body; } } /// <summary> /// Report maximum damping time needed for this component. /// </summary> /// <returns>Highest damping setting in this component</returns> public override float GetMaxDampTime() { var d2 = AngularDamping; var a = Mathf.Max(m_XDamping, Mathf.Max(m_YDamping, m_ZDamping)); var b = Mathf.Max(d2.x, Mathf.Max(d2.y, d2.z)); return Mathf.Max(a, b); } /// <summary>Positions the virtual camera according to the transposer rules.</summary> /// <param name="curState">The current camera state</param> /// <param name="deltaTime">Used for damping. If less that 0, no damping is done.</param> public override void MutateCameraState(ref CameraState curState, float deltaTime) { // Init previous frame state info if (deltaTime < 0 || !VirtualCamera.PreviousStateIsValid) { m_PreviousPathPosition = m_PathPosition; m_PreviousCameraPosition = curState.RawPosition; m_PreviousOrientation = curState.RawOrientation; } if (!IsValid) return; // Get the new ideal path base position if (m_AutoDolly.m_Enabled && FollowTarget != null) { float prevPos = m_Path.ToNativePathUnits(m_PreviousPathPosition, m_PositionUnits); // This works in path units m_PathPosition = m_Path.FindClosestPoint( FollowTargetPosition, Mathf.FloorToInt(prevPos), (deltaTime < 0 || m_AutoDolly.m_SearchRadius <= 0) ? -1 : m_AutoDolly.m_SearchRadius, m_AutoDolly.m_SearchResolution); m_PathPosition = m_Path.FromPathNativeUnits(m_PathPosition, m_PositionUnits); // Apply the path position offset m_PathPosition += m_AutoDolly.m_PositionOffset; } float newPathPosition = m_PathPosition; if (deltaTime >= 0 && VirtualCamera.PreviousStateIsValid) { // Normalize previous position to find the shortest path float maxUnit = m_Path.MaxUnit(m_PositionUnits); if (maxUnit > 0) { float prev = m_Path.StandardizeUnit(m_PreviousPathPosition, m_PositionUnits); float next = m_Path.StandardizeUnit(newPathPosition, m_PositionUnits); if (m_Path.Looped && Mathf.Abs(next - prev) > maxUnit / 2) { if (next > prev) prev += maxUnit; else prev -= maxUnit; } m_PreviousPathPosition = prev; newPathPosition = next; } // Apply damping along the path direction float offset = m_PreviousPathPosition - newPathPosition; offset = Damper.Damp(offset, m_ZDamping, deltaTime); newPathPosition = m_PreviousPathPosition - offset; } m_PreviousPathPosition = newPathPosition; Quaternion newPathOrientation = m_Path.EvaluateOrientationAtUnit(newPathPosition, m_PositionUnits); // Apply the offset to get the new camera position Vector3 newCameraPos = m_Path.EvaluatePositionAtUnit(newPathPosition, m_PositionUnits); Vector3 offsetX = newPathOrientation * Vector3.right; Vector3 offsetY = newPathOrientation * Vector3.up; Vector3 offsetZ = newPathOrientation * Vector3.forward; newCameraPos += m_PathOffset.x * offsetX; newCameraPos += m_PathOffset.y * offsetY; newCameraPos += m_PathOffset.z * offsetZ; // Apply damping to the remaining directions if (deltaTime >= 0 && VirtualCamera.PreviousStateIsValid) { Vector3 currentCameraPos = m_PreviousCameraPosition; Vector3 delta = (currentCameraPos - newCameraPos); Vector3 delta1 = Vector3.Dot(delta, offsetY) * offsetY; Vector3 delta0 = delta - delta1; delta0 = Damper.Damp(delta0, m_XDamping, deltaTime); delta1 = Damper.Damp(delta1, m_YDamping, deltaTime); newCameraPos = currentCameraPos - (delta0 + delta1); } curState.RawPosition = m_PreviousCameraPosition = newCameraPos; // Set the orientation and up Quaternion newOrientation = GetCameraOrientationAtPathPoint(newPathOrientation, curState.ReferenceUp); if (deltaTime >= 0 && VirtualCamera.PreviousStateIsValid) { Vector3 relative = (Quaternion.Inverse(m_PreviousOrientation) * newOrientation).eulerAngles; for (int i = 0; i < 3; ++i) if (relative[i] > 180) relative[i] -= 360; relative = Damper.Damp(relative, AngularDamping, deltaTime); newOrientation = m_PreviousOrientation * Quaternion.Euler(relative); } m_PreviousOrientation = newOrientation; curState.RawOrientation = newOrientation; if (m_CameraUp != CameraUpMode.Default) curState.ReferenceUp = curState.RawOrientation * Vector3.up; } private Quaternion GetCameraOrientationAtPathPoint(Quaternion pathOrientation, Vector3 up) { switch (m_CameraUp) { default: case CameraUpMode.Default: break; case CameraUpMode.Path: return pathOrientation; case CameraUpMode.PathNoRoll: return Quaternion.LookRotation(pathOrientation * Vector3.forward, up); case CameraUpMode.FollowTarget: if (FollowTarget != null) return FollowTargetRotation; break; case CameraUpMode.FollowTargetNoRoll: if (FollowTarget != null) return Quaternion.LookRotation(FollowTargetRotation * Vector3.forward, up); break; } return Quaternion.LookRotation(VirtualCamera.transform.rotation * Vector3.forward, up); } private Vector3 AngularDamping { get { switch (m_CameraUp) { case CameraUpMode.PathNoRoll: case CameraUpMode.FollowTargetNoRoll: return new Vector3(m_PitchDamping, m_YawDamping, 0); case CameraUpMode.Default: return Vector3.zero; default: return new Vector3(m_PitchDamping, m_YawDamping, m_RollDamping); } } } private float m_PreviousPathPosition = 0; Quaternion m_PreviousOrientation = Quaternion.identity; private Vector3 m_PreviousCameraPosition = Vector3.zero; // Helper to upgrade to CM3 internal void UpgradeToCm3(CinemachineSplineDolly c) { c.Damping.Enabled = true; c.Damping.Position = new Vector3(m_XDamping, m_YDamping, m_ZDamping); c.Damping.Angular = Mathf.Max(m_YawDamping, Mathf.Max(m_RollDamping, m_PitchDamping)); c.CameraRotation = (CinemachineSplineDolly.RotationMode)m_CameraUp; // enum values match c.AutomaticDolly.Enabled = m_AutoDolly.m_Enabled; if (m_AutoDolly.m_Enabled) { c.AutomaticDolly.Method = new SplineAutoDolly.NearestPointToTarget { PositionOffset = m_AutoDolly.m_PositionOffset, SearchResolution = m_AutoDolly.m_SearchResolution, SearchIteration = 2 }; } // set splineDolly spline reference if (m_Path != null) c.Spline = m_Path.GetComponent<UnityEngine.Splines.SplineContainer>(); c.CameraPosition = m_PathPosition; switch (m_PositionUnits) { case CinemachinePathBase.PositionUnits.PathUnits: c.PositionUnits = UnityEngine.Splines.PathIndexUnit.Knot; break; case CinemachinePathBase.PositionUnits.Distance: c.PositionUnits = UnityEngine.Splines.PathIndexUnit.Distance; break; case CinemachinePathBase.PositionUnits.Normalized: c.PositionUnits = UnityEngine.Splines.PathIndexUnit.Normalized; break; } c.SplineOffset = m_PathOffset; } } } #endif