intial commit

This commit is contained in:
Caleb Sandford deQuincey
2025-06-27 23:27:49 +01:00
commit ecdd3e2a9e
31269 changed files with 4937644 additions and 0 deletions

View File

@@ -0,0 +1,585 @@
#if CINEMACHINE_EXPERIMENTAL_VCAM
using UnityEngine;
using Cinemachine.Utility;
using System;
namespace Cinemachine
{
/// <summary>
///
/// NOTE: THIS CLASS IS EXPERIMENTAL, AND NOT FOR PUBLIC USE
///
/// Lighter-weight version of the CinemachineFreeLook, with extra radial axis.
///
/// A Cinemachine Camera geared towards a 3rd person camera experience.
/// The camera orbits around its subject with three separate camera rigs defining
/// rings around the target. Each rig has its own radius, height offset, composer,
/// and lens settings.
/// Depending on the camera's position along the spline connecting these three rigs,
/// these settings are interpolated to give the final camera position and state.
/// </summary>
[DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
[DisallowMultipleComponent]
[ExecuteAlways]
[AddComponentMenu("Cinemachine/CinemachineNewFreeLook")]
[SaveDuringPlay]
public class CinemachineNewFreeLook : CinemachineNewVirtualCamera
{
/// <summary>The Vertical axis. Value is 0..1. Chooses how to blend the child rigs</summary>
[Tooltip("The Vertical axis. Value is 0..1. 0.5 is the middle rig. Chooses how to blend the child rigs")]
[AxisStateProperty]
public AxisState m_VerticalAxis = new AxisState(0, 1, false, true, 2f, 0.2f, 0.1f, "Mouse Y", false);
[Tooltip("The Radial axis. Value is the base radius of the orbits")]
[AxisStateProperty]
public AxisState m_RadialAxis = new AxisState(1, 1, false, false, 100, 0f, 0f, "Mouse ScrollWheel", false);
/// <summary>Defines the height and radius for an orbit</summary>
[Serializable]
public struct Orbit
{
/// <summary>Height relative to target</summary>
public float m_Height;
/// <summary>Radius of orbit</summary>
public float m_Radius;
}
/// <summary>Order is Top, Middle, Bottom</summary>
public Orbit[] m_Orbits = new Orbit[3];
/// <summary></summary>
[Tooltip("Controls how taut is the line that connects the rigs' orbits, which determines final placement on the Y axis")]
[Range(0f, 1f)]
public float m_SplineCurvature;
/// <summary>Identifiers for accessing override settings for top and bottom rigs</summary>
public enum RigID { Top, Bottom };
/// <summary>Override settings for top and bottom rigs</summary>
[Serializable]
public class Rig : ISerializationCallbackReceiver
{
public bool m_CustomLens;
public LensSettings m_Lens;
public bool m_CustomBody;
public TransposerSettings m_Body;
public bool m_CustomAim;
public ComposerSettings m_Aim;
public bool m_CustomNoise;
public PerlinNoiseSettings m_Noise;
public void Validate()
{
if (m_Lens.FieldOfView == 0)
m_Lens = LensSettings.Default;
m_Lens.Validate();
}
/// <summary>Blendable settings for Transposer Transposer</summary>
[Serializable] public class TransposerSettings
{
[Range(0f, 20f)] public float m_XDamping;
[Range(0f, 20f)] public float m_YDamping;
[Range(0f, 20f)] public float m_ZDamping;
[Range(0f, 20f)] public float m_PitchDamping;
[Range(0f, 20f)] public float m_YawDamping;
[Range(0f, 20f)] public float m_RollDamping;
internal void Lerp(CinemachineTransposer o, float t)
{
o.m_XDamping = Mathf.Lerp(o.m_XDamping, m_XDamping, t);
o.m_YDamping = Mathf.Lerp(o.m_YDamping, m_YDamping, t);
o.m_ZDamping = Mathf.Lerp(o.m_ZDamping, m_ZDamping, t);
o.m_PitchDamping = Mathf.Lerp(o.m_PitchDamping, m_PitchDamping, t);
o.m_YawDamping = Mathf.Lerp(o.m_YawDamping, m_YawDamping, t);
o.m_RollDamping = Mathf.Lerp(o.m_RollDamping, m_RollDamping, t);
}
internal void PullFrom(CinemachineTransposer o)
{
m_XDamping = o.m_XDamping;
m_YDamping = o.m_YDamping;
m_ZDamping = o.m_ZDamping;
m_PitchDamping = o.m_PitchDamping;
m_YawDamping = o.m_YawDamping;
m_RollDamping = o.m_RollDamping;
}
internal void PushTo(CinemachineTransposer o)
{
o.m_XDamping = m_XDamping;
o.m_YDamping = m_YDamping;
o.m_ZDamping = m_ZDamping;
o.m_PitchDamping =m_PitchDamping;
o.m_YawDamping = m_YawDamping;
o.m_RollDamping = m_RollDamping;
}
}
/// <summary>Blendable settings for Composer</summary>
[Serializable] public class ComposerSettings
{
public Vector3 m_LookAtOffset;
[Space]
[Range(0f, 20)] public float m_HorizontalDamping;
[Range(0f, 20)] public float m_VerticalDamping;
[Space]
[Range(0f, 1f)] public float m_ScreenX;
[Range(0f, 1f)] public float m_ScreenY;
[Range(0f, 1f)] public float m_DeadZoneWidth;
[Range(0f, 1f)] public float m_DeadZoneHeight;
[Range(0f, 2f)] public float m_SoftZoneWidth;
[Range(0f, 2f)] public float m_SoftZoneHeight;
[Range(-0.5f, 0.5f)] public float m_BiasX;
[Range(-0.5f, 0.5f)] public float m_BiasY;
internal void Lerp(CinemachineComposer c, float t)
{
c.m_TrackedObjectOffset = Vector3.Lerp(c.m_TrackedObjectOffset, m_LookAtOffset, t);
c.m_HorizontalDamping = Mathf.Lerp(c.m_HorizontalDamping, m_HorizontalDamping, t);
c.m_VerticalDamping = Mathf.Lerp(c.m_VerticalDamping, m_VerticalDamping, t);
c.m_ScreenX = Mathf.Lerp(c.m_ScreenX, m_ScreenX, t);
c.m_ScreenY = Mathf.Lerp(c.m_ScreenY, m_ScreenY, t);
c.m_DeadZoneWidth = Mathf.Lerp(c.m_DeadZoneWidth, m_DeadZoneWidth, t);
c.m_DeadZoneHeight = Mathf.Lerp(c.m_DeadZoneHeight, m_DeadZoneHeight, t);
c.m_SoftZoneWidth = Mathf.Lerp(c.m_SoftZoneWidth, m_SoftZoneWidth, t);
c.m_SoftZoneHeight = Mathf.Lerp(c.m_SoftZoneHeight, m_SoftZoneHeight, t);
c.m_BiasX = Mathf.Lerp(c.m_BiasX, m_BiasX, t);
c.m_BiasY = Mathf.Lerp(c.m_BiasY, m_BiasY, t);
}
internal void PullFrom(CinemachineComposer c)
{
m_LookAtOffset = c.m_TrackedObjectOffset;
m_HorizontalDamping = c.m_HorizontalDamping;
m_VerticalDamping = c.m_VerticalDamping;
m_ScreenX = c.m_ScreenX;
m_ScreenY = c.m_ScreenY;
m_DeadZoneWidth = c.m_DeadZoneWidth;
m_DeadZoneHeight = c.m_DeadZoneHeight;
m_SoftZoneWidth = c.m_SoftZoneWidth;
m_SoftZoneHeight = c.m_SoftZoneHeight;
m_BiasX = c.m_BiasX;
m_BiasY = c.m_BiasY;
}
internal void PushTo(CinemachineComposer c)
{
c.m_TrackedObjectOffset = m_LookAtOffset;
c.m_HorizontalDamping = m_HorizontalDamping;
c.m_VerticalDamping = m_VerticalDamping;
c.m_ScreenX = m_ScreenX;
c.m_ScreenY = m_ScreenY;
c.m_DeadZoneWidth = m_DeadZoneWidth;
c.m_DeadZoneHeight = m_DeadZoneHeight;
c.m_SoftZoneWidth = m_SoftZoneWidth;
c.m_SoftZoneHeight = m_SoftZoneHeight;
c.m_BiasX = m_BiasX;
c.m_BiasY = m_BiasY;
}
}
/// <summary>Blendable settings for CinemachineBasicMultiChannelPerlin</summary>
[Serializable] public class PerlinNoiseSettings
{
public float m_AmplitudeGain;
public float m_FrequencyGain;
internal void Lerp(CinemachineBasicMultiChannelPerlin p, float t)
{
p.m_AmplitudeGain = Mathf.Lerp(p.m_AmplitudeGain, m_AmplitudeGain, t);
p.m_FrequencyGain =Mathf.Lerp(p.m_FrequencyGain, m_FrequencyGain, t);
}
internal void PullFrom(CinemachineBasicMultiChannelPerlin p)
{
m_AmplitudeGain = p.m_AmplitudeGain;
m_FrequencyGain = p.m_FrequencyGain;
}
internal void PushTo(CinemachineBasicMultiChannelPerlin p)
{
p.m_AmplitudeGain = m_AmplitudeGain;
p.m_FrequencyGain = m_FrequencyGain;
}
}
// This prevents the sensor size from dirtying the scene in the event of aspect ratio change
void ISerializationCallbackReceiver.OnBeforeSerialize()
{
if (!m_Lens.IsPhysicalCamera)
m_Lens.SensorSize = Vector2.one;
}
void ISerializationCallbackReceiver.OnAfterDeserialize() {}
}
[SerializeField]
internal Rig[] m_Rigs = new Rig[2] { new Rig(), new Rig() };
/// <summary>Accessor for rig override settings</summary>
public Rig RigSettings(RigID rig) { return m_Rigs[(int)rig]; }
/// Easy access to the transposer (may be null)
CinemachineTransposer Transposer
{
get { return ComponentCache[(int)CinemachineCore.Stage.Body] as CinemachineTransposer; }
}
/// <summary>Enforce bounds for fields, when changed in inspector.</summary>
protected override void OnValidate()
{
base.OnValidate();
for (int i = 0; i < m_Rigs.Length; ++i)
m_Rigs[i].Validate();
}
private void Awake()
{
m_VerticalAxis.HasRecentering = true;
m_RadialAxis.HasRecentering = false;
}
/// <summary>Updates the child rig cache</summary>
protected override void OnEnable()
{
base.OnEnable();
UpdateInputAxisProvider();
}
/// <summary>
/// API for the inspector. Internal use only
/// </summary>
public void UpdateInputAxisProvider()
{
m_VerticalAxis.SetInputAxisProvider(0, null);
m_RadialAxis.SetInputAxisProvider(1, null);
var provider = GetInputAxisProvider();
if (provider != null)
{
m_VerticalAxis.SetInputAxisProvider(1, provider);
m_RadialAxis.SetInputAxisProvider(2, provider);
}
}
void Reset()
{
DestroyComponents();
#if UNITY_EDITOR
var orbital = UnityEditor.Undo.AddComponent<CinemachineOrbitalTransposer>(gameObject);
UnityEditor.Undo.AddComponent<CinemachineComposer>(gameObject);
#else
var orbital = gameObject.AddComponent<CinemachineOrbitalTransposer>();
gameObject.AddComponent<CinemachineComposer>();
#endif
orbital.HideOffsetInInspector = true;
orbital.m_BindingMode = CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp;
InvalidateComponentCache();
m_Rigs = new Rig[2] { new Rig(), new Rig() };
// Default orbits
m_Orbits = new Orbit[3];
m_Orbits[0].m_Height = 10; m_Orbits[0].m_Radius = 4;
m_Orbits[1].m_Height = 2.5f; m_Orbits[1].m_Radius = 8;
m_Orbits[2].m_Height = -0.5f; m_Orbits[2].m_Radius = 5;
m_SplineCurvature = 0.5f;
}
/// <summary>
/// Force the virtual camera to assume a given position and orientation.
/// Procedural placement then takes over
/// </summary>
/// <param name="pos">Worldspace pposition to take</param>
/// <param name="rot">Worldspace orientation to take</param>
public override void ForceCameraPosition(Vector3 pos, Quaternion rot)
{
base.ForceCameraPosition(pos, rot);
m_VerticalAxis.Value = GetYAxisClosestValue(pos, State.ReferenceUp);
}
/// <summary>If we are transitioning from another FreeLook, grab the axis values from it.</summary>
/// <param name="fromCam">The camera being deactivated. May be null.</param>
/// <param name="worldUp">Default world Up, set by the CinemachineBrain</param>
/// <param name="deltaTime">Delta time for time-based effects (ignore if less than or equal to 0)</param>
public override void OnTransitionFromCamera(
ICinemachineCamera fromCam, Vector3 worldUp, float deltaTime)
{
base.OnTransitionFromCamera(fromCam, worldUp, deltaTime);
InvokeOnTransitionInExtensions(fromCam, worldUp, deltaTime);
m_VerticalAxis.m_Recentering.DoRecentering(ref m_VerticalAxis, -1, 0.5f);
m_VerticalAxis.m_Recentering.CancelRecentering();
if (fromCam != null && m_Transitions.m_InheritPosition
&& !CinemachineCore.Instance.IsLiveInBlend(this))
{
// Note: horizontal axis already taken care of by base class
var cameraPos = fromCam.State.RawPosition;
// Special handling for FreeLook: get an undamped outgoing position
if (fromCam is CinemachineNewFreeLook)
{
var orbital = (fromCam as CinemachineNewFreeLook).Transposer;
if (orbital != null)
cameraPos = orbital.GetTargetCameraPosition(worldUp);
}
ForceCameraPosition(cameraPos, fromCam.State.FinalOrientation);
}
}
float GetYAxisClosestValue(Vector3 cameraPos, Vector3 up)
{
if (Follow != null)
{
// Rotate the camera pos to the back
Quaternion q = Quaternion.FromToRotation(up, Vector3.up);
Vector3 dir = q * (cameraPos - Follow.position);
Vector3 flatDir = dir; flatDir.y = 0;
if (!flatDir.AlmostZero())
{
float angle = Vector3.SignedAngle(flatDir, Vector3.back, Vector3.up);
dir = Quaternion.AngleAxis(angle, Vector3.up) * dir;
}
dir.x = 0;
// Sample the spline in a few places, find the 2 closest, and lerp
int i0 = 0, i1 = 0;
float a0 = 0, a1 = 0;
const int NumSamples = 13;
float step = 1f / (NumSamples-1);
for (int i = 0; i < NumSamples; ++i)
{
float a = Vector3.SignedAngle(
dir, GetLocalPositionForCameraFromInput(i * step), Vector3.right);
if (i == 0)
a0 = a1 = a;
else
{
if (Mathf.Abs(a) < Mathf.Abs(a0))
{
a1 = a0;
i1 = i0;
a0 = a;
i0 = i;
}
else if (Mathf.Abs(a) < Mathf.Abs(a1))
{
a1 = a;
i1 = i;
}
}
}
if (Mathf.Sign(a0) == Mathf.Sign(a1))
return i0 * step;
float t = Mathf.Abs(a0) / (Mathf.Abs(a0) + Mathf.Abs(a1));
return Mathf.Lerp(i0 * step, i1 * step, t);
}
return m_VerticalAxis.Value; // stay conservative
}
/// <summary>Internal use only. Called by CinemachineCore at designated update time
/// so the vcam can position itself and track its targets. All 3 child rigs are updated,
/// and a blend calculated, depending on the value of the Y axis.</summary>
/// <param name="worldUp">Default world Up, set by the CinemachineBrain</param>
/// <param name="deltaTime">Delta time for time-based effects (ignore if less than 0)</param>
override public void InternalUpdateCameraState(Vector3 worldUp, float deltaTime)
{
UpdateTargetCache();
FollowTargetAttachment = 1;
LookAtTargetAttachment = 1;
// Initialize the camera state, in case the game object got moved in the editor
m_State = PullStateFromVirtualCamera(worldUp, ref m_Lens);
m_Rigs[(int)RigID.Top].m_Lens.SnapshotCameraReadOnlyProperties(ref m_Lens);
m_Rigs[(int)RigID.Bottom].m_Lens.SnapshotCameraReadOnlyProperties(ref m_Lens);
// Update our axes
bool activeCam = PreviousStateIsValid || CinemachineCore.Instance.IsLive(this);
if (!activeCam || deltaTime < 0)
m_VerticalAxis.m_Recentering.DoRecentering(ref m_VerticalAxis, -1, 0.5f);
else
{
if (m_VerticalAxis.Update(deltaTime))
m_VerticalAxis.m_Recentering.CancelRecentering();
m_RadialAxis.Update(deltaTime);
m_VerticalAxis.m_Recentering.DoRecentering(ref m_VerticalAxis, deltaTime, 0.5f);
}
// Blend the components
if (mBlender == null)
mBlender = new ComponentBlender(this);
mBlender.Blend(GetVerticalAxisValue());
// Blend the lens
if (m_Rigs[mBlender.OtherRig].m_CustomLens)
m_State.Lens = LensSettings.Lerp(
m_State.Lens, m_Rigs[mBlender.OtherRig].m_Lens, mBlender.BlendAmount);
// Do our stuff
SetReferenceLookAtTargetInState(ref m_State);
InvokeComponentPipeline(ref m_State, worldUp, deltaTime);
ApplyPositionBlendMethod(ref m_State, m_Transitions.m_BlendHint);
// Restore the components
mBlender.Restore();
// Push the raw position back to the game object's transform, so it
// moves along with the camera.
if (Follow != null)
transform.position = State.RawPosition;
if (LookAt != null)
transform.rotation = State.RawOrientation;
// Signal that it's all done
InvokePostPipelineStageCallback(this, CinemachineCore.Stage.Finalize, ref m_State, deltaTime);
PreviousStateIsValid = true;
}
ComponentBlender mBlender;
protected override void OnComponentCacheUpdated()
{
var transposer = Transposer;
if (transposer != null)
{
transposer.HideOffsetInInspector = true;
transposer.m_FollowOffset = new Vector3(
0, m_Orbits[1].m_Height, -m_Orbits[1].m_Radius);
}
}
private float GetVerticalAxisValue()
{
float range = m_VerticalAxis.m_MaxValue - m_VerticalAxis.m_MinValue;
return (range > UnityVectorExtensions.Epsilon) ? m_VerticalAxis.Value / range : 0.5f;
}
/// <summary>
/// Returns the local position of the camera along the spline used to connect the
/// three camera rigs. Does not take into account the current heading of the
/// camera (or its target)
/// </summary>
/// <param name="t">The t-value for the camera on its spline. Internally clamped to
/// the value [0,1]</param>
/// <returns>The local offset (back + up) of the camera WRT its target based on the
/// supplied t-value</returns>
public Vector3 GetLocalPositionForCameraFromInput(float t)
{
UpdateCachedSpline();
int n = 1;
if (t > 0.5f)
{
t -= 0.5f;
n = 2;
}
Vector3 pos = SplineHelpers.Bezier3(
t * 2f, m_CachedKnots[n], m_CachedCtrl1[n], m_CachedCtrl2[n], m_CachedKnots[n+1]);
pos *= Mathf.Max(0, m_RadialAxis.Value);
return pos;
}
Vector2[] m_CachedOrbits;
float m_CachedTension;
Vector4[] m_CachedKnots;
Vector4[] m_CachedCtrl1;
Vector4[] m_CachedCtrl2;
void UpdateCachedSpline()
{
bool cacheIsValid = (m_CachedOrbits != null && m_CachedTension == m_SplineCurvature);
for (int i = 0; i < 3 && cacheIsValid; ++i)
cacheIsValid = (m_CachedOrbits[i].y == m_Orbits[i].m_Height
&& m_CachedOrbits[i].x == m_Orbits[i].m_Radius);
if (!cacheIsValid)
{
float t = m_SplineCurvature;
m_CachedKnots = new Vector4[5];
m_CachedCtrl1 = new Vector4[5];
m_CachedCtrl2 = new Vector4[5];
m_CachedKnots[1] = new Vector4(0, m_Orbits[2].m_Height, -m_Orbits[2].m_Radius, 0);
m_CachedKnots[2] = new Vector4(0, m_Orbits[1].m_Height, -m_Orbits[1].m_Radius, 0);
m_CachedKnots[3] = new Vector4(0, m_Orbits[0].m_Height, -m_Orbits[0].m_Radius, 0);
m_CachedKnots[0] = Vector4.Lerp(m_CachedKnots[0], Vector4.zero, t);
m_CachedKnots[4] = Vector4.Lerp(m_CachedKnots[3], Vector4.zero, t);
SplineHelpers.ComputeSmoothControlPoints(
ref m_CachedKnots, ref m_CachedCtrl1, ref m_CachedCtrl2);
m_CachedOrbits = new Vector2[3];
for (int i = 0; i < 3; ++i)
m_CachedOrbits[i] = new Vector2(m_Orbits[i].m_Radius, m_Orbits[i].m_Height);
m_CachedTension = m_SplineCurvature;
}
}
// Crazy damn thing for blending components at the source level
internal class ComponentBlender
{
Rig.TransposerSettings orbitalSaved = new Rig.TransposerSettings();
Rig.ComposerSettings composerSaved = new Rig.ComposerSettings();
Rig.PerlinNoiseSettings noiseSaved = new Rig.PerlinNoiseSettings();
public int OtherRig;
public float BlendAmount;
CinemachineNewFreeLook mFreeLook;
public ComponentBlender(CinemachineNewFreeLook freeLook) { mFreeLook = freeLook; }
public void Blend(float y)
{
if (y < 0.5f)
{
BlendAmount = 1 - (y * 2);
OtherRig = (int)RigID.Bottom;
}
else
{
BlendAmount = (y - 0.5f) * 2f;
OtherRig = (int)RigID.Top;
}
var orbital = mFreeLook.Transposer;
if (orbital != null && mFreeLook.m_Rigs[OtherRig].m_CustomBody)
{
orbitalSaved.PullFrom(orbital);
mFreeLook.m_Rigs[OtherRig].m_Body.Lerp(orbital, BlendAmount);
}
if (orbital != null)
orbital.m_FollowOffset = mFreeLook.GetLocalPositionForCameraFromInput(y);
var components = mFreeLook.ComponentCache;
var composer = components[(int)CinemachineCore.Stage.Aim] as CinemachineComposer;
if (composer != null && mFreeLook.m_Rigs[OtherRig].m_CustomAim)
{
composerSaved.PullFrom(composer);
mFreeLook.m_Rigs[OtherRig].m_Aim.Lerp(composer, BlendAmount);
}
var noise = components[(int)CinemachineCore.Stage.Noise] as CinemachineBasicMultiChannelPerlin;
if (noise != null && mFreeLook.m_Rigs[OtherRig].m_CustomNoise)
{
noiseSaved.PullFrom(noise);
mFreeLook.m_Rigs[OtherRig].m_Noise.Lerp(noise, BlendAmount);
}
}
public void Restore()
{
var orbital = mFreeLook.Transposer;
if (orbital != null && mFreeLook.m_Rigs[OtherRig].m_CustomBody)
orbitalSaved.PushTo(orbital);
if (orbital != null)
orbital.m_FollowOffset = new Vector3(
0, mFreeLook.m_Orbits[1].m_Height, -mFreeLook.m_Orbits[1].m_Radius);
var components = mFreeLook.ComponentCache;
var composer = components[(int)CinemachineCore.Stage.Aim] as CinemachineComposer;
if (composer != null && mFreeLook.m_Rigs[OtherRig].m_CustomAim)
composerSaved.PushTo(composer);
var noise = components[(int)CinemachineCore.Stage.Noise] as CinemachineBasicMultiChannelPerlin;
if (noise != null && mFreeLook.m_Rigs[OtherRig].m_CustomNoise)
noiseSaved.PushTo(noise);
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bd920fe83f1086148a2b401f3e66528f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: b8ba3923a6094cd48b85425f47fd7450, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,451 @@
#if CINEMACHINE_EXPERIMENTAL_VCAM
using UnityEngine;
using System;
using System.Linq;
namespace Cinemachine
{
/// <summary>
///
/// NOTE: THIS CLASS IS EXPERIMENTAL, AND NOT FOR PUBLIC USE
///
/// Lighter-weight version of the CinemachineVirtualCamera.
///
/// </summary>
[DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
[DisallowMultipleComponent]
[ExecuteAlways]
[AddComponentMenu("Cinemachine/CinemachineNewVirtualCamera")]
public class CinemachineNewVirtualCamera : CinemachineVirtualCameraBase
{
/// <summary>Object for the camera children to look at (the aim target)</summary>
[Tooltip("Object for the camera children to look at (the aim target).")]
[NoSaveDuringPlay]
[VcamTargetProperty]
public Transform m_LookAt = null;
/// <summary>Object for the camera children wants to move with (the body target)</summary>
[Tooltip("Object for the camera children wants to move with (the body target).")]
[NoSaveDuringPlay]
[VcamTargetProperty]
public Transform m_Follow = null;
/// <summary>Specifies the LensSettings of this Virtual Camera.
/// These settings will be transferred to the Unity camera when the vcam is live.</summary>
[Tooltip("Specifies the lens properties of this Virtual Camera. This generally mirrors the "
+ "Unity Camera's lens settings, and will be used to drive the Unity camera when the vcam is active.")]
public LensSettings m_Lens = LensSettings.Default;
/// <summary> Collection of parameters that influence how this virtual camera transitions from
/// other virtual cameras </summary>
public TransitionParams m_Transitions;
/// <summary>Updates the child rig cache</summary>
protected override void OnEnable()
{
base.OnEnable();
InvalidateComponentCache();
}
void Reset()
{
DestroyComponents();
}
/// <summary>Validates the settings avter inspector edit</summary>
protected override void OnValidate()
{
base.OnValidate();
m_Lens.Validate();
}
/// <summary>The camera state, which will be a blend of the child rig states</summary>
override public CameraState State { get { return m_State; } }
/// <summary>The camera state, which will be a blend of the child rig states</summary>
protected CameraState m_State = CameraState.Default;
/// <summary>Get the current LookAt target. Returns parent's LookAt if parent
/// is non-null and no specific LookAt defined for this camera</summary>
override public Transform LookAt
{
get { return ResolveLookAt(m_LookAt); }
set { m_LookAt = value; }
}
/// <summary>Get the current Follow target. Returns parent's Follow if parent
/// is non-null and no specific Follow defined for this camera</summary>
override public Transform Follow
{
get { return ResolveFollow(m_Follow); }
set { m_Follow = value; }
}
/// <summary>This is called to notify the vcam that a target got warped,
/// so that the vcam can update its internal state to make the camera
/// also warp seamlessy.</summary>
/// <param name="target">The object that was warped</param>
/// <param name="positionDelta">The amount the target's position changed</param>
public override void OnTargetObjectWarped(Transform target, Vector3 positionDelta)
{
if (target == Follow)
{
transform.position += positionDelta;
m_State.RawPosition += positionDelta;
}
UpdateComponentCache();
for (int i = 0; i < m_Components.Length; ++i)
{
if (m_Components[i] != null)
m_Components[i].OnTargetObjectWarped(target, positionDelta);
}
base.OnTargetObjectWarped(target, positionDelta);
}
/// <summary>
/// Force the virtual camera to assume a given position and orientation
/// </summary>
/// <param name="pos">Worldspace pposition to take</param>
/// <param name="rot">Worldspace orientation to take</param>
public override void ForceCameraPosition(Vector3 pos, Quaternion rot)
{
PreviousStateIsValid = false;
transform.position = pos;
transform.rotation = rot;
m_State.RawPosition = pos;
m_State.RawOrientation = rot;
UpdateComponentCache();
for (int i = 0; i < m_Components.Length; ++i)
if (m_Components[i] != null)
m_Components[i].ForceCameraPosition(pos, rot);
base.ForceCameraPosition(pos, rot);
}
/// <summary>
/// Query components and extensions for the maximum damping time.
/// </summary>
/// <returns>Highest damping setting in this vcam</returns>
public override float GetMaxDampTime()
{
float maxDamp = base.GetMaxDampTime();
UpdateComponentCache();
for (int i = 0; i < m_Components.Length; ++i)
if (m_Components[i] != null)
maxDamp = Mathf.Max(maxDamp, m_Components[i].GetMaxDampTime());
return maxDamp;
}
/// <summary>If we are transitioning from another FreeLook, grab the axis values from it.</summary>
/// <param name="fromCam">The camera being deactivated. May be null.</param>
/// <param name="worldUp">Default world Up, set by the CinemachineBrain</param>
/// <param name="deltaTime">Delta time for time-based effects (ignore if less than or equal to 0)</param>
public override void OnTransitionFromCamera(
ICinemachineCamera fromCam, Vector3 worldUp, float deltaTime)
{
base.OnTransitionFromCamera(fromCam, worldUp, deltaTime);
InvokeOnTransitionInExtensions(fromCam, worldUp, deltaTime);
bool forceUpdate = false;
if (m_Transitions.m_InheritPosition && fromCam != null
&& !CinemachineCore.Instance.IsLiveInBlend(this))
{
ForceCameraPosition(fromCam.State.FinalPosition, fromCam.State.FinalOrientation);
}
UpdateComponentCache();
for (int i = 0; i < m_Components.Length; ++i)
{
if (m_Components[i] != null
&& m_Components[i].OnTransitionFromCamera(
fromCam, worldUp, deltaTime, ref m_Transitions))
forceUpdate = true;
}
if (forceUpdate)
{
InternalUpdateCameraState(worldUp, deltaTime);
InternalUpdateCameraState(worldUp, deltaTime);
}
else
UpdateCameraState(worldUp, deltaTime);
if (m_Transitions.m_OnCameraLive != null)
m_Transitions.m_OnCameraLive.Invoke(this, fromCam);
}
/// <summary>Internal use only. Called by CinemachineCore at designated update time
/// so the vcam can position itself and track its targets. All 3 child rigs are updated,
/// and a blend calculated, depending on the value of the Y axis.</summary>
/// <param name="worldUp">Default world Up, set by the CinemachineBrain</param>
/// <param name="deltaTime">Delta time for time-based effects (ignore if less than 0)</param>
override public void InternalUpdateCameraState(Vector3 worldUp, float deltaTime)
{
UpdateTargetCache();
FollowTargetAttachment = 1;
LookAtTargetAttachment = 1;
// Initialize the camera state, in case the game object got moved in the editor
m_State = PullStateFromVirtualCamera(worldUp, ref m_Lens);
// Do our stuff
SetReferenceLookAtTargetInState(ref m_State);
InvokeComponentPipeline(ref m_State, worldUp, deltaTime);
ApplyPositionBlendMethod(ref m_State, m_Transitions.m_BlendHint);
// Push the raw position back to the game object's transform, so it
// moves along with the camera.
if (Follow != null)
transform.position = State.RawPosition;
if (LookAt != null)
transform.rotation = State.RawOrientation;
// Signal that it's all done
InvokePostPipelineStageCallback(this, CinemachineCore.Stage.Finalize, ref m_State, deltaTime);
PreviousStateIsValid = true;
}
/// <summary>
/// Returns true, when the vcam has extensions or components that require input.
/// </summary>
internal override bool RequiresUserInput()
{
return base.RequiresUserInput() ||
m_Components != null && m_Components.Any(t => t != null && t.RequiresUserInput);
}
private Transform mCachedLookAtTarget;
private CinemachineVirtualCameraBase mCachedLookAtTargetVcam;
/// <summary>Set the state's refeenceLookAt target to our lookAt, with some smarts
/// in case our LookAt points to a vcam</summary>
protected void SetReferenceLookAtTargetInState(ref CameraState state)
{
Transform lookAtTarget = LookAt;
if (lookAtTarget != mCachedLookAtTarget)
{
mCachedLookAtTarget = lookAtTarget;
mCachedLookAtTargetVcam = null;
if (lookAtTarget != null)
mCachedLookAtTargetVcam = lookAtTarget.GetComponent<CinemachineVirtualCameraBase>();
}
if (lookAtTarget != null)
{
if (mCachedLookAtTargetVcam != null)
state.ReferenceLookAt = mCachedLookAtTargetVcam.State.FinalPosition;
else
state.ReferenceLookAt = TargetPositionCache.GetTargetPosition(lookAtTarget);
}
}
protected CameraState InvokeComponentPipeline(
ref CameraState state, Vector3 worldUp, float deltaTime)
{
UpdateComponentCache();
// Extensions first
InvokePrePipelineMutateCameraStateCallback(this, ref state, deltaTime);
// Apply the component pipeline
for (CinemachineCore.Stage stage = CinemachineCore.Stage.Body;
stage <= CinemachineCore.Stage.Finalize; ++stage)
{
var c = m_Components[(int)stage];
if (c != null)
c.PrePipelineMutateCameraState(ref state, deltaTime);
}
CinemachineComponentBase postAimBody = null;
for (CinemachineCore.Stage stage = CinemachineCore.Stage.Body;
stage <= CinemachineCore.Stage.Finalize; ++stage)
{
var c = m_Components[(int)stage];
if (c != null)
{
if (stage == CinemachineCore.Stage.Body && c.BodyAppliesAfterAim)
{
postAimBody = c;
continue; // do the body stage of the pipeline after Aim
}
c.MutateCameraState(ref state, deltaTime);
}
InvokePostPipelineStageCallback(this, stage, ref state, deltaTime);
if (stage == CinemachineCore.Stage.Aim)
{
if (c == null)
state.BlendHint |= CameraState.BlendHintValue.IgnoreLookAtTarget; // no aim
// If we have saved a Body for after Aim, do it now
if (postAimBody != null)
{
postAimBody.MutateCameraState(ref state, deltaTime);
InvokePostPipelineStageCallback(this, CinemachineCore.Stage.Body, ref state, deltaTime);
}
}
}
return state;
}
// Component Cache - serialized only for copy/paste
[SerializeField, HideInInspector, NoSaveDuringPlay]
CinemachineComponentBase[] m_Components;
/// For inspector
internal CinemachineComponentBase[] ComponentCache
{
get
{
UpdateComponentCache();
return m_Components;
}
}
/// <summary>Call this when CinemachineCompponentBase compponents are added
/// or removed. If you don't call this, you may get null reference errors.</summary>
public void InvalidateComponentCache()
{
m_Components = null;
}
/// <summary>Bring the component cache up to date if needed</summary>
protected void UpdateComponentCache()
{
#if UNITY_EDITOR
// Special case: if we have serialized in with some other game object's
// components, then we have just been pasted so we should clone them
for (int i = 0; m_Components != null && i < m_Components.Length; ++i)
{
if (m_Components[i] != null && m_Components[i].gameObject != gameObject)
{
var copyFrom = m_Components;
DestroyComponents();
CopyComponents(copyFrom);
break;
}
}
#endif
if (m_Components != null && m_Components.Length == (int)CinemachineCore.Stage.Finalize + 1)
return; // up to date
m_Components = new CinemachineComponentBase[(int)CinemachineCore.Stage.Finalize + 1];
var existing = GetComponents<CinemachineComponentBase>();
for (int i = 0; existing != null && i < existing.Length; ++i)
m_Components[(int)existing[i].Stage] = existing[i];
for (int i = 0; i < m_Components.Length; ++i)
{
if (m_Components[i] != null)
{
if (CinemachineCore.sShowHiddenObjects)
m_Components[i].hideFlags &= ~HideFlags.HideInInspector;
else
m_Components[i].hideFlags |= HideFlags.HideInInspector;
}
}
OnComponentCacheUpdated();
}
/// <summary>Notification that the component cache has just been update,
/// in case a subclass needs to do something extra</summary>
protected virtual void OnComponentCacheUpdated() {}
/// <summary>Destroy all the CinmachineComponentBase components</summary>
protected void DestroyComponents()
{
var existing = GetComponents<CinemachineComponentBase>();
for (int i = 0; i < existing.Length; ++i)
{
#if UNITY_EDITOR
UnityEditor.Undo.DestroyObjectImmediate(existing[i]);
#else
UnityEngine.Object.Destroy(existing[i]);
#endif
}
InvalidateComponentCache();
}
#if UNITY_EDITOR
// This gets called when user pastes component values
void CopyComponents(CinemachineComponentBase[] copyFrom)
{
foreach (CinemachineComponentBase c in copyFrom)
{
if (c != null)
{
Type type = c.GetType();
var copy = UnityEditor.Undo.AddComponent(gameObject, type);
UnityEditor.Undo.RecordObject(copy, "copying pipeline");
System.Reflection.BindingFlags bindingAttr
= System.Reflection.BindingFlags.Public
| System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Instance;
System.Reflection.FieldInfo[] fields = type.GetFields(bindingAttr);
for (int i = 0; i < fields.Length; ++i)
if (!fields[i].IsStatic)
fields[i].SetValue(copy, fields[i].GetValue(c));
}
}
}
#endif
/// Legacy support for an old API. GML todo: deprecate these methods
/// <summary>Get the component set for a specific stage.</summary>
/// <param name="stage">The stage for which we want the component</param>
/// <returns>The Cinemachine component for that stage, or null if not defined</returns>
public CinemachineComponentBase GetCinemachineComponent(CinemachineCore.Stage stage)
{
var cache = ComponentCache;
var i = (int)stage;
return i >= 0 && i < cache.Length ? cache[i] : null;
}
/// <summary>Get an existing component of a specific type from the cinemachine pipeline.</summary>
public T GetCinemachineComponent<T>() where T : CinemachineComponentBase
{
var components = ComponentCache;
foreach (var c in components)
if (c is T)
return c as T;
return null;
}
/// <summary>Add a component to the cinemachine pipeline.</summary>
public T AddCinemachineComponent<T>() where T : CinemachineComponentBase
{
var components = ComponentCache;
T c = gameObject.AddComponent<T>();
var oldC = components[(int)c.Stage];
if (oldC != null)
{
oldC.enabled = false;
RuntimeUtility.DestroyObject(oldC);
}
InvalidateComponentCache();
return c;
}
/// <summary>Remove a component from the cinemachine pipeline.</summary>
public void DestroyCinemachineComponent<T>() where T : CinemachineComponentBase
{
var components = ComponentCache;
foreach (var c in components)
{
if (c is T)
{
c.enabled = false;
RuntimeUtility.DestroyObject(c);
InvalidateComponentCache();
return;
}
}
}
// This prevents the sensor size from dirtying the scene in the event of aspect ratio change
internal override void OnBeforeSerialize()
{
if (!m_Lens.IsPhysicalCamera)
m_Lens.SensorSize = Vector2.one;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: be817a1286f201845b5c36ae9afe9e35
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: b8ba3923a6094cd48b85425f47fd7450, type: 3}
userData:
assetBundleName:
assetBundleVariant: