Cinemachine framing transposer fixed on Y axis but moving camera towards mouse position on X and Z

Hi!

I’ve been working on a Cinemachine implementation of a camera I recently made.

The functionality is pretty simple, where I want the camera to look ahead of the player in the direction of the cursor by moving only on the X and Z axis, while retaining a fixed height (see gif).

digitalboilingegg

However, my implementation is not entirely correct. In theory, the yellow box should be at the intersection of the blue lines, but this is not correct when moving on the mouse on the Y axis of the screen. I realize that this is because I’ve locked the camera’s Y position which causes a slight skew, but I am unsure what the best way to rectify this is. See the following image, where the cursor is placed at the bottom of the screen but the yellow box is not at the intersection of the thin blue lines.

Zoomed in:

6892928--806207--upload_2021-3-2_9-53-3.png

These are my cinemachine settings:

Furthermore, I have attached two scripts to my camera:

using UnityEngine;

public class CameraController : MonoBehaviour
{
    [Header("General")]
    [SerializeField]
    float minPlayerScreenPositionX;

    [SerializeField]
    float maxPlayerScreenPositionX;

    [SerializeField]
    float minPlayerScreenPositionY;

    [SerializeField]
    float maxPlayerScreenPositionY;

    [Header("References")]
    [SerializeField]
    Cinemachine.CinemachineVirtualCamera virtualCamera;

    // Cached variables
    Cinemachine.CinemachineFramingTransposer transposer;

    // Start is called before the first frame update
    void Start()
    {
        transposer = virtualCamera.GetCinemachineComponent<Cinemachine.CinemachineFramingTransposer>();
    }

    // Update is called once per frame
    void Update()
    {
        updateCameraPositionBasedOnMousePosition();
    }

    void updateCameraPositionBasedOnMousePosition()
    {
        Vector3 mousePos = Input.mousePosition;

        transposer.m_ScreenX = Mathf.Lerp(minPlayerScreenPositionX, maxPlayerScreenPositionX, Mathf.Clamp((Screen.width - mousePos.x) / Screen.width, 0f, 1f));
        transposer.m_ScreenY = Mathf.Lerp(minPlayerScreenPositionY, maxPlayerScreenPositionY, Mathf.Clamp(mousePos.y / Screen.height, 0f, 1f));
    }
}

and

using UnityEngine;
using Cinemachine;

/// <summary>
/// An add-on module for Cinemachine Virtual Camera that locks the camera's Z co-ordinate
/// </summary>
[ExecuteInEditMode] [SaveDuringPlay] [AddComponentMenu("")] // Hide in menu
public class LockCameraY : CinemachineExtension
{
    float yPosition;

    protected override void Awake()
    {
        base.Awake();
        yPosition = transform.position.y;
    }

    protected override void PostPipelineStageCallback(CinemachineVirtualCameraBase vcam, CinemachineCore.Stage stage, ref CameraState state, float deltaTime)
    {
        if (stage == CinemachineCore.Stage.Body)
        {
            Vector3 pos = state.RawPosition;
            pos.y = yPosition;
            state.RawPosition = pos;
        }
    }
}

So essentially, I’m changing the screenX and screenY variables based on the position of the mouse, but fixing the camera’s Y axis such that the camera’s zoom level doesn’t get altered.

Any help would be greatly appreciated!

P.S. the min- and maxPlayerScreenPosition variables are set to 0.33 and 0.66 respectively.

The framing transposer gets the desired framing by moving the camera in space - not rotating it. If you lock the camera’s Y, then you’re preventing the framing transposer from being able to completely do its job.

Maybe a better approach would be to have the mouse control the position of an invisible game object that you use as a target for the vcam, instead of the player. That way, you can leave the Screen X/Y alone and just displace the invisible target to get the framing you want.

I understand that the camera is moved in space and not rotated, I was just hoping to be able to configure it to move only on the X and Z and not on the Y, to always maintain the same camera height while still moving around the player.

Good idea with the invisible game object! I’ll try that out.

1 Like