/// <summary>Applies the composer rules and orients the camera accordingly</summary> /// <param name="curState">The current camera state</param> /// <param name="deltaTime">Used for calculating damping. If less than /// zero, then target will snap to the center of the dead zone.</param> public override void MutateCameraState(ref CameraState curState, float deltaTime) { // Can't do anything without a group to look at CinemachineTargetGroup group = TargetGroup; if (group == null) { base.MutateCameraState(ref curState, deltaTime); return; } if (!IsValid || !curState.HasLookAt) { m_prevTargetHeight = 0; return; } curState.ReferenceLookAt = GetLookAtPointAndSetTrackedPoint(group.transform.position); Vector3 currentOffset = TrackedPoint - curState.RawPosition; float currentDistance = currentOffset.magnitude; if (currentDistance < Epsilon) { return; // navel-gazing, get outa here } //UnityEngine.Profiling.Profiler.BeginSample("CinemachineGroupComposer.MutateCameraState"); // Get the camera axis Vector3 fwd = currentOffset.AlmostZero() ? Vector3.forward : currentOffset.normalized; // Get the bounding box from that POV in view space, and find its width Bounds bounds = group.BoundingBox; LastBoundsMatrix = Matrix4x4.TRS( bounds.center - (fwd * bounds.extents.magnitude), Quaternion.LookRotation(fwd, curState.ReferenceUp), Vector3.one); LastBounds = group.GetViewSpaceBoundingBox(LastBoundsMatrix); float targetHeight = GetTargetHeight(LastBounds); Vector3 targetPos = LastBoundsMatrix.MultiplyPoint3x4(LastBounds.center); // Apply damping if (deltaTime >= 0) { float delta = targetHeight - m_prevTargetHeight; delta = Damper.Damp(delta, m_FrameDamping, deltaTime); targetHeight = m_prevTargetHeight + delta; } m_prevTargetHeight = targetHeight; // Move the camera if (!curState.Lens.Orthographic && m_AdjustmentMode != AdjustmentMode.ZoomOnly) { // What distance would be needed to get the target height, at the current FOV float currentFOV = curState.Lens.FieldOfView; float targetDistance = targetHeight / (2f * Mathf.Tan(currentFOV * Mathf.Deg2Rad / 2f)); // target the near surface of the bounding box float cameraDistance = targetDistance + LastBounds.extents.z; // Clamp to respect min/max distance settings cameraDistance = Mathf.Clamp( cameraDistance, currentDistance - m_MaxDollyIn, currentDistance + m_MaxDollyOut); cameraDistance = Mathf.Clamp(cameraDistance, m_MinimumDistance, m_MaximumDistance); // Apply curState.PositionCorrection += targetPos - fwd * cameraDistance - curState.RawPosition; } // Apply zoom if (curState.Lens.Orthographic || m_AdjustmentMode != AdjustmentMode.DollyOnly) { float nearBoundsDistance = (TrackedPoint - curState.CorrectedPosition).magnitude - LastBounds.extents.z; float currentFOV = 179; if (nearBoundsDistance > Epsilon) { currentFOV = 2f * Mathf.Atan(targetHeight / (2 * nearBoundsDistance)) * Mathf.Rad2Deg; } LensSettings lens = curState.Lens; lens.FieldOfView = Mathf.Clamp(currentFOV, m_MinimumFOV, m_MaximumFOV); lens.OrthographicSize = Mathf.Clamp(targetHeight / 2, m_MinimumOrthoSize, m_MaximumOrthoSize); curState.Lens = lens; } // Now compose normally base.MutateCameraState(ref curState, deltaTime); //UnityEngine.Profiling.Profiler.EndSample(); }
/// <summary>Applies the composer rules and orients the camera accordingly</summary> /// <param name="curState">The current camera state</param> /// <param name="deltaTime">Used for calculating damping. If less than /// zero, then target will snap to the center of the dead zone.</param> public override void MutateCameraState(ref CameraState curState, float deltaTime) { // Can't do anything without a group to look at CinemachineTargetGroup group = LookAtTargetGroup; if (group == null) { base.MutateCameraState(ref curState, deltaTime); return; } if (!IsValid || !curState.HasLookAt) { m_prevTargetHeight = 0; m_prevCameraOffset = Vector3.zero; return; } bool canMoveCamera = !curState.Lens.Orthographic && m_AdjustmentMode != AdjustmentMode.ZoomOnly; // Get the bounding box from camera's POV in view space Vector3 observerPosition = curState.RawPosition; BoundingSphere s = group.Sphere; Vector3 groupCenter = s.position; Vector3 currentOffset = groupCenter - observerPosition; float currentDistance = currentOffset.magnitude; if (currentDistance < Epsilon) { return; // navel-gazing, get outa here } Vector3 fwd = currentOffset / currentDistance; LastBoundsMatrix = Matrix4x4.TRS(observerPosition, Quaternion.LookRotation(fwd, curState.ReferenceUp), Vector3.one); Bounds b; if (curState.Lens.Orthographic) { b = group.GetViewSpaceBoundingBox(LastBoundsMatrix); Vector3 sizeDelta = new Vector3(b.center.x, b.center.y, 0); b.size += sizeDelta.Abs() * 2; b.center = new Vector3(0, 0, b.center.z); LastBounds = b; } else { if (canMoveCamera) { // Get an upper bound on the distance b = group.GetViewSpaceBoundingBox(LastBoundsMatrix); groupCenter = LastBoundsMatrix.MultiplyPoint3x4(b.center); // Now try to get closer float distance = GetTargetHeight(b) / (2f * Mathf.Tan(curState.Lens.FieldOfView * Mathf.Deg2Rad / 2f)); Vector3 nearCenter = b.center; nearCenter.z -= b.extents.z; nearCenter = LastBoundsMatrix.MultiplyPoint3x4(nearCenter); Vector3 newFwd = (groupCenter - nearCenter).normalized; if (!newFwd.AlmostZero()) { fwd = newFwd; } observerPosition = nearCenter - (fwd * distance); LastBoundsMatrix = Matrix4x4.TRS(observerPosition, Quaternion.LookRotation(fwd, curState.ReferenceUp), Vector3.one); } b = GetScreenSpaceGroupBoundingBox(group, LastBoundsMatrix, out fwd); LastBoundsMatrix = Matrix4x4.TRS(observerPosition, Quaternion.LookRotation(fwd, curState.ReferenceUp), Vector3.one); LastBounds = b; groupCenter = LastBoundsMatrix.MultiplyPoint3x4(b.center); currentOffset = groupCenter - curState.RawPosition; currentDistance = currentOffset.magnitude; } // Adjust bounds for framing size Vector3 extents = b.extents / m_GroupFramingSize; extents.z = Mathf.Min(b.extents.z, extents.z); b.extents = extents; // Apply damping float targetHeight = GetTargetHeight(b); if (deltaTime >= 0) { float delta = targetHeight - m_prevTargetHeight; delta = Damper.Damp(delta, m_FrameDamping, deltaTime); targetHeight = m_prevTargetHeight + delta; } m_prevTargetHeight = targetHeight; // Move the camera if (canMoveCamera) { // What distance would be needed to get the target height, at the current FOV float depth = b.extents.z; float d = (groupCenter - observerPosition).magnitude; float nearTargetHeight = targetHeight * (d - depth) / d; float targetDistance = nearTargetHeight / (2f * Mathf.Tan(curState.Lens.FieldOfView * Mathf.Deg2Rad / 2f)); // Clamp to respect min/max distance settings to the near surface of the bounds float cameraDistance = targetDistance; cameraDistance = Mathf.Clamp(cameraDistance, currentDistance - m_MaxDollyIn, currentDistance + m_MaxDollyOut); cameraDistance -= depth; cameraDistance = Mathf.Clamp(cameraDistance, m_MinimumDistance, m_MaximumDistance); cameraDistance += depth; // Apply Vector3 newCamOffset = (groupCenter - (fwd * (cameraDistance + depth))) - curState.RawPosition; if (deltaTime >= 0) { Vector3 delta = newCamOffset - m_prevCameraOffset; delta = Damper.Damp(delta, m_FrameDamping, deltaTime); newCamOffset = m_prevCameraOffset + delta; } m_prevCameraOffset = newCamOffset; curState.PositionCorrection += newCamOffset; } // Apply zoom if (curState.Lens.Orthographic || m_AdjustmentMode != AdjustmentMode.DollyOnly) { float nearBoundsDistance = (groupCenter - curState.CorrectedPosition).magnitude; float currentFOV = 179; if (nearBoundsDistance > Epsilon) { currentFOV = 2f * Mathf.Atan(targetHeight / (2 * nearBoundsDistance)) * Mathf.Rad2Deg; } LensSettings lens = curState.Lens; lens.FieldOfView = Mathf.Clamp(currentFOV, m_MinimumFOV, m_MaximumFOV); lens.OrthographicSize = Mathf.Clamp(targetHeight / 2, m_MinimumOrthoSize, m_MaximumOrthoSize); curState.Lens = lens; } // Now compose normally curState.ReferenceLookAt = GetLookAtPointAndSetTrackedPoint(groupCenter); base.MutateCameraState(ref curState, deltaTime); }