Auto Set Freelook camera behind player with Simple Follow

Hi there, in our game we currently use a Cinemachine freelook camera in combination with “Simple Follow With World Up”, and that mostly gives us the camera control we need with the one issue that the camera starts in the wrong position. Using “Lock to Target” starts the camera behind the player (where it should be), but also completely breaks the smooth auto camera that “Simple Follow With World Up” gives when moving around (“Lock to Target” causes the A/D to essentially hard-rotate the camera). So what I am confused about is how to get both pieces of functionality, The auto centring behind player on start and the Smooth Auto Camera that “Simple Follow With World Up” offers. I’m not entirely familiar with what is going on under the hood, Is it possibly to achieve a similar auto-camera like “Simple Follow With World Up” with one of the other binding modes. Our game is procedurally generated so relative forward may not always be the same direction each time (So world space doesn’t really fix the problem, and sacrifices the auto-camera completely). One possible solution I can think of is starting with “Lock to Target” and creating a co-routine to change the binding mode at runtime once it is locked in but that feels like completely the wrong approach.

This has been bothering me for months and our game has got to the point where it would make sense to have this issue sorted. See screenshots (of game start-up with no additional camera movement) for an idea of what I mean.

“Simple Follow With World Up”

“Lock to Target”

The SimpleFollow binding mode has no built-in concept of “centered behind the target”, but it’s not difficult to make a custom extension that does the job. Here is one. Add it to your FreeLook.

using UnityEngine;
using Cinemachine;
using Cinemachine.Utility;

public class SimpleFollowRecenter : CinemachineExtension
{
    public bool Recenter;
    public float RecenterTime = 0.5f;
    protected override void PostPipelineStageCallback(
        CinemachineVirtualCameraBase vcam,
        CinemachineCore.Stage stage, ref CameraState state, float deltaTime)
    {
        if (stage != CinemachineCore.Stage.Body)
            return;

        Transform target = vcam != null ? vcam.Follow : null;
        if (target == null)
            return;

        if (Recenter)
        {
            // How far away from centered are we?
            Vector3 up = vcam.State.ReferenceUp;
            Vector3 back = vcam.transform.position - target.position;
            float angle = UnityVectorExtensions.SignedAngle(
                back.ProjectOntoPlane(up), -target.forward.ProjectOntoPlane(up), up);
            if (Mathf.Abs(angle) < 0.1f)
                Recenter = false; // done!
            else
            {
                angle = Damper.Damp(angle, RecenterTime, deltaTime);
                Vector3 pos = state.RawPosition - target.position;
                pos = Quaternion.AngleAxis(angle, up) * pos;
                state.RawPosition = pos + target.position;
            }
        }
    }
}
1 Like