Search code examples
unity-game-enginecamera

Unity: Main Camera following player wants to be right side up


I have a player moving around a sphere and have my camera following the player. When I move past the poles at the absolute top or bottom of the sphere, the scene quickly spins around so that the up direction of the camera (and scene) remain at the top of the display. Is there a way to simply turn that off? I would rather the scene had no global concept of up or down, just relative up and down. If there isn't a simple way, is there a not so simple way to avoid the spinning? Here's my Update() code in case that helps. Thanks!

void Update()
    {
        Vector3 waypointTunnelDirection = Geometry.Scale(waypoints[waypointIndex] - transform.position, 0.01f);
        Vector3 waypointSurfaceStepPosition = Geometry.Scale(transform.position + waypointTunnelDirection, 0.5f);

        transform.position = Geometry.Scale(Vector3.MoveTowards(transform.position, waypointSurfaceStepPosition, maxSpeed), 0.5f);
        if (Vector3.Distance(transform.position, waypoints[waypointIndex]) < 0.01f) {
            waypointIndex = (waypointIndex + 1) % waypoints.Length;
        }

        mainCamera.transform.position = Geometry.Scale(transform.position, 2.0f);
        Vector3 targetDirection = transform.position - mainCamera.transform.position;
        mainCamera.transform.rotation = Quaternion.LookRotation(Vector3.RotateTowards(mainCamera.transform.forward, targetDirection, 99, 99));
    }

Solution

  • First of all put the camera control logic in the camera component's update function. By doing this you make your camera modular and so it becomes easier to reuse in another game for instance.

    Instead of using a sphere for your temporary player, use any object that has an obvious top and front for testing and debugging purposes. Then get your player to move around the world before working on the camera logic since the player orientation is necessary to properly orient the camera.

    To move the player forward simply move it in it's transform.forward and then reorient the players "up" with Quaternion.LookRotation:

    void Update()
    {
        transform.position = transform.position + transform.forward * Time.deltaTime * speed;
    
        Vector3 up = transform.position;     // Assuming the center of the sphere world is (0,0,0)
    
        Vector3 forward = Vector3.Cross(transform.right, up);
        transform.rotation = Quaternion.LookRotation(forward, up);
    }
    

    To Rotate your player, rotate it around its own transform.up Vector3:

    transform.RotateAround(Vector3.zero, transform.up, angularSpeed*Time.deltaTime);
    

    Now you can update your camera in it's update function:

    public class Camera : MonoBehavior
    {
        public GameObject player;
        public float camHeight;
        public float camDistance;
    
        void Update()
        {
            Transform pt = player.transform;
    
            // Set camera Position;
            Vector3 camOffset = -pt.forward * camDistance + pt.up * camHeight;
            transform.position = pt.position + camOffset;
    
            // Set camera orientation
            transform.LookAt(pt.position, pt.up);
        }
    }