Hey there!
If have an environment with lots of different virtual cams. The cameras are categorized into different layers in a hierarchy structure. Overview->Area->Building->Engine.
The cameras of the layers are set up to have different distances in the given order above and are free look cameras that rotate around a pivot using the orbital transposer. The layers are basically zoom levels of the environment. You can go freely up and down in the zoom levels and the transitions work great out of the box for most use cases.
The problem I currently face is happening when I switch from one Engine-Object to another Engine-Object that is in another building. The default blending options don’t allow me to specify a path where the cam moves along, so the cam takes the shortest path. And the shortest path is this case is very bad, as it is a straight line through tons of walls, which looks pretty ugly ;(
My current solution(which is actually working) is to add an Y-Offset arc to the blending path, which works as the following:
First I wrote an CinemachineExtension called “CameraPositionOffset”
Code
public class CameraPositionOffset : CinemachineExtension
{
public Vector3 offset = Vector3.zero;
public CinemachineCore.Stage stage = CinemachineCore.Stage.Aim;
protected override void PostPipelineStageCallback(
CinemachineVirtualCameraBase vcam,
CinemachineCore.Stage stage, ref CameraState state, float deltaTime)
{
if (stage == this.stage)
{
state.PositionCorrection += offset;
}
}
}
which allows me to specify a position offset for the camera.
I then added a script to the GO that holds the CinemachineBrain:
Code
[RequireComponent(typeof(CinemachineBrain))]
public class CameraAnimatedHeightOffset : MonoBehaviour
{
public float heightOffset = 20f;
public CinemachineCore.Stage stage;
private CinemachineBrain _brain;
private CameraPositionOffset _lastOffsetCompA;
private CameraPositionOffset _lastOffsetCompB;
private Vector3 _lastOffset = Vector3.zero;
private void Awake()
{
_brain = GetComponent<CinemachineBrain>();
}
private void Update()
{
TryReset(ref _lastOffsetCompA);
TryReset(ref _lastOffsetCompB);
if (_brain.ActiveBlend != null && !_brain.ActiveBlend.IsComplete)
{
var blend = _brain.ActiveBlend;
if (blend.CamA != null && blend.CamB != null)
{
//Engine to engine transtion
if (blend.CamA.Name.Contains("Engine") && blend.CamB.Name.Contains("Engine"))
{
float lerp = Mathf.Clamp01(blend.TimeInBlend / blend.Duration);
Vector3 offset = heightOffset * Mathf.Sin(lerp * Mathf.PI) * Vector3.up;
_lastOffset = offset;
var compA = blend.CamA.VirtualCameraGameObject.GetComponent<CameraPositionOffset>();
TryApplyOffset(compA, ref _lastOffsetCompA, offset);
var compB = blend.CamB.VirtualCameraGameObject.GetComponent<CameraPositionOffset>();
TryApplyOffset(compB, ref _lastOffsetCompB, offset);
return;
}
}
}
_lastOffset = Vector3.zero;
}
private void TryReset(ref CameraPositionOffset cam)
{
if(cam != null)
{
cam.offset = _lastOffset;
cam = null;
}
}
private void TryApplyOffset(CameraPositionOffset from, ref CameraPositionOffset to, Vector3 offset)
{
if (from)
{
to = from;
to.offset = offset;
to.stage = stage;
}
}
}
It basically check every frame if a transition between two Engine-Object cams is happening (by name), and if true applies an sine wave Y-Offset to both cameras using the CameraPositionOffset script above.
This solution actually works as intended and the cam makes a nice arc that avoids flying though the walls.
But from a coding standpoint I find this to be a very very ugly solution
So to the actual question after this way to long introduction
Is there any better and more flexible way to customize the transition path between cameras?
A system like the custom blends is pretty much what I am looking for, but with control over the path instead of only the duration and style. I looked through a lot of source code and other forum threatds but could’t find anything useful on this topic.
Any help is appreciated!
Greets
MSQTobi