Specific camera behavior via Cinemachine help

Hello, I’m working on a 3d camera system and using Cinemachine’s Freelook camera. My preferred binding mode is “World Space,” however it seems to lack a specific piece of functionality that I’d like. The camera does not rotate WITH the player, and by this I mean no matter what the angle the player is standing on the camera will always be level with the player.

The only solution I have found is to set the “World Up Override” to my player, and then changing my binding mode to “Simple Follow with World Up.” This allows the camera to rotate with the player just like I wanted, however it also makes the camera rotate with the player whenever they move, despite there being zero input for the camera. That’s the bit I have a problem with. I’d like to be able to make the camera able to track and rotate around the player as if I had my binding mode set to 'Simple Follow with World Up" as well as maintaining the no rotation behavior that “World Up” provides.

Any ideas anyone?

Are you using Cinemachine 3?

If so, take a look at the the 3D sample scenes that ship with CM. Specifically, look at the FreeLook on Spherical Surface scene. Is that the behaviour you’re looking for?

To achieve your desired behavior—a camera that rotates with the player’s orientation but does not rotate unnecessarily when the player moves—you can combine certain configurations with scripting. Here’s how you can refine the Cinemachine Freelook camera behavior to achieve this functionality:

Solution Outline:
Set the Binding Mode to “World Space” for more precise control.
Use a Custom Script to update the World Up Override dynamically without the unwanted movement.

Implementation Steps:
Use “World Space” Binding Mode: Keep the Freelook camera’s binding mode set to “World Space.” This allows the camera to track the player without excessive movement caused by “Simple Follow with World Up.”
Create a Custom Script for Dynamic Rotation: Write a script to set the World Up Override to the player’s transform but conditionally rotate the camera based on the player’s orientation, rather than movement.

Here’s a sample script:

csharp
CopyEdit
using UnityEngine;
using Cinemachine;

public class CustomCameraController : MonoBehaviour
{
public CinemachineFreeLook freeLookCamera;
public Transform player;

void LateUpdate()
{
    if (freeLookCamera != null && player != null)
    {
        // Dynamically set the camera's World Up Override to the player's rotation
        freeLookCamera.m_WorldUpOverride = player;

        // Manually adjust the camera's rotation to align with the player's orientation
        Vector3 playerUp = player.up;
        Vector3 cameraUp = freeLookCamera.transform.up;

        // Preserve the "no extra rotation during movement" by ensuring the camera's up aligns with the player's up
        if (Vector3.Dot(playerUp, cameraUp) < 0.99f) // Avoid jitter from small adjustments
        {
            freeLookCamera.transform.rotation = Quaternion.FromToRotation(cameraUp, playerUp) * freeLookCamera.transform.rotation;
        }
    }
}

}

How This Script Works:
The m_WorldUpOverride is assigned to the player’s transform, ensuring the camera reacts to the player’s orientation.

The camera’s rotation is adjusted only when the player’s “up” vector and the camera’s “up” vector differ significantly. This prevents unnecessary rotations while maintaining alignment with the player.

Adjust Freelook Rig Settings:
Fine-tune the Freelook camera rig’s height and radius values for each ring (Top, Middle, and Bottom).
Adjust damping and sensitivity settings to ensure the camera remains smooth during transitions.

Why This Works:

“World Space” Binding Mode: Keeps the camera level unless explicitly rotated.
Dynamic Up Vector Alignment: Aligns the camera’s rotation with the player’s orientation without introducing unwanted movement during normal gameplay.
By combining “World Space” binding mode with this custom script, you should achieve the desired behavior: a camera that tracks and rotates with the player’s orientation while ignoring unintended rotations caused by player movement. It worked for my nulls brawl.

Sorry for the late reply! Health issues, anyway, I just took a look and no thats not quite it. That version has the part of the camera’s logic i dont like. When the player moves right or left, the camera rotates to look at them, instead of just following them like it does with the “World Space” Orbit method.

this did not work

Thanks, now I understand what you want.

You can make the FreeLook On Spherical Surface scene behave the way you describe by making some small modifications to it, which I will describe here.

The solution involves creating an intermediary object to represent your travelling “world” coords, and making the camera track that instead of your player. The FreeLook On Spherical Surface scene already has such an object, but it’s matching its rotation to the Player. You need to modify the custom script so that it only matches its up to the player’s up.

On the WorldUpOverride object there is a script called DampedTracker. It matches the transform of the current object to the tracked object.

Replace the contents of the DampedTracker script with this code:

using UnityEngine;

namespace Unity.Cinemachine.Samples
{
    /// <summary>
    /// Will match a GameObject's position and rotation to a target's position 
    /// and rotation, with damping
    /// </summary>
    [ExecuteAlways]
    public class DampedTracker : MonoBehaviour
    {
        [Tooltip("The target to track")]
        public Transform Target;
        [Tooltip("How fast the GameObject moves to the target position")]
        public float PositionDamping = 1;
        [Tooltip("How fast the rotation aligns with target rotation")]
        public float RotationDamping = 1;

        void OnEnable()
        {
            if (Target != null)
                transform.SetPositionAndRotation(Target.position, Target.rotation);
        }

        void LateUpdate()
        {
            if (Target != null)
            {
                // Match the player's position
                float t = Damper.Damp(1, PositionDamping, Time.deltaTime);
                var pos = Vector3.Lerp(transform.position, Target.position, t);

                // Rotate my transform to make my up match the target's up
                var rot = transform.rotation;
                t = Damper.Damp(1, RotationDamping, Time.deltaTime);
                var srcUp = transform.up;
                var dstUp = Target.up;
                var axis = Vector3.Cross(srcUp, dstUp);
                if (axis.sqrMagnitude > 0.001f)
                {
                    var angle = UnityVectorExtensions.SignedAngle(srcUp, dstUp, axis) * t;
                    rot = Quaternion.AngleAxis(angle, axis) * rot;
                }
                transform.SetPositionAndRotation(pos, rot);
            }
        }
    }
}

Next, go to the Player Camera object, and make two changes:

  1. Change the TrackingTarget from Player to WorldUpOverride:

  1. In the OrbitalFollow component, change the Binding Mode to Lock To Target:

Now, it should behave the way you want. You might want to reduce the Damping settings in DampedTracker to make the camera a little snappier.