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;
}
}
}