I’ve looked through 20+ scripts including the Camera Scripts in the Standard Asset Package.
Most people that are looking for something like this all seem to be satisfied with transform.RotateAround…but you’re limited to 1 axis. I need 2 axes for a smooth orbit.
I think mouse orbit does use 2 axis. You probably have the wrong Standard assets package. The unity asset store should have the most recent. Just search for standard assets in the store.
There are ways to do it from scratch. Although its best to save work if you don’t have to. Its smart laziness.
No, this is intended for Building Visualization (or BIM) and so I’m trying to mimic the orbit controls found in those types of programs.
However, I got the code “working”…it achieves what I wanted…EXCEPT!..the camera flickers like crazy…not sure why…something about storing variables first? Here’s the code as is…I’ve identified the line that causes the flickering to happen. I will mark this as solved and start a new post to clean things up a bit.
public class new3rdPerson : MonoBehaviour {
public Transform lookAt;
private Vector3 currentMouse;
private Vector3 mouseDelta;
private Vector3 lastMouse;
private Vector3 cursorDirection;
// Use this for initialization
void Start ()
{
}
private void Update()
{
//Camera main is the transform
currentMouse = Input.mousePosition;
mouseDelta = lastMouse - currentMouse;
cursorDirection = lookAt.position - transform.position;
}
// Update is called once per frame
void LateUpdate ()
{
float z = transform.eulerAngles.z; //Keeps camera flat to x-z plane
Quaternion rotation = Quaternion.Euler(mouseDelta.y *.1f, mouseDelta.x * -.05f, -z);
transform.position = lookAt.position + rotation * cursorDirection; //this line is causing the flickering
transform.rotation = rotation * transform.rotation;
lastMouse = Input.mousePosition;
}
}
Nah, unstable math. I fixed the flickering but it’s still borked when the camera is looking up steeply.
I’d suggest simply copying the code I posted above. It does the same thing but without the flickering. Anyway here’s the semi-fixed version I have so far:
EDIT: Removed code. Check post below.
In your code, the camera looks at the target (and therefore centers it on the screen). I do not want that. The cursor location needs to serve as the pivot point of the orbit. So the farther the cursor is left or right of the center, the longer the radius of the pivot point to camera is.
EDIT: In the pursuit of simplicity, I left out a key piece of my code…which may have caused confusion earlier. Without it, the pivot point aspect doesn’t work. Below is the current full code (which still flickers)
public class new3rdPerson : MonoBehaviour {
public Transform lookAt; //A game object that moves to wherever the cursor clicks on screen
private Vector3 currentMouse;
private Vector3 mouseDelta;
private Vector3 lastMouse;
private Vector3 cursorDirection;
bool activeRotate = false;
// Use this for initialization
void Start ()
{
}
private void Update()
{
mouseDelta = lastMouse - currentMouse;
cursorDirection = lookAt.position - transform.position;
if (Input.GetKey(KeyCode.LeftShift))
{
activeRotate = true;
currentMouse = Input.mousePosition;
}
if (Input.GetKeyUp(KeyCode.LeftShift))
{
activeRotate = false;
}
currentMouse = Input.mousePosition;
mouseDelta = lastMouse - currentMouse;
}
// Update is called once per frame
void LateUpdate ()
{
if (activeRotate)
{
float z = transform.eulerAngles.z; //Keeps camera flat to x-z plane
Quaternion rotation = Quaternion.Euler(mouseDelta.y * .1f, mouseDelta.x * -.05f, -z);
transform.position = lookAt.position + rotation * cursorDirection; //this line is causing the flickering
transform.rotation = rotation * transform.rotation;
lastMouse = Input.mousePosition;
}
}
}
I got this effect by using the same script for two different axis. One axis is the base that rotates horizontally (horizontal Y) and the other gets parented to the base and rotates up and down (Vertical X)
using UnityEngine;
using System.Collections;
public class lookAroundOrbit: MonoBehaviour {
public float horizontalSpeed = 2.0F;
public float verticalSpeed = 2.0F;
void Update() {
if(Input.GetMouseButton (2)){
float h = horizontalSpeed * Input.GetAxis("Mouse X");
float v = verticalSpeed * Input.GetAxis("Mouse Y");
transform.Rotate(v, h, 0);
}
}
}
Setup the parenting hierarchy like this.
On orbitCenterY, keep vertical speed 0, on orbitCenterX, keep horizontal speed 0.
This is the most basic setup. I built off of it and added things like smoothing and making the view reset when the middle mouse is not being pressed down.
I will throw in my code which I was rocking for a small project.
It orbits a GameObject called target but this could easily be switched out for any Vector3.
Note also how its not the most optimised but hey it worked great for my use. It also doesnt suffer from the ‘tilt’ you experience.
This script goes on your camera.
using UnityEngine;
public class RotateCamera : MonoBehaviour
{
[SerializeField] GameObject target;
[Header("Speed")]
[SerializeField] float moveSpeed = 300f;
[SerializeField] float zoomSpeed = 100f;
[Header("Zoom")]
[SerializeField] float minDistance = 2f;
[SerializeField] float maxDistance = 5f;
void Update ()
{
CameraControl();
}
void CameraControl()
{
if (Input.GetMouseButton(0))
{
/* Rotate the camera using the input managers mouse axis */
transform.RotateAround(target.transform.position, Vector3.up, ((Input.GetAxisRaw("Mouse X") * Time.deltaTime) * moveSpeed));
transform.RotateAround(target.transform.position, transform.right, -((Input.GetAxisRaw("Mouse Y") * Time.deltaTime) * moveSpeed));
}
/* Zoom the camera */
ZoomCamera();
}
void ZoomCamera()
{
/* If we are already close enough for the min distance and we try to zoom in, dont, return instead */
/* Similarly for zooming out */
if (Vector3.Distance(transform.position, target.transform.position) <= minDistance && Input.GetAxis("Mouse ScrollWheel") > 0f) { return; }
if (Vector3.Distance(transform.position, target.transform.position) >= maxDistance && Input.GetAxis("Mouse ScrollWheel") < 0f) { return; }
/* Only move in the Z relative to the Camera (so forward and back) */
transform.Translate(
0f,
0f,
(Input.GetAxis("Mouse ScrollWheel") * Time.deltaTime) * zoomSpeed,
Space.Self
);
}
}
public class New3rdPerson : MonoBehaviour
{
public Transform pivot;
public float sensitivity = 3f;
public float distance = 5f;
float xRot = 0f;
float yRot = 0f;
Quaternion adjustmentRotation;
Quaternion rotation;
private void LateUpdate()
{
if(Input.GetMouseButtonDown(0))
{
// Set distance to the current distance of the target
distance = Vector3.Distance(transform.position, pivot.position);
// Set the x and y rotation to the new rotation relative to the pivot
Vector3 pivotToHere = transform.position - pivot.position;
Vector3 tempVec = Vector3.ProjectOnPlane(pivotToHere, Vector3.up);
if (pivotToHere.x > 0f)
yRot = Vector3.Angle(Vector3.forward, tempVec) + 180f;
else
yRot = -Vector3.Angle(Vector3.forward, tempVec) + 180f;
if(pivotToHere.y > 0f)
xRot = Vector3.Angle(tempVec, pivotToHere);
else
xRot = -Vector3.Angle(tempVec, pivotToHere);
rotation = Quaternion.Euler(xRot, yRot, 0);
adjustmentRotation = Quaternion.FromToRotation(rotation * Vector3.forward, transform.forward);
}
if (Input.GetMouseButton(0))
{
xRot -= Input.GetAxis("Mouse Y") * Time.deltaTime * sensitivity;
yRot += Input.GetAxis("Mouse X") * Time.deltaTime * sensitivity;
rotation = Quaternion.Euler(xRot, yRot, 0);
transform.position = pivot.position - rotation * (Vector3.forward * distance);
transform.rotation = Quaternion.LookRotation(adjustmentRotation* (rotation * Vector3.forward), Vector3.up);
}
}
}
Note I put the movement in LateUpdate so it’ll register the move of the pivot in case you’re setting the pivot position in Update and that script runs after the camera script, in which case it’ll bork your rotation.
The if/else statements for the x/y rotations are because the Vector3.Angle method returns the smallest angle between the two vectors.
The general idea is that you’re getting the rotation that makes the camera look at the pivot, then get a rotation from that rotation to the current rotation, which I call “adjustmentRotation”.
Then you rotate normally towards the pivot, and then add onto that the adjustment rotation.
It’s a lot of vector/quaternion math, but rotations are tricky buggers. It might be simpler to understand using the hierarchy method @roger0 posted above, because Unity will do all the local/global transformations for you, but I like little challenges like these, so that’s my input lol
the problem you’re having is from gimble lock and because you are using Euler and Angles calls instead of just Quaternion. Even worse, the tiny imperfections from the gimble and float percision are adding up across the frames which is causing the visible tilt.
Before you rotate around, inside the OnMouseButtonDown the save the from-to rotation which goes from looking at the pivot to the camera’s current foward.
var pivotDirection = pivotPosition - transform.postion;
//save the camera's rotation relative to the look-at-pivot rotation
offsetRotation = Quaternion.FromToRotation(pivotDirection, transform.forward);
now, while you are dragging, and after you’ve Rotated around the pivot this frame (which updated your position), you then focus on correcting the rotation.
//get the current direction to the pivot which we will use as the origin.
var pivotDirection= pivotPostion - transform.postion;
//we add in the offset rotation to find the rotation with the correct forward direction but the up direction might be tilted.
var targetLook = Quaternion.LookDirection(pivotDirection) * offsetRotation;
//build a new rotation using targetLook's correct foward combined with world Up as the up rotation
trasnform.rotation = Quaternion.LookRotation(Vector3.foward * targetLook, Vector3.up);
this way the rotations don’t build on itself. its always rebuilt on the current frame which should prevent visible tilt.
Thanks once more to everyone for their feedback and patience.
The first approach I tried worked…it was TaleOf4Gamers approach, which utilized 2 transform.RotateAround function calls.
I had tried this at the beginning, but was getting the tilt and thought Quat’s were the only way to go. However, I was using Vector.right as the X axis. If I change that to “transform.right” it works with no tilt!
I will work through everyone’s replies and see if they all achieve the same result.