Rotate Around a moving Object with constant Offset

I’ve been trying to figure this out for ages now, but there’s too many moving parts for me to make sense of the solution.

I have an Isometric Camera, which needs to always be offset from the Player position by a constant amount. The Camera needs to be able to rotate around the player in 90 degree set increments when a button is pressed, and this all needs to be maintained whilst the player is moving.

I haven’t been able to get all aspects working at the same time though. If I try setting the offset every frame to maintain it, then the Pivot Rotation doesn’t work as it cannot change it’s transform during the rotation. If I get it to rotate, then the offset changes due to the rotation so the camera faces the wrong way.

Can anyone offer a solution to this? This is my code so far:

void Start()
    {
            SetDynamicCameraPivotPoint();
            playerCameraOffset = transform.position - PlayerController.instance.transform.position;
    }

void Update()
    {
        if (activated && LevelController.instance.levelActive && !LevelController.instance.paused)
        {
            if (cameraType == CameraType.DYNAMIC)
            {
                UpdateDynamicCamera();
            }

            if (InputManager.GetKeyDown(InputManager.Controls.CameraRight) && !InputManager.GetKeyDown(InputManager.Controls.CameraLeft) && !rotating)
            {
                StartCoroutine(RotateAroundPivot(-1));
            }

            else if (InputManager.GetKeyDown(InputManager.Controls.CameraLeft) && !InputManager.GetKeyDown(InputManager.Controls.CameraRight) && !rotating)
            {
                StartCoroutine(RotateAroundPivot(1));
            }
        }
    }

private void UpdateDynamicCamera()
    {
        SetDynamicCameraPivotPoint();
        transform.position = new Vector3(
            PlayerController.instance.transform.position.x + playerCameraOffset.x,
            PlayerController.instance.transform.position.y + playerCameraOffset.y,
            PlayerController.instance.transform.position.z + playerCameraOffset.z);
    }

    private void SetDynamicCameraPivotPoint()
    {
        pivotPoint = new Vector3(PlayerController.instance.transform.position.x, cam.transform.position.y, PlayerController.instance.transform.position.z);
    }

private IEnumerator RotateAroundPivot(int direction)
    {
        targetRotation = transform.rotation.eulerAngles.y + (90f * direction);

        cameraSound.Play();

        rotating = true;
        float rotatedDegrees = 0;

        while (rotatedDegrees <= 90f)
        {
            float anglePerFrame = 90 * Time.deltaTime;

            transform.RotateAround(pivotPoint, Vector3.up * direction, anglePerFrame);

            rotatedDegrees += anglePerFrame;
            yield return new WaitForEndOfFrame();
        }

        transform.eulerAngles = new Vector3(transform.rotation.eulerAngles.x, targetRotation, transform.rotation.eulerAngles.z);
        transform.position = new Vector3(truncateFloat(transform.position.x), truncateFloat(transform.position.y), truncateFloat(transform.position.z));
        rotating = false;
    }

I feel as though you are trying to do a lot of the 3D maths that the transform hierarchy can do automatically for you. You’ll have a far better time using a dolly/boom camera setup.

By this I mean that you have your camera “dolly” object following your player’s position then you can create your camera’s offset by using a child “boom” object, with the camera attached to or as a child of this. I’ve used these terms as they’re what real-life camera operators use - https://bit.ly/3jbGY8U, the dolly is the base and the boom arm holds the camera at a distance.

With this setup in unity you can rotate your dolly object to have your camera rotate around your player at your fixed offset and not have to worry about calculating positions in 3D space.
This shows a super simple example: Boom hosted at ImgBB — ImgBB

The following code demonstrates this behaviour but I expect you’ll want to fit this setup into your own camera design so use it just to understand how it works.

public class DollyCamera : MonoBehaviour
{
    [SerializeField] private Transform _player;
    [SerializeField] private Transform _dolly;
    [Space(15)]
    [SerializeField] private float _maxSpeed = 50f;
    [SerializeField] private float _targetAngle = 0f;

    private void Update()
    {
        UpdateDollyPosition();

        UpdateDollyRotation();
    }

    private void UpdateDollyPosition()
    {
        // the dolly object follows the player
        // you could add some damping here if you don't like how rigidly it follows the player
        _dolly.transform.position = _player.position;
    }

    private void UpdateDollyRotation()
    {
        Vector3 dollyEulerAngles = _dolly.eulerAngles;
        
        // use the target angle to get the target orientation
        Quaternion targetRotation = Quaternion.Euler(dollyEulerAngles.x, _targetAngle, dollyEulerAngles.z);
        
        // rotate towards this target orientation using a maximum rotation speed
        _dolly.rotation = Quaternion.RotateTowards(_dolly.rotation, targetRotation, _maxSpeed * Time.deltaTime);
    }
}

Hope this helps and let me know if you have any questions about this :slight_smile: