Using Cinemachine to smoothly look at targets in first person mode

I am using Cinemachine 3.1.0 and a pretty basic StarterAsset environment. The Cinemachine camera is set up by default, so I assume it’s what the StartAssets want me to use. I have a first person view that seems to work just fine with movement, turning, jumping, etc and the StarterAssets set the Cinemachine camera with a Tracking Target of PlayerCameraRoot and a Position Control of Third Person Follow. Ok, great.

What I want to accomplish is using binoculars to zoom into objects in the environment and some of these objects have an event box around them that will kick off an event that will temporarily lock control and move the player’s view from one object to another while they are still zoomed in. Basically small scripted events that can occur when viewing something from a distance. I have the zoom working just fine using FOV adjustments and also I am able to acknowledge when one of these events should start by using a raycast and also checking the zoom level of the binoculars. No problems so far.

Now when kicking off this event I want multiple objects to be looked at via the event, like possibly switching the center of the view between two characters, objects, etc. I notice that the default StartAssets control the horizontal rotation of the player by rotating the transform but the vertical rotation is handled by changing the Cinemachine localrotation pitch:

// Update Cinemachine camera target pitch
CinemachineCameraTarget.transform.localRotation = Quaternion.Euler(_cinemachineTargetPitch, 0.0f, 0.0f);

// rotate the player left and right
transform.Rotate(Vector3.up * _rotationVelocity);

I’m not sure if I need to be concerned about that but I’m assuming that I can just use the Look At Target transform to move the player’s view to look at what I want. So I’m setting the Look At Target and then setting the Rotation Control to Rotation Composer and then setting the Damping to 3 for both the X and Y values. It kind of works, but the first target to look at results in the camera jerking toward it, but oddly subsequent targets don’t have the jerking motion and do move smoothly toward the target, although they never quite center on it, always being either a little to the left or right compared to what I’m trying to center on. Then when the camera control is given back to the player, the first input jerks the camera again and then it returns to normal.

Maybe I’m giving too much context, but basically I am trying to:

Move the camera target to multiple objects using the player’s zoomed in view, initially starting from where the player is already looking and then smoothly moving between multiple targets by switching Look At Target several times before returning control to the player with the same level of zoom that the event is currently at.

I did see things about having multiple cameras for Cinemachine but this seems like it isn’t needed here? Any help is appreciated, thanks!

Could you post the code that makes this work?

The way you describe it, if that‘s the order of operations (set lookat target, set rotation control, set damping) I would do the reverse so that changing the lookat target comes last because this may already perform some computation with the current settings, and then changing the settings is where I imagine this once-only behaviour might come from.

Or it might be something else altogether.

I definitely could have described that better!

So I have the Camera preset to Rotation Composer and the damping is already set, then in code is where I am changing the Look At Target.

_cinemachineCamera.LookAt = lookAt;

So nothing too fancy there. I guess I should check the values after setting the Target to see if they are changing.

Hmmm having the rotation composer active all the time will make it update its internal state which you likely override with mouselook, since this aspect seems to work. But once you switch off mouselook the composer may start from a different state than expected. Unless you need the composer in normal operation I would change it when switching modes.

But the alternative of using multiple cameras is also viable. This has the additional effect that switching cameras smoothly transitions one to the other.

I’ll try switching it.

On the topic of more cameras, I’m curious about it and maybe my trepidation in this case comes from my lack of experience on it. Having multiple cameras makes sense to me if those cameras inhabit different locations, such needing to move across the room to show something, or rotate around an object. But since the camera in this case is always on the player, and always looking out from the player’s forward position I’m not understanding what the value added with another camera is I guess? What do I do with this extra camera? Do I have it pointing at the object that I want to look at and then when I’m done doing that I switch back to the free look camera? Why is that less complicated than moving the freelook camera to the intended Look at target? I am genuinely curious about the answers btw since Cinemachine is quite new to me.

Another question:

So is the jerking part basically happening because the StarterAssets only control the pitch of the virtual camera, so when it begins it’s LookAt functionality, the forward of the virtual camera is looking elsewhere than where the player was just looking?

If that’s the case, is the virtual camera just getting in my way for what I’m trying to do? Should I remove the virtual cam altogether and just use the main camera and move it around as needed? Mostly I had the virtual cam being used because the StarterAssets were using it, so I assumed it was the way to go, but maybe for my use case it isn’t?

I got something working for the most part. I’m just rotating on the X axis for the virtual camera and on the Y axis for the player capsule, same as the StarterAssets are doing:

var dirTotarget = Quaternion.LookRotation(_currentLookAt.position - _cinemachineCameraTarget.transform.position);
var dirTotarget2 = Quaternion.LookRotation(_currentLookAt.position - _playerSubject.transform.position);

_cinemachineCameraTarget.transform.localRotation = Quaternion.Slerp(_cinemachineCameraTarget.transform.localRotation, Quaternion.Euler(dirTotarget.eulerAngles.x, 0.0f, 0.0f), Time.deltaTime * rotateSpeed);
_playerSubject.transform.rotation = Quaternion.Slerp(_playerSubject.transform.rotation, Quaternion.Euler(0f, dirTotarget2.eulerAngles.y, 0f), Time.deltaTime * rotateSpeed);

I’m hoping this doesn’t come back to bite me later. I do still need to figure out why the camera jerks after the end has ended. Seems like it’s returning to some state that existed before the zoom event started, not sure why.

Same as for code: separation of concerns. With two separate cameras you can tweak them individually and simply switch between them. If you use a single component to do two or more things at once you start adding control statements, extra fields to store previous state, and generate issues when switching as extra control flow can cause unforeseen side effects.

By being able to disable one and enable another thing, if anything goes wrong you know you have to focus only on one of the two scripts/cameras, and any bugfixes applied typically won‘t affect the other script.

In essence, it‘s how you maintain a healthy level of sanity in any project, more so the greater the complexity. And we all know that complexity grows over time.