Reputation: 509
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.
Upvotes: 2
Views: 2148
Reputation: 350
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
Upvotes: -2
Reputation: 20249
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);
}
}
Upvotes: 0
Reputation: 635
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.
Upvotes: 4
Reputation: 435
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.
Upvotes: 2