Trying to have a cube face the screen

Here is my current implementation:

void RotateCubeTowardsCamera()
{
    // Calculate the direction from the cube to the camera
    Vector3 directionToCamera = mainCamera.transform.position - transform.position;

    // Create a rotation that looks in the direction of the camera
    Quaternion lookRotation = Quaternion.LookRotation(-directionToCamera, Vector3.up);

    // Set the cube's rotation to the calculated rotation
    transform.rotation = lookRotation;
}

My issue is that it’s very jittery, I think it might be conflicting with something but I have no idea what is could be conflicting with. Or maybe this is a poor implementation. This is currently running in the update loop. The object this script is attached to has no parents. Everything else movement related is operating in fixed update or update.

Here is a video of what I mean.
https://youtu.be/t6SH8l7EYUk

Try it in good old void LateUpdate()

1 Like

You’re setting the cube’s rotation before setting its position and so the rotation is always a step/frame behind.

Either call the rotation function after positioning the cube or simply just make the cube a child of the camera and then you won’t need to script it…

1 Like

nah that doesn’t seem to work either

I did try to make the cube a child of the camera actually, it became an issue because of it’s position since I am using a cinemachine freelook camera (guess I didn’t specify that in OP, oops). The cube would just move in a circle by a specifiied offset, here is a video of what I mean:

https://youtu.be/2NP6dMbUqlk

maybe I just don’t know how to stop transforms like that but I assume that any further transform modifications would just conflict with the hierarchy and cause more jittering. Maybe I’m wrong though. Also I intend for have mild transforms of this cube when moving like the video below and like I said before I assume it would just jitter due to transform conflicts, but again maybe I’m wrong:

https://youtu.be/1GbpbzDPkRI

also just tried setting the function in after the cube position function here:

void UpdateCubePosition()
{
    if (anchorObject == null) return; // Exit if no anchor object is assigned

    // Convert the screen position to a world position relative to the camera
    Vector3 targetWorldPosition = mainCamera.ViewportToWorldPoint(screenPosition);

    // Calculate the offset from the anchorObject to the target position
    Vector3 offset = targetWorldPosition - mainCamera.transform.position;

    // Move the cube to maintain its position relative to the anchorObject
    transform.position = anchorObject.position + offset;
}

didn’t work unfortunatly

maybe this function is messing with it

Place the script below onto a default cube and it will place itself ahead of the camera. Be sure to set the mainCamera reference.

using UnityEngine;
public class HUD : MonoBehaviour
{
public Transform mainCamera;

	void LateUpdate()
	{
		transform.position=mainCamera.position+mainCamera.forward*3;
		transform.rotation=mainCamera.rotation;
	}
}

nah that didn’t work either, my OP implied this but I didn’t specify that camera operates in fixedUpdate, if it was in late update it would be less severe but still wouldn’t fix the issue unfortunately. also no I will not move the brain to lateUpdate I like the camera damping that comes with the freelook camera and if I changed it to late update then simply moving around would look terrible since my character also moves in fixedUpdate. here’s another video of what I mean

yea putting the function you gave me in fixed update does decrease some of the lagging, but it’s not completely smooth yet. it does look better now though

FixedUpdate won’t be smooth unless it’s set to update at the same rate as your monitor which isn’t advisable if you’re using a high refresh rate monitor.

It seems your issue is more with CinemaMachine than anything. I’ve never used it myself and so I can’t really help you there.

I see, guess I’ll have to make a custom camera script then, that sound annoying but I guess I can figure it out. Thanks.

Sorry for the late reply, I’ve been having some issues getting notifications.

The problem is that whether you use LateUpdate or FixedUpdate, you are using a stale camera position from the previous frame.

You must position your cube after Cinemachine has updated the camera transform. This happens very late in the frame, in CinemachineBrain.LateUpdate, and CinemachineBrain has a late execution order. The simplest way is to hook into CinemachineCore.CameraUpdatedEvent, which is fired immediately after the camera has been positioned. At that point your code can use the camera position to place the cube.

Thank you for the reply, it’s very much appreciated. I should probably update how this has went so far. For the position I just made it a child of the player since I don’t plan for it’s position to be different from how it already is. The main goal here is essentially an “aiming reticle” (or in this case an aiming polyhedron) where an enemy inside is targeted for an auto aim gun. So the this case the Object (Fire Control System or FCS) should always be infront of the player an should copy the position transforms of the player and the rotation transfroms of the camera. The position and left and right rotation is simply handled by making the FCS a child of the player. The left and right (or I guess horizontal relative to the camera) rotation is based on a function that matches the player’s rotation to the camera so that the player is always facing where the camera is horizontally (for whatever reason this does not give me an issue). I did that to make a new script to get the vertical rotation working but like before it is lagging for the reason you explained. Here is the script I’m working on that still has this issue.

using Cinemachine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CamTargetRotate : MonoBehaviour
{
    // Reference to the camera whose X rotation you want to match
    [SerializeField] private CinemachineFreeLook targetCamera;
    [SerializeField] private float TestValue;
    [SerializeField] private float cameraXRotation;

    public float rotateRange = 180f;

    public float rotateMax = 67f;
    public float rotateMin = -54f;
    private float newRotateRange;
    private float newRotateMax;
    private float newRotateMin;
    public float againRotateMax;
    public float againRotateMin;

    private float previousValue;

    private void Start()
    {
        newRotateMax = 2f * rotateMax;
        newRotateMin = -2f * rotateMin;

        //againRotateMax = (rotateMax / 90f);
        //againRotateMin = (rotateMin / -90f);
    }

    // Update is called once per frame
    void Update()
    {
        // Get the current Y-axis value
        float TestValue = targetCamera.m_YAxis.Value;
        MatchCameraRotation();
    }

    void MatchCameraRotation()
    {
        if (targetCamera == null)
        {
            Debug.LogError("Target camera is not assigned.");
            return;
        }
       
        //Changes the rotation max/min depending on whether the camera is above or below the middle camera value
        if(targetCamera.m_YAxis.Value >= 0.5)
        {
            cameraXRotation = getThingRotation(newRotateMax);
        }
        else
        {
            cameraXRotation = getThingRotation(newRotateMin);
        }
        
        

        // Get the camera's current X rotation (pitch) value
        //cameraXRotation = ((targetCamera.m_YAxis.Value - 0.5f) * rotateRange);
        //these two stop the weird reflection each frame
        if (cameraXRotation < -90)
        {
            cameraXRotation += 180;
            cameraXRotation = -cameraXRotation;
        }
        if (cameraXRotation > 90)
        {
            cameraXRotation -= 180;
            cameraXRotation = -cameraXRotation;
        }

        //experimental thing here
        /*
        if (cameraXRotation >= 0 && cameraXRotation < 90)
        {
            cameraXRotation *= againRotateMax;
        }
        if ( cameraXRotation < 0 && cameraXRotation > -90)
        {
            cameraXRotation *= againRotateMin;
        }
        */

        //these two set a max/min rotate value
        if(cameraXRotation < rotateMin)
        {
            cameraXRotation = rotateMin;
        }
        if(cameraXRotation > rotateMax)
        {
            cameraXRotation = rotateMax;
        }
        


        // Apply the camera's X rotation to the object, keeping other axis the same
        transform.localEulerAngles = new Vector3(cameraXRotation, transform.localEulerAngles.y, transform.localEulerAngles.z);
    }

    public float getThingRotation(float MaxMin)
    {
        return ((targetCamera.m_YAxis.Value - 0.5f) * MaxMin); 
    }
}

Now I tried for a bit to use the implementation you recommended but I’m having trouble understanding what you mean I guess. Here’s my attempt at achieving what you recommended:

private void Start()
{
    newRotateMax = 2f * rotateMax;
    newRotateMin = -2f * rotateMin;

    // Subscribe to the Cinemachine camera update event to update the cube's rotation after the camera has been updated
    CinemachineCore.CameraUpdatedEvent.AddListener(OnCameraUpdated);

    //againRotateMax = (rotateMax / 90f);
    //againRotateMin = (rotateMin / -90f);
}

// Unsubscribe from the event when the object is destroyed to avoid memory leaks
private void OnDestroy()
{
    CinemachineCore.CameraUpdatedEvent.RemoveListener(OnCameraUpdated);
}

// Called after the Cinemachine camera has been updated
private void OnCameraUpdated(CinemachineBrain brain)
{
    // Ensure the update is only applied when the correct camera is the active camera in the brain
    // Casting to ICinemachineCamera to avoid the warning and ensure proper reference comparison
    if (brain == null || brain.ActiveVirtualCamera != (ICinemachineCamera)targetCamera)
    {
        return;
    }

    // Get the current Y-axis value
    float TestValue = targetCamera.m_YAxis.Value;
    // The rotation update is now handled in OnCameraUpdated, so no need to call MatchCameraRotation here

    // Update the cube's rotation
    MatchCameraRotation();
}

I’m sure I’m just doing something wrong somewhere because this is giving the same kind of laggy response as before. I can send a video of what it looks like if you need it. Anyways thanks again for responding even if it was pretty long after the original post. And thanks if you have read this far as well.

Your code to extract the vertical rotation from the FreeLook appears sketchy to me. Why not take a different approach? I suggest this: in OnCameraUpdated, set your cube’s rotation to brain.OutputCamera.transform.rotation. That will exactly match what the camera is doing, and even if you later replace the FreeLook with a different style of camera, it will continue to work.

Have you considered upgrading to CM3? The samples there include some nice code for positioning aiming reticles.

1 Like

Ah, didn’t know you could make a reference to the brain like that. When creating that script I was assuming that I could only access values from the objects components, hence the jury-rigged nature of it. Anyways it works perfectly now. Thanks a lot!!

I assume this requires me to upgrade my version of unity to a later version? I started working in this current version because I never bothered to update it in 2 years since I installed it and I’m not sure if Unity 6 is worth updating to. Does CM3 (and the engine in general) have any notable features/improvements compared to the one I’m currently using aside from the samples?

CM3 works with Unity 2022.3 LTS and up.

Upgrading CM is not trivial, and if you have scripts that reference the Cinemachine API they will need to be modified. You can see the upgrade guide here: Upgrading a Project from Cinemachine 2.X | Cinemachine | 3.1.2

CM3 comes with some nice samples and is generally easier to work with. Event handling is better. If you’re happy with CM2 you can stick with it, we’ll be supporting it for a while.

1 Like