#if !CINEMACHINE_NO_CM2_SUPPORT using UnityEngine; namespace Unity.Cinemachine { /// /// This is a deprecated component. Use CinemachineRotationComposer and CinemachineGroupFraming instead. /// [System.Obsolete("CinemachineGroupTransposer has been deprecated. Use CinemachineRotationComposer and CinemachineGroupFraming instead")] [AddComponentMenu("")] // Don't display in add component menu [SaveDuringPlay] [CameraPipeline(CinemachineCore.Stage.Aim)] public class CinemachineGroupComposer : CinemachineComposer { /// How much of the screen to fill with the bounding box of the targets. [Tooltip("The bounding box of the targets should occupy this amount of the screen space. " + "1 means fill the whole screen. 0.5 means fill half the screen, etc.")] public float m_GroupFramingSize = 0.8f; /// What screen dimensions to consider when framing public enum FramingMode { /// Consider only the horizontal dimension. Vertical framing is ignored. Horizontal, /// Consider only the vertical dimension. Horizontal framing is ignored. Vertical, /// The larger of the horizontal and vertical dimensions will dominate, to get the best fit. HorizontalAndVertical }; /// What screen dimensions to consider when framing [Tooltip("What screen dimensions to consider when framing. Can be Horizontal, Vertical, or both")] public FramingMode m_FramingMode = FramingMode.HorizontalAndVertical; /// How aggressively the camera tries to frame the group. /// Small numbers are more responsive [Range(0, 20)] [Tooltip("How aggressively the camera tries to frame the group. Small numbers are more responsive, " + "rapidly adjusting the camera to keep the group in the frame. Larger numbers give a heavier " + "more slowly responding camera.")] public float m_FrameDamping = 2f; /// How to adjust the camera to get the desired framing public enum AdjustmentMode { /// Do not move the camera, only adjust the FOV. ZoomOnly, /// Just move the camera, don't change the FOV. DollyOnly, /// Move the camera as much as permitted by the ranges, then /// adjust the FOV if necessary to make the shot. DollyThenZoom }; /// How to adjust the camera to get the desired framing [Tooltip("How to adjust the camera to get the desired framing. You can zoom, dolly in/out, or do both.")] public AdjustmentMode m_AdjustmentMode = AdjustmentMode.ZoomOnly; /// How much closer to the target can the camera go? [Tooltip("The maximum distance toward the target that this behaviour is allowed to move the camera.")] public float m_MaxDollyIn = 5000f; /// How much farther from the target can the camera go? [Tooltip("The maximum distance away the target that this behaviour is allowed to move the camera.")] public float m_MaxDollyOut = 5000f; /// Set this to limit how close to the target the camera can get [Tooltip("Set this to limit how close to the target the camera can get.")] public float m_MinimumDistance = 1; /// Set this to limit how far from the taregt the camera can get [Tooltip("Set this to limit how far from the target the camera can get.")] public float m_MaximumDistance = 5000f; /// If adjusting FOV, will not set the FOV lower than this [Range(1, 179)] [Tooltip("If adjusting FOV, will not set the FOV lower than this.")] public float m_MinimumFOV = 3; /// If adjusting FOV, will not set the FOV higher than this [Range(1, 179)] [Tooltip("If adjusting FOV, will not set the FOV higher than this.")] public float m_MaximumFOV = 60; /// If adjusting Orthographic Size, will not set it lower than this [Tooltip("If adjusting Orthographic Size, will not set it lower than this.")] public float m_MinimumOrthoSize = 1; /// If adjusting Orthographic Size, will not set it higher than this [Tooltip("If adjusting Orthographic Size, will not set it higher than this.")] public float m_MaximumOrthoSize = 5000; private void OnValidate() { m_GroupFramingSize = Mathf.Max(0.001f, m_GroupFramingSize); m_MaxDollyIn = Mathf.Max(0, m_MaxDollyIn); m_MaxDollyOut = Mathf.Max(0, m_MaxDollyOut); m_MinimumDistance = Mathf.Max(0, m_MinimumDistance); m_MaximumDistance = Mathf.Max(m_MinimumDistance, m_MaximumDistance); m_MinimumFOV = Mathf.Max(1, m_MinimumFOV); m_MaximumFOV = Mathf.Clamp(m_MaximumFOV, m_MinimumFOV, 179); m_MinimumOrthoSize = Mathf.Max(0.01f, m_MinimumOrthoSize); m_MaximumOrthoSize = Mathf.Max(m_MinimumOrthoSize, m_MaximumOrthoSize); } private void Reset() { m_GroupFramingSize = 0.8f; m_FramingMode = FramingMode.HorizontalAndVertical; m_FrameDamping = 2f; m_AdjustmentMode = AdjustmentMode.ZoomOnly; m_MaxDollyIn = 5000f; m_MaxDollyOut = 5000f; m_MinimumDistance = 1; m_MaximumDistance = 5000f; m_MinimumFOV = 3; m_MaximumFOV = 60; m_MinimumOrthoSize = 1; m_MaximumOrthoSize = 5000; } // State for damping float m_prevFramingDistance; float m_prevFOV; /// For editor visulaization of the calculated bounding box of the group public Bounds LastBounds { get; private set; } /// For editor visualization of the calculated bounding box of the group public Matrix4x4 LastBoundsMatrix { get; private set; } /// /// Report maximum damping time needed for this component. /// /// Highest damping setting in this component public override float GetMaxDampTime() { return Mathf.Max(base.GetMaxDampTime(), m_FrameDamping); } /// Applies the composer rules and orients the camera accordingly /// The current camera state /// Used for calculating damping. If less than /// zero, then target will snap to the center of the dead zone. public override void MutateCameraState(ref CameraState curState, float deltaTime) { // Can't do anything without a group to look at ICinemachineTargetGroup group = LookAtTargetAsGroup; if (group == null || !group.IsValid) { base.MutateCameraState(ref curState, deltaTime); return; } if (!IsValid || !curState.HasLookAt()) { m_prevFramingDistance = 0; m_prevFOV = 0; return; } bool isOrthographic = curState.Lens.Orthographic; bool canMoveCamera = !isOrthographic && m_AdjustmentMode != AdjustmentMode.ZoomOnly; // Get the bounding box from camera's POV in view space Vector3 up = curState.ReferenceUp; var cameraPos = curState.RawPosition; BoundingSphere s = group.Sphere; Vector3 groupCenter = s.position; Vector3 fwd = groupCenter - cameraPos; float d = fwd.magnitude; if (d < Epsilon) return; // navel-gazing, get outa here // Approximate looking at the group center fwd /= d; LastBoundsMatrix = Matrix4x4.TRS( cameraPos, Quaternion.LookRotation(fwd, up), Vector3.one); // Correction for the actual center Bounds b; if (isOrthographic) { b = group.GetViewSpaceBoundingBox(LastBoundsMatrix, true); groupCenter = LastBoundsMatrix.MultiplyPoint3x4(b.center); fwd = (groupCenter - cameraPos).normalized; LastBoundsMatrix = Matrix4x4.TRS(cameraPos, Quaternion.LookRotation(fwd, up), Vector3.one); b = group.GetViewSpaceBoundingBox(LastBoundsMatrix, true); LastBounds = b; } else { b = GetScreenSpaceGroupBoundingBox(group, LastBoundsMatrix, out fwd); LastBoundsMatrix = Matrix4x4.TRS(cameraPos, Quaternion.LookRotation(fwd, up), Vector3.one); LastBounds = b; groupCenter = cameraPos + fwd * b.center.z; } // Adjust bounds for framing size, and get target height float boundsDepth = b.extents.z; float targetHeight = GetTargetHeight(b.size / m_GroupFramingSize); if (isOrthographic) { targetHeight = Mathf.Clamp(targetHeight / 2, m_MinimumOrthoSize, m_MaximumOrthoSize); // ApplyDamping if (deltaTime >= 0 && VirtualCamera.PreviousStateIsValid) targetHeight = m_prevFOV + VirtualCamera.DetachedLookAtTargetDamp( targetHeight - m_prevFOV, m_FrameDamping, deltaTime); m_prevFOV = targetHeight; LensSettings lens = curState.Lens; lens.OrthographicSize = Mathf.Clamp(targetHeight, m_MinimumOrthoSize, m_MaximumOrthoSize); curState.Lens = lens; } else { // Adjust height for perspective - we want the height at the near surface float z = b.center.z; if (z > boundsDepth) targetHeight = Mathf.Lerp(0, targetHeight, (z - boundsDepth) / z); // Move the camera if (canMoveCamera) { // What distance from near edge would be needed to get the adjusted // target height, at the current FOV float targetDistance = boundsDepth + targetHeight / (2f * Mathf.Tan(curState.Lens.FieldOfView * Mathf.Deg2Rad / 2f)); // Clamp to respect min/max distance settings to the near surface of the bounds targetDistance = Mathf.Clamp( targetDistance, boundsDepth + m_MinimumDistance, boundsDepth + m_MaximumDistance); // Clamp to respect min/max camera movement float targetDelta = targetDistance - Vector3.Distance(curState.RawPosition, groupCenter); targetDelta = Mathf.Clamp(targetDelta, -m_MaxDollyIn, m_MaxDollyOut); // ApplyDamping if (deltaTime >= 0 && VirtualCamera.PreviousStateIsValid) { float delta = targetDelta - m_prevFramingDistance; delta = VirtualCamera.DetachedLookAtTargetDamp(delta, m_FrameDamping, deltaTime); targetDelta = m_prevFramingDistance + delta; } m_prevFramingDistance = targetDelta; curState.PositionCorrection -= fwd * targetDelta; cameraPos -= fwd * targetDelta; } // Apply zoom if (m_AdjustmentMode != AdjustmentMode.DollyOnly) { float nearBoundsDistance = (groupCenter - cameraPos).magnitude - boundsDepth; float targetFOV = 179; if (nearBoundsDistance > Epsilon) targetFOV = 2f * Mathf.Atan(targetHeight / (2 * nearBoundsDistance)) * Mathf.Rad2Deg; targetFOV = Mathf.Clamp(targetFOV, m_MinimumFOV, m_MaximumFOV); // ApplyDamping if (deltaTime >= 0 && m_prevFOV != 0 && VirtualCamera.PreviousStateIsValid) targetFOV = m_prevFOV + VirtualCamera.DetachedLookAtTargetDamp( targetFOV - m_prevFOV, m_FrameDamping, deltaTime); m_prevFOV = targetFOV; LensSettings lens = curState.Lens; lens.FieldOfView = targetFOV; curState.Lens = lens; } } // Now compose normally curState.ReferenceLookAt = GetLookAtPointAndSetTrackedPoint( groupCenter, curState.ReferenceUp, deltaTime); base.MutateCameraState(ref curState, deltaTime); } float GetTargetHeight(Vector2 boundsSize) { switch (m_FramingMode) { case FramingMode.Horizontal: return Mathf.Max(Epsilon, boundsSize.x ) / VcamState.Lens.Aspect; case FramingMode.Vertical: return Mathf.Max(Epsilon, boundsSize.y); default: case FramingMode.HorizontalAndVertical: return Mathf.Max( Mathf.Max(Epsilon, boundsSize.x) / VcamState.Lens.Aspect, Mathf.Max(Epsilon, boundsSize.y)); } } /// Point of view /// New forward direction to use when interpreting the return value /// Bounding box in a slightly rotated version of observer, as specified by newFwd static Bounds GetScreenSpaceGroupBoundingBox( ICinemachineTargetGroup group, Matrix4x4 observer, out Vector3 newFwd) { group.GetViewSpaceAngularBounds(observer, out var minAngles, out var maxAngles, out var zRange); var shift = (minAngles + maxAngles) / 2; newFwd = Quaternion.identity.ApplyCameraRotation(shift, Vector3.up) * Vector3.forward; newFwd = observer.MultiplyVector(newFwd); // For width and height (in camera space) of the bounding box, we use the values at the center of the box. // This is an arbitrary choice. The gizmo drawer will take this into account when displaying // the frustum bounds of the group var d = zRange.y + zRange.x; var angles = Vector2.Min(maxAngles - shift, new Vector2(89.5f, 89.5f)) * Mathf.Deg2Rad; return new Bounds( new Vector3(0, 0, d/2), new Vector3(Mathf.Tan(angles.y) * d, Mathf.Tan(angles.x) * d, zRange.y - zRange.x)); } // Helper to upgrade to CM3 internal void UpgradeToCm3(CinemachineGroupFraming c) { c.FramingMode = (CinemachineGroupFraming.FramingModes)m_FramingMode; // values are the same c.FramingSize = m_GroupFramingSize; c.Damping = m_FrameDamping; c.SizeAdjustment = (CinemachineGroupFraming.SizeAdjustmentModes)m_AdjustmentMode; // values are the same c.LateralAdjustment = CinemachineGroupFraming.LateralAdjustmentModes.ChangeRotation; c.DollyRange = new Vector2(-m_MaxDollyIn, m_MaxDollyOut); c.FovRange = new Vector2(m_MinimumFOV, m_MaximumFOV); c.OrthoSizeRange = new Vector2(m_MinimumOrthoSize, m_MaximumOrthoSize); } } } #endif