Unity Quick-tip: Billboarding with Cinemachine

When building your 3D game in Unity, you might have the need to add some UI elements in world space. Maybe it’s an arrow indicating the active pawn or a health bar for each of the enemies, or some interaction hits for the user.

Unity Quick-tip: Billboarding with Cinemachine
Photo by Pawel Czerwinski / Unsplash

When building your 3D game in Unity, you might have the need to add some UI elements in world space. Maybe it’s an arrow indicating the active pawn or a health bar for each of the enemies, or some interaction hits for the user. In these cases, typically you will want the game objects to always face the camera. This technique is called billboarding.

Screenshot Illustrating World Space UI without Billboarding
Screenshot Illustrating World Space UI without Billboarding

It’s a fairly straight forward technique with the default Unity Camera. You simply set the transform of the object to match that of the camera every frame.

public class Billboard : MonoBehaviour { 
  private void Update() { 
    transform.forward = Camera.main.transform.forward; 
  }
}
Screenshot Illustrating World Space UI without Billboarding
Screenshot Illustrating World Space UI with Billboarding

When using Cinemachine, it can be a little more complex. Cinemachine cameras do their update work in the LateUpdate() event function. Using a technique like above will result in your billboarding effect happening a frame behind each camera update, causing jittering and a potentially bad experience for the player.

One potential fix is to just change your Update event function to a LateUpdate. This may fix the problem now, but if Cinemachine changes in the future, you may still run into issues.

There’s a better way. Cinemachine triggers an event CameraUpdateEvent after it updates. By listening for this event, you can be sure your objects transform always updates after the camera updates.

public class CinemachineBillboard : MonoBehaviour
{
    private Transform _cameraTransform;
    
    private void Awake()
    {
        _cameraTransform = Camera.main.transform;
    }

    private void OnEnable()
    {
        CinemachineCore.CameraUpdatedEvent.AddListener(OnCameraUpdated);
    }

    private void OnDisable()
    {
        CinemachineCore.CameraUpdatedEvent.RemoveListener(OnCameraUpdated);
    }

    void OnCameraUpdated(CinemachineBrain brain)
    {
        transform.forward = _cameraTransform.forward;
    }
}

Hope this quick Unity tip was helpful. Let me know in the comments if you want to see more content like this.