/// <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 than 0, no damping is done.</param> public override void MutateCameraState(ref CameraState curState, float deltaTime) { InitPrevFrameStateInfo(ref curState, deltaTime); // Update the heading if (FollowTarget != PreviousTarget) { PreviousTarget = FollowTarget; mTargetRigidBody = (PreviousTarget == null) ? null : PreviousTarget.GetComponent <Rigidbody>(); mLastTargetPosition = (PreviousTarget == null) ? Vector3.zero : PreviousTarget.position; mHeadingTracker = null; } LastHeading = HeadingUpdater(this, deltaTime, curState.ReferenceUp); float heading = LastHeading; if (IsValid) { // Calculate the heading if (m_BindingMode != BindingMode.SimpleFollowWithWorldUp) { heading += m_Heading.m_Bias; } Quaternion headingRot = Quaternion.AngleAxis(heading, Vector3.up); Vector3 rawOffset = EffectiveOffset; Vector3 offset = headingRot * rawOffset; // Track the target, with damping TrackTarget(deltaTime, curState.ReferenceUp, offset, out Vector3 pos, out Quaternion orient); // Place the camera offset = orient * offset; curState.ReferenceUp = orient * Vector3.up; // Respect minimum target distance on XZ plane var targetPosition = FollowTargetPosition; pos += GetOffsetForMinimumTargetDistance( pos, offset, curState.RawOrientation * Vector3.forward, curState.ReferenceUp, targetPosition); curState.RawPosition = pos + offset; if (deltaTime >= 0 && VirtualCamera.PreviousStateIsValid) { var lookAt = targetPosition; if (LookAtTarget != null) { lookAt = LookAtTargetPosition; } var dir0 = mLastCameraPosition - lookAt; var dir1 = curState.RawPosition - lookAt; if (dir0.sqrMagnitude > 0.01f && dir1.sqrMagnitude > 0.01f) { curState.PositionDampingBypass = UnityVectorExtensions.SafeFromToRotation( dir0, dir1, curState.ReferenceUp).eulerAngles; } } mLastTargetPosition = targetPosition; mLastCameraPosition = curState.RawPosition; } }
/// <summary>Positions the virtual camera according to the transposer rules.</summary> /// <param name="deltaTime">Used for damping. If less than 0, no damping is done.</param> /// <param name="up">Current camera up</param> /// <param name="desiredCameraOffset">Where we want to put the camera relative to the follow target</param> /// <param name="outTargetPosition">Resulting camera position</param> /// <param name="outTargetOrient">Damped target orientation</param> protected void TrackTarget( float deltaTime, Vector3 up, Vector3 desiredCameraOffset, out Vector3 outTargetPosition, out Quaternion outTargetOrient) { var targetOrientation = GetReferenceOrientation(up); var dampedOrientation = targetOrientation; bool prevStateValid = deltaTime >= 0 && VirtualCamera.PreviousStateIsValid; if (prevStateValid) { if (m_AngularDampingMode == AngularDampingMode.Quaternion && m_BindingMode == BindingMode.LockToTarget) { float t = VirtualCamera.DetachedFollowTargetDamp(1, m_AngularDamping, deltaTime); dampedOrientation = Quaternion.Slerp( m_PreviousReferenceOrientation, targetOrientation, t); } else { var relative = (Quaternion.Inverse(m_PreviousReferenceOrientation) * targetOrientation).eulerAngles; for (int i = 0; i < 3; ++i) { if (Mathf.Abs(relative[i]) < 0.01f) // correct for precision drift { relative[i] = 0; } else if (relative[i] > 180) { relative[i] -= 360; } } relative = VirtualCamera.DetachedFollowTargetDamp(relative, AngularDamping, deltaTime); dampedOrientation = m_PreviousReferenceOrientation * Quaternion.Euler(relative); } } m_PreviousReferenceOrientation = dampedOrientation; var targetPosition = FollowTargetPosition; var currentPosition = m_PreviousTargetPosition; var previousOffset = prevStateValid ? m_PreviousOffset : desiredCameraOffset; var offsetDelta = desiredCameraOffset - previousOffset; if (offsetDelta.sqrMagnitude > 0.01f) { var q = UnityVectorExtensions.SafeFromToRotation( m_PreviousOffset.ProjectOntoPlane(up), desiredCameraOffset.ProjectOntoPlane(up), up); currentPosition = targetPosition + q * (m_PreviousTargetPosition - targetPosition); } m_PreviousOffset = desiredCameraOffset; // Adjust for damping, which is done in camera-offset-local coords var positionDelta = targetPosition - currentPosition; if (prevStateValid) { Quaternion dampingSpace; if (desiredCameraOffset.AlmostZero()) { dampingSpace = VcamState.RawOrientation; } else { dampingSpace = Quaternion.LookRotation(dampedOrientation * desiredCameraOffset, up); } var localDelta = Quaternion.Inverse(dampingSpace) * positionDelta; localDelta = VirtualCamera.DetachedFollowTargetDamp(localDelta, Damping, deltaTime); positionDelta = dampingSpace * localDelta; } currentPosition += positionDelta; outTargetPosition = m_PreviousTargetPosition = currentPosition; outTargetOrient = dampedOrientation; }