using UnityEngine; using Unity.Cinemachine.TargetTracking; namespace Unity.Cinemachine { /// /// This is a CinemachineComponent in the Body section of the component pipeline. /// Its job is to position the camera in a fixed relationship to the vcam's Tracking /// Target object, with offsets and damping. /// /// This component will only change the camera's position in space. It will not /// re-orient or otherwise aim the camera. To to that, you need to instruct /// the camera in the Aim section of its pipeline. /// [AddComponentMenu("Cinemachine/Procedural/Position Control/Cinemachine Follow")] [SaveDuringPlay] [DisallowMultipleComponent] [CameraPipeline(CinemachineCore.Stage.Body)] [RequiredTarget(RequiredTargetAttribute.RequiredTargets.Tracking)] [HelpURL(Documentation.BaseURL + "manual/CinemachineFollow.html")] public class CinemachineFollow : CinemachineComponentBase { /// The distance which the camera will attempt to maintain from the tracking target [Tooltip("The distance vector that the camera will attempt to maintain from the tracking target")] public Vector3 FollowOffset = Vector3.back * 10f; /// Settings to control damping for target tracking. public TrackerSettings TrackerSettings = TrackerSettings.Default; Tracker m_TargetTracker; /// Derived classes should call this from their OnValidate() implementation void OnValidate() { FollowOffset = EffectiveOffset; TrackerSettings.Validate(); } private void Reset() { FollowOffset = Vector3.back * 10f; TrackerSettings = TrackerSettings.Default; } /// Get the target offset, with sanitization internal Vector3 EffectiveOffset { get { Vector3 offset = FollowOffset; if (TrackerSettings.BindingMode == BindingMode.LazyFollow) { offset.x = 0; offset.z = -Mathf.Abs(offset.z); } return offset; } } /// True if component is enabled and has a valid Follow target public override bool IsValid => enabled && FollowTarget != null; /// Get the Cinemachine Pipeline stage that this component implements. /// Always returns the Body stage public override CinemachineCore.Stage Stage { get { return CinemachineCore.Stage.Body; } } /// /// Report maximum damping time needed for this component. /// /// Highest damping setting in this component public override float GetMaxDampTime() => TrackerSettings.GetMaxDampTime(); /// Positions the virtual camera according to the transposer rules. /// The current camera state /// Used for damping. If less than 0, no damping is done. public override void MutateCameraState(ref CameraState curState, float deltaTime) { m_TargetTracker.InitStateInfo( this, deltaTime, TrackerSettings.BindingMode, curState.ReferenceUp); if (IsValid) { Vector3 offset = EffectiveOffset; m_TargetTracker.TrackTarget( this, deltaTime, curState.ReferenceUp, offset, TrackerSettings, out Vector3 pos, out Quaternion orient); offset = orient * offset; curState.ReferenceUp = orient * Vector3.up; // Respect minimum target distance on XZ plane var targetPosition = FollowTargetPosition; pos += m_TargetTracker.GetOffsetForMinimumTargetDistance( this, pos, offset, curState.RawOrientation * Vector3.forward, curState.ReferenceUp, targetPosition); curState.RawPosition = pos + offset; } } /// This is called to notify the user that a target got warped, /// so that we can update its internal state to make the camera /// also warp seamlessly. /// The object that was warped /// The amount the target's position changed public override void OnTargetObjectWarped(Transform target, Vector3 positionDelta) { base.OnTargetObjectWarped(target, positionDelta); if (target == FollowTarget) m_TargetTracker.OnTargetObjectWarped(positionDelta); } /// /// Force the virtual camera to assume a given position and orientation /// /// World-space position to take /// World-space orientation to take public override void ForceCameraPosition(Vector3 pos, Quaternion rot) { base.ForceCameraPosition(pos, rot); m_TargetTracker.ForceCameraPosition(this, TrackerSettings.BindingMode, pos, rot, EffectiveOffset); } internal Quaternion GetReferenceOrientation(Vector3 up) { return m_TargetTracker.GetReferenceOrientation(this, TrackerSettings.BindingMode, up); } /// Internal API for the Inspector Editor, so it can draw a marker at the target /// Current effective world up /// The position of the Follow target internal Vector3 GetDesiredCameraPosition(Vector3 worldUp) { if (!IsValid) return Vector3.zero; return FollowTargetPosition + m_TargetTracker.GetReferenceOrientation( this, TrackerSettings.BindingMode, worldUp) * EffectiveOffset; } } }