Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions com.unity.cinemachine/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Unreleased

### Bugfixes
- Ensure that correct blend time is always used when backing out of a blend-in-progress.
- CinemachineVolumeSettings: changes to Focal Length and Aperture settings were not being applied while auto-focus was enabled.
- InheritPosition was not inheriting the camera position in all cases.

Expand Down
25 changes: 11 additions & 14 deletions com.unity.cinemachine/Runtime/Core/CameraBlendStack.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ class StackFrame : NestedBlendSource
ICinemachineCamera m_SnapshotSource;
float m_SnapshotBlendWeight;

// If reversing a blend-in-progress, this will indicate how much of the blend was skipped
public float MidBlendNormalizedStartPoint;

public StackFrame() : base(new ()) {}
public bool Active => Source.IsValid;

Expand Down Expand Up @@ -234,7 +237,7 @@ public void UpdateRootFrame(
if (activeCamera != outgoingCamera)
{
bool backingOutOfBlend = false;
float backingOutPercentCompleted = 0;
float normalizedBlendPosition = 0;
float duration = 0;

// Do we need to create a game-play blend?
Expand All @@ -248,7 +251,8 @@ public void UpdateRootFrame(
// Are we backing out of a blend-in-progress?
backingOutOfBlend = frame.Source.CamA == activeCamera && frame.Source.CamB == outgoingCamera;
if (backingOutOfBlend && frame.Blend.Duration > kEpsilon)
backingOutPercentCompleted = frame.Blend.TimeInBlend / frame.Blend.Duration;
normalizedBlendPosition = frame.MidBlendNormalizedStartPoint
+ (1 - frame.MidBlendNormalizedStartPoint) * frame.Blend.TimeInBlend / frame.Blend.Duration;

frame.Source.CamA = outgoingCamera;
frame.Source.BlendCurve = blendDef.BlendCurve;
Expand Down Expand Up @@ -282,8 +286,7 @@ public void UpdateRootFrame(
snapshot = true;

// Avoid nesting too deeply
var nbs = frame.Blend.CamA as NestedBlendSource;
if (!snapshot && nbs != null && nbs.Blend.CamA is NestedBlendSource nbs2)
if (!snapshot && frame.Blend.CamA is NestedBlendSource nbs && nbs.Blend.CamA is NestedBlendSource nbs2)
nbs2.Blend.CamA = new SnapshotBlendSource(nbs2.Blend.CamA);

// Special case: if backing out of a blend-in-progress
Expand All @@ -292,15 +295,8 @@ public void UpdateRootFrame(
if (backingOutOfBlend)
{
snapshot = true; // always use a snapshot for this to prevent pops
var adjustedDuration = frame.Blend.TimeInBlend;
if (nbs != null)
adjustedDuration += nbs.Blend.Duration - nbs.Blend.TimeInBlend;
else if (frame.Blend.CamA is SnapshotBlendSource sbs)
adjustedDuration += sbs.RemainingTimeInBlend;

// In the event that the blend times in the different directions are different,
// don't make the blend longer than it would otherwise have been
duration = Mathf.Min(duration * backingOutPercentCompleted, adjustedDuration);
duration = duration * normalizedBlendPosition; // skip first part of blend
normalizedBlendPosition = 1 - normalizedBlendPosition;
}

// Chain to existing blend
Expand All @@ -313,13 +309,14 @@ public void UpdateRootFrame(
camA = new NestedBlendSource(blendCopy);
}
}
// For the event, we use the raw outgoing camera, not the blend source
// For the event, we use the raw outgoing camera, not the actual blend source
frame.Blend.CamA = outgoingCamera;
frame.Blend.CamB = activeCamera;
frame.Blend.BlendCurve = frame.Source.BlendCurve;
frame.Blend.Duration = duration;
frame.Blend.TimeInBlend = 0;
frame.Blend.CustomBlender = frame.Source.CustomBlender;
frame.MidBlendNormalizedStartPoint = normalizedBlendPosition;

// Allow the client to modify the blend
if (duration > 0)
Expand Down
59 changes: 57 additions & 2 deletions com.unity.cinemachine/Tests/Editor/BlendManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,21 @@ class FakeCamera : ICinemachineCamera
public FakeCamera(string name) => m_Name = name;
public string Name => m_Name;
public string Description => string.Empty;
public CameraState State => CameraState.Default;
public CameraState State
{
get
{
var state = CameraState.Default;
state.RawPosition = Position;
return state;
}
}
public bool IsValid => true;
public ICinemachineMixer ParentCamera => null;
public void UpdateCameraState(Vector3 worldUp, float deltaTime) {}
public void OnCameraActivated(ICinemachineCamera.ActivationEventParams evt) {}

public Vector3 Position;
}

class FakeMixer : FakeCamera, ICinemachineMixer
Expand Down Expand Up @@ -61,7 +71,7 @@ [TearDown] public void TearDown()
void Reset(float blendTime)
{
m_BlendManager.LookupBlendDelegate = (outgoing, incoming)
=> new (CinemachineBlendDefinition.Styles.EaseInOut, blendTime); // constant blend time
=> new (CinemachineBlendDefinition.Styles.Linear, blendTime); // linear blend, constant blend time
m_BlendManager.OnEnable();
ProcessFrame(null, 0.1f);
ResetCounters();
Expand Down Expand Up @@ -224,5 +234,50 @@ public void TestEventsBlendToNestedBlend()
Assert.AreEqual(1, m_BlendFinishedCount);
Assert.That(m_BlendManager.IsBlending, Is.False);
}

[Test]
public void TestBlendReversal()
{
Reset(1); // constant blend time of 1
m_Cam1.Position = new Vector3(0, 0, 0);
m_Cam2.Position = new Vector3(1, 0, 0);

// Start with cam1
ProcessFrame(m_Cam1, 0.1f);
Assert.That(m_BlendManager.IsBlending, Is.False);

// Activate cam2
ProcessFrame(m_Cam2, 0.5f);
Assert.That(m_BlendManager.IsBlending, Is.True);
Assert.AreEqual(0.5f, m_BlendManager.CameraState.RawPosition.x, 0.001f);

// Reverse the blend to cam1
ProcessFrame(m_Cam1, 0.2f);
Assert.That(m_BlendManager.IsBlending, Is.True);
Assert.AreEqual(0.3f, m_BlendManager.CameraState.RawPosition.x, 0.001f);

// Reverse the blend again to cam2
ProcessFrame(m_Cam2, 0.1f);
Assert.That(m_BlendManager.IsBlending, Is.True);
Assert.AreEqual(0.4f, m_BlendManager.CameraState.RawPosition.x, 0.001f);

ProcessFrame(m_Cam2, 0.4f);
Assert.That(m_BlendManager.IsBlending, Is.True);
Assert.AreEqual(0.8f, m_BlendManager.CameraState.RawPosition.x, 0.001f);

// Reverse the blend again to cam1
ProcessFrame(m_Cam1, 0.1f);
Assert.That(m_BlendManager.IsBlending, Is.True);
Assert.AreEqual(0.7f, m_BlendManager.CameraState.RawPosition.x, 0.001f);

// And finish the blend on cam2
ProcessFrame(m_Cam2, 0.1f);
Assert.That(m_BlendManager.IsBlending, Is.True);
Assert.AreEqual(0.8f, m_BlendManager.CameraState.RawPosition.x, 0.001f);

ProcessFrame(m_Cam2, 0.201f);
Assert.That(m_BlendManager.IsBlending, Is.False);
Assert.AreEqual(1.0f, m_BlendManager.CameraState.RawPosition.x, 0.001f);
}
}
}
2 changes: 1 addition & 1 deletion com.unity.cinemachine/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "com.unity.cinemachine",
"displayName": "Cinemachine",
"version": "3.1.4",
"version": "3.1.5",
"unity": "2022.3",
"description": "Smart camera tools for passionate creators. \n\nCinemachine 3 is a newer and better version of Cinemachine, but upgrading an existing project from 2.X will likely require some effort. If you're considering upgrading an older project, please see our upgrade guide in the user manual.",
"keywords": [
Expand Down