简体   繁体   中英

Rotating camera around sphere in Unity/C# using arrow keys

I am currently learning Unity and have been stuck for a while on a project I am doing.

I have a Mars model that is given torque to rotate and has two moons orbiting it. I want to be able to move the camera around it using the arrow keys but I cant seem to figure it out.

Current code (all in a single script called GameManager):

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

public class GameManagerScript : MonoBehaviour
{
    // Start is called before the first frame update
    public GameObject mars;//planet
    public GameObject phobos;//moon1
    public GameObject deimos;//moon2
    public GameObject refpoint;//empty game object I placed inside mars
    //All object are referenced using the inspector

    void Start()
    {
        Camera.main.transform.position = new Vector3(0,0,-200);

        Camera.main.transform.LookAt(mars.transform.position);

        Rigidbody rb = mars.GetComponent<Rigidbody>();
        rb.angularVelocity = new Vector3(0,20,0);
    }

    // Update is called once per frame
    void Update()
    {

        if(Input.GetKey("up")){
            Camera.main.transform.transform.RotateAround(refpoint.transform.position, Vector3.right, 50* Time.deltaTime);
        }
        else if(Input.GetKey("down")){
            Camera.main.transform.transform.RotateAround(refpoint.transform.position, Vector3.right, -50* Time.deltaTime);
        }else if(Input.GetKey("right")){
            Camera.main.transform.transform.RotateAround(refpoint.transform.position, Vector3.up, -50* Time.deltaTime);
        }else if(Input.GetKey("left")){
            Camera.main.transform.transform.RotateAround(refpoint.transform.position, Vector3.up, 50* Time.deltaTime);
        }

        phobos.transform.RotateAround(mars.transform.position, phobos.transform.up, 60*Time.deltaTime);
        deimos.transform.RotateAround(mars.transform.position, deimos.transform.up, 50*Time.deltaTime);

    }
}

Initially this works fine, but the directions start getting confused once you use left/right after up/down or vice versa.

Any help appreciated.

Maybe it would be better if you defined another function for getting a new Vector for the rotation, like this:

Vector3 CalculateRotationVector(){
    Vector3 RotationVector = Vector3.zero;

    if(Input.GetKey("up")){
        RotationVector += Vector3.right;
    }
    else if(Input.GetKey("down")){
        RotationVector -= Vector3.right;
    }else if(Input.GetKey("right")){
        RotationVector -= Vector3.right;
    }else if(Input.GetKey("left")){
        RotationVector += Vector3.right;
    }

    return RotationVector;

}

And in your update function you may call it like this:

void Update(){
    Camera.main.transform.RotateAround(refpoint.transform.position, CalculateRotationVector(), 50* Time.deltaTime);
}

However I'm not really sure if this will work since I can't try this on unity right now, might give you an idea.

You don't always want to rotate around the global right axis. You can use a cross product between global down and the direction from the reference point to the camera to find a suitable axis.

This logic requires that you prevent the camera from going directly above or below the planet. Clamp the vertical rotation based on the angle between global up/down and the direction from the reference to the camera and some "minimum angle" the camera can be from the up/down axis.

Also, Camera.main calls FindGameObjectsWithTag internally, so it is recommended to cache the results.

Altogether, this might look like this:

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

public class GameManagerScript : MonoBehaviour
{
    // Start is called before the first frame update
    public GameObject mars;//planet
    public GameObject phobos;//moon1
    public GameObject deimos;//moon2
    public GameObject refpoint;//empty game object I placed inside mars
    //All object are referenced using the inspector

    private Camera mainCam;
    private float cameraRotateSpeed = 50f;
    private float cameraMinVerticalAngle = 10f;

    void Start()
    {
        mainCam = Camera.main;

        mainCam.transform.position = new Vector3(0,0,-200);

        mainCam.transform.LookAt(mars.transform.position);

        Rigidbody rb = mars.GetComponent<Rigidbody>();
        rb.angularVelocity = new Vector3(0,20,0);
    }

    // Update is called once per frame
    void Update()
    {

        float horizontalRotateDirection = 0f;
        float verticalRotateDirection = 0f;


        if(Input.GetKey("up")){
            doRotate = true;
            verticalRotateDirection += 1f;
        }

        if(Input.GetKey("down")){
            doRotate = true;
            verticalRotateDirection -= 1f;
        }

        if(Input.GetKey("right")){
            doRotate = true;
            horizontalRotateDirection += 1f;
        }

        if(Input.GetKey("left")){
            doRotate = true;
            verticalRotateDirection -= 1f;
        }

        float horizontalRotateAmount = horizontalRotateDirection 
                * cameraRotateSpeed * Time.deltaTime;

        float verticalRotateAmount = verticalRotateDirection 
                * cameraRotateSpeed * Time.deltaTime;

        // Prevent camera from flipping
        Vector3 fromRefToCam = (mainCam.transform.position - refpoint.transform.position).normalized;

        float maxRotateDown = Vector3.Angle(Vector3.down, fromRefToCam);
        float maxRotateUp = Vector3.Angle(Vector3.up, fromRefToCam)

        verticalRotateAmount = Mathf.Clamp(verticalRotateAmount, 
                cameraMinVerticalAngle - maxRotateDown, 
                maxRotateUp - cameraMinVerticalAngle);

        Vector3 verticalRotateAxis = Vector3.Cross(Vector3.down, fromRefToCam); 

        //Rotate horizontally around global down
        mainCam.transform.RotateAround(refpoint.transform.position, 
                    Vector3.up, 
                    horizontalRotateAmount);

        // Rotate vertically around camera's right (in global space)
        mainCam.transform.RotateAround(refpoint.transform.position,
                    verticalRotateAxis, 
                    verticalRotateAmount);


        phobos.transform.RotateAround(mars.transform.position, phobos.transform.up, 
                60*Time.deltaTime);
        deimos.transform.RotateAround(mars.transform.position, deimos.transform.up, 
                50*Time.deltaTime);

    }
}

I believe RotateAround here is a mistake. With the other suggested solutions you would rapidly get lost in the rotation, with the camera ending up upside down or even sideways.

public class GameManagerScript : MonoBehaviour
{
    // Start is called before the first frame update
    public GameObject mars;//planet
    public GameObject phobos;//moon1
    public GameObject deimos;//moon2
    public GameObject refpoint;//empty game object I placed inside mars
    //All object are referenced using the inspector

    public float cameraAngularVelocity = 60f;
    public float cameraDistance = 200;
    public float cameraAngleY = 0;
    public float cameraAngleX = 0;

    private Camera mainCam;

    void Start()
    {
        mainCam = Camera.main;
    }

    void Update()
    {
        float angleDelta = cameraAngularVelocity * Time.deltaTime;

        //Standard Input management
        if (Input.GetKey("up"))
        {
            cameraAngleX += angleDelta;
        }
        if (Input.GetKey("down"))
        {
            cameraAngleX -= angleDelta;
        }
        if (Input.GetKey("right"))
        {
            cameraAngleY -= angleDelta;
        }
        if (Input.GetKey("left"))
        {
            cameraAngleY += angleDelta;
        }
        //Alternative using axis
        cameraAngleX += Input.GetAxis("Vertical") * angleDelta;
        cameraAngleY += Input.GetAxis("Horizontal") * angleDelta;

        //Protections
        cameraAngleX = Mathf.Clamp(cameraAngleX, -90f, 90f);
        cameraAngleY = Mathf.Repeat(cameraAngleY, 360f);

        Quaternion cameraRotation =
            Quaternion.AngleAxis(cameraAngleY, Vector3.up)
            * Quaternion.AngleAxis(cameraAngleX, Vector3.right);

        Vector3 cameraPosition =
            refpoint.transform.position
            + cameraRotation * Vector3.back * cameraDistance;

        mainCam.transform.position = cameraPosition;
        mainCam.transform.rotation = cameraRotation;

        phobos.transform.RotateAround(mars.transform.position, phobos.transform.up, 60 * Time.deltaTime);
        deimos.transform.RotateAround(mars.transform.position, deimos.transform.up, 50 * Time.deltaTime);
    }
}

This solution locks your roll, and prevents you passing the vertical through the X rotation.

Have a look at this.

https://docs.unity3d.com/ScriptReference/Transform.RotateAround.html

With this you can rotate around a given point. If you combine this with key input and a constant speed multiplied by Time.deltaTime this might work

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM