Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Camera Orbit Around World Pivot Point Where Camera position and rotation Orbit together

Discussion in 'Scripting' started by macros976, Jun 27, 2017.

  1. macros976

    macros976

    Joined:
    Jun 22, 2017
    Posts:
    20
    I've been searching for days and haven't quite found what I'm looking for...

    I have a GameObject (target) that positions itself in world space based on a cursor location.

    This GameObject is linked to a Camera.

    If orbit is active, I want to orbit around that point...wherever it is on the screen.

    This is almost achieved using 2 transform.rotateAround Functions in a row:

    Code (CSharp):
    1.  
    2. transform.RotateAround(target.position, Vector3.up, mouseDelta.x * .05f);
    3. transform.RotateAround(target.position, Vector3.left, mouseDelta.y *01f);
    4.  
    However, this eventually tilts the camera off the X-Z plane and won't work

    With a quaternion, I can achieve the correct orbit of the camera's position, but the camera rotation (frustum/field of view doesn't change)

    Code (CSharp):
    1.  
    2. Vector3 direction = target.position - transform.position;
    3.  
    4. xEuler = mouseDelta.x * .05f;
    5. yEuler = (mouseDelta.y) * .1f;
    6.  
    7. transform.position = transform.position + direction;
    8.  
    9. Quaternion qt = Quaternion.Euler(yEuler, -xEuler, 0);
    10.  
    11. transform.rotation = Quaternion.Lerp(transform.rotation, qt, Time.deltaTime * 4);
    12.  
    13. transform.position = qt * (transform.position - direction);
    14.  
    If I add:
    Code (CSharp):
    1. transform.LookAt(target);
    2.  
    it ruins it by centering the target in the camera's field of view

    I'm stuck on how to get the camera's rotation (perspective/frustum) to also orbit around the pivot point
     
  2. roger0

    roger0

    Joined:
    Feb 3, 2012
    Posts:
    1,208
    There is a script that comes with Unity called Mouse Orbit that does this. Do you know about it? Or are you looking for something more?
     
  3. macros976

    macros976

    Joined:
    Jun 22, 2017
    Posts:
    20
    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.
     
  4. roger0

    roger0

    Joined:
    Feb 3, 2012
    Posts:
    1,208
    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.
     
  5. Scabbage

    Scabbage

    Joined:
    Dec 11, 2014
    Posts:
    268
    Never liked using stuff from SA, they're always large and never quite what I want.

    Is this not what you're after?
    Code (CSharp):
    1.  
    2.     public float xRot = 0f;
    3.     public float yRot = 0f;
    4.  
    5.     public float distance = 5f;
    6.     public float sensitivity = 1000f;
    7.     public Transform target;
    8.  
    9.     void Update()
    10.     {
    11.         xRot += Input.GetAxis("Mouse Y") * sensitivity * Time.deltaTime;
    12.         yRot += Input.GetAxis("Mouse X") * sensitivity * Time.deltaTime;
    13.  
    14.         if(xRot > 90f)
    15.         {
    16.             xRot = 90f;
    17.         }else if(xRot < -90f)
    18.         {
    19.             xRot = -90f;
    20.         }
    21.  
    22.         transform.position = target.position + Quaternion.Euler(xRot, yRot, 0f) * (distance * -Vector3.back);
    23.         transform.LookAt(target.position, Vector3.up);
    24.     }
    You said your code was "ruined" by keeping the target in centre frame, isn't that what you want? Is this a 2D game?
     
    Last edited: Jun 29, 2017
  6. macros976

    macros976

    Joined:
    Jun 22, 2017
    Posts:
    20
    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.

    Code (CSharp):
    1. public class new3rdPerson : MonoBehaviour {
    2.  
    3.     public Transform lookAt;
    4.  
    5.     private Vector3 currentMouse;
    6.     private Vector3 mouseDelta;
    7.     private Vector3 lastMouse;
    8.     private Vector3 cursorDirection;
    9.  
    10.     // Use this for initialization
    11.     void Start ()
    12.     {
    13.              
    14.     }
    15.    
    16.     private void Update()
    17.     {
    18.         //Camera main is the transform
    19.  
    20.         currentMouse = Input.mousePosition;
    21.  
    22.         mouseDelta = lastMouse - currentMouse;
    23.  
    24.         cursorDirection = lookAt.position - transform.position;
    25.     }
    26.  
    27.     // Update is called once per frame
    28.     void LateUpdate ()
    29.     {
    30.  
    31.         float z = transform.eulerAngles.z; //Keeps camera flat to x-z plane
    32.  
    33.         Quaternion rotation = Quaternion.Euler(mouseDelta.y *.1f, mouseDelta.x * -.05f, -z);
    34.  
    35.         transform.position = lookAt.position + rotation * cursorDirection; //this line is causing the flickering
    36.         transform.rotation = rotation * transform.rotation;
    37.        
    38.              
    39.         lastMouse = Input.mousePosition;
    40.     }
    41. }
     
  7. Scabbage

    Scabbage

    Joined:
    Dec 11, 2014
    Posts:
    268
    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.
     
    Last edited: Jun 29, 2017
  8. Scabbage

    Scabbage

    Joined:
    Dec 11, 2014
    Posts:
    268
    Fixed it, and simplified a bit.

    Code (CSharp):
    1.  
    2.     public Transform lookAt;
    3.  
    4.     private Vector3 currentMouse;
    5.     private Vector3 cursorDirection;
    6.     public float sensitivity = 3f;
    7.     public float distance = 5f;
    8.  
    9.     private void Update()
    10.     {
    11.         currentMouse = Input.mousePosition * sensitivity;
    12.         currentMouse.y *= -1f;
    13.  
    14.         cursorDirection = lookAt.position - transform.position;
    15.  
    16.         Quaternion rotation = Quaternion.Euler(currentMouse.y * .1f, currentMouse.x * -.05f, 0);
    17.  
    18.         transform.position = lookAt.position - rotation * (Vector3.forward * distance);
    19.         transform.rotation = rotation;
    20.     }
    By the way, transform.LookAt can keep the camera flat to the xz plane by adding the world up variable Vector3.up like this:

    Code (CSharp):
    1. transform.LookAt(target.position, Vector3.up);
     
    JasonH_BMT likes this.
  9. macros976

    macros976

    Joined:
    Jun 22, 2017
    Posts:
    20
    Here's the issue:

    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)

    Code (CSharp):
    1. public class new3rdPerson : MonoBehaviour {
    2.  
    3.     public Transform lookAt; //A game object that moves to wherever the cursor clicks on screen
    4.  
    5.     private Vector3 currentMouse;
    6.     private Vector3 mouseDelta;
    7.     private Vector3 lastMouse;
    8.     private Vector3 cursorDirection;
    9.  
    10.     bool activeRotate = false;
    11.  
    12.     // Use this for initialization
    13.     void Start ()
    14.     {
    15.            
    16.     }
    17.  
    18.     private void Update()
    19.     {
    20.  
    21.         mouseDelta = lastMouse - currentMouse;
    22.  
    23.         cursorDirection = lookAt.position - transform.position;
    24.  
    25.         if (Input.GetKey(KeyCode.LeftShift))
    26.         {
    27.             activeRotate = true;
    28.  
    29.             currentMouse = Input.mousePosition;
    30.         }
    31.  
    32.  
    33.         if (Input.GetKeyUp(KeyCode.LeftShift))
    34.         {
    35.             activeRotate = false;
    36.         }
    37.  
    38.        
    39.  
    40.         currentMouse = Input.mousePosition;
    41.  
    42.         mouseDelta = lastMouse - currentMouse;
    43.     }
    44.  
    45.     // Update is called once per frame
    46.     void LateUpdate ()
    47.     {
    48.  
    49.         if (activeRotate)
    50.         {
    51.             float z = transform.eulerAngles.z; //Keeps camera flat to x-z plane
    52.  
    53.             Quaternion rotation = Quaternion.Euler(mouseDelta.y * .1f, mouseDelta.x * -.05f, -z);
    54.  
    55.             transform.position = lookAt.position + rotation * cursorDirection; //this line is causing the flickering
    56.             transform.rotation = rotation * transform.rotation;
    57.  
    58.             lastMouse = Input.mousePosition;
    59.         }
    60.     }
    61. }
     
    Last edited: Jun 29, 2017
  10. Scabbage

    Scabbage

    Joined:
    Dec 11, 2014
    Posts:
    268
    Ok yea I'm definitely confused on what effect you're going for.

    The rotation in the script of my last post can be disabled just by commenting out this line:
    Code (CSharp):
    1. transform.rotation = rotation;
    Since the rotation is never changed (only position), it will never tilt it off the xz plane like in your original post.

    Could you post a video of what exactly you're aiming for?
     
  11. macros976

    macros976

    Joined:
    Jun 22, 2017
    Posts:
    20
    Sorry, I should've done this from post 1. Thanks for sticking with me on this! Below is a video of what I'm trying to do.

     
  12. macros976

    macros976

    Joined:
    Jun 22, 2017
    Posts:
    20
    Here's as far as I've gotten up to this point:

     
  13. roger0

    roger0

    Joined:
    Feb 3, 2012
    Posts:
    1,208
    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)

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class lookAroundOrbit: MonoBehaviour {
    5.  
    6.     public float horizontalSpeed = 2.0F;
    7.     public float verticalSpeed = 2.0F;
    8.  
    9.  
    10.     void Update() {
    11.  
    12.         if(Input.GetMouseButton (2)){
    13.         float h = horizontalSpeed * Input.GetAxis("Mouse X");
    14.         float v = verticalSpeed * Input.GetAxis("Mouse Y");
    15.  
    16.         transform.Rotate(v, h, 0);
    17.  
    18.  
    19.                 }
    20.             }
    21. }
    22.  
    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.
     
  14. TaleOf4Gamers

    TaleOf4Gamers

    Joined:
    Nov 15, 2013
    Posts:
    825
    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.
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class RotateCamera : MonoBehaviour
    4. {
    5.     [SerializeField] GameObject target;
    6.  
    7.     [Header("Speed")]
    8.     [SerializeField] float moveSpeed = 300f;
    9.     [SerializeField] float zoomSpeed = 100f;
    10.  
    11.     [Header("Zoom")]
    12.     [SerializeField] float minDistance = 2f;
    13.     [SerializeField] float maxDistance = 5f;
    14.  
    15.     void Update ()
    16.     {
    17.         CameraControl();
    18.     }
    19.  
    20.     void CameraControl()
    21.     {
    22.         if (Input.GetMouseButton(0))
    23.         {
    24.             /* Rotate the camera using the input managers mouse axis */
    25.             transform.RotateAround(target.transform.position, Vector3.up, ((Input.GetAxisRaw("Mouse X") * Time.deltaTime) * moveSpeed));
    26.             transform.RotateAround(target.transform.position, transform.right, -((Input.GetAxisRaw("Mouse Y") * Time.deltaTime) * moveSpeed));
    27.         }
    28.  
    29.         /* Zoom the camera */
    30.         ZoomCamera();
    31.     }
    32.  
    33.     void ZoomCamera()
    34.     {
    35.         /* If we are already close enough for the min distance and we try to zoom in, dont, return instead */
    36.         /* Similarly for zooming out */
    37.         if (Vector3.Distance(transform.position, target.transform.position) <= minDistance && Input.GetAxis("Mouse ScrollWheel") > 0f) { return; }
    38.         if (Vector3.Distance(transform.position, target.transform.position) >= maxDistance && Input.GetAxis("Mouse ScrollWheel") < 0f) { return; }
    39.  
    40.         /* Only move in the Z relative to the Camera (so forward and back) */
    41.         transform.Translate(
    42.             0f,
    43.             0f,
    44.             (Input.GetAxis("Mouse ScrollWheel") * Time.deltaTime) * zoomSpeed,
    45.             Space.Self
    46.         );
    47.     }  
    48. }
     
  15. Scabbage

    Scabbage

    Joined:
    Dec 11, 2014
    Posts:
    268
    Ah! I see now. I have a general idea for how to do this, but I'll have to think about it a bit.
     
  16. Scabbage

    Scabbage

    Joined:
    Dec 11, 2014
    Posts:
    268
    Right, I think this is what you're looking for:
    Code (CSharp):
    1.  
    2. public class New3rdPerson : MonoBehaviour
    3. {
    4.     public Transform pivot;
    5.     public float sensitivity = 3f;
    6.     public float distance = 5f;
    7.  
    8.     float xRot = 0f;
    9.     float yRot = 0f;
    10.  
    11.     Quaternion adjustmentRotation;
    12.     Quaternion rotation;
    13.  
    14.     private void LateUpdate()
    15.     {
    16.         if(Input.GetMouseButtonDown(0))
    17.         {
    18.             // Set distance to the current distance of the target
    19.             distance = Vector3.Distance(transform.position, pivot.position);
    20.  
    21.             // Set the x and y rotation to the new rotation relative to the pivot
    22.             Vector3 pivotToHere = transform.position - pivot.position;
    23.             Vector3 tempVec = Vector3.ProjectOnPlane(pivotToHere, Vector3.up);
    24.             if (pivotToHere.x > 0f)
    25.                 yRot = Vector3.Angle(Vector3.forward, tempVec) + 180f;
    26.             else
    27.                 yRot = -Vector3.Angle(Vector3.forward, tempVec) + 180f;
    28.  
    29.             if(pivotToHere.y > 0f)
    30.                 xRot = Vector3.Angle(tempVec, pivotToHere);
    31.             else
    32.                 xRot = -Vector3.Angle(tempVec, pivotToHere);
    33.  
    34.             rotation = Quaternion.Euler(xRot, yRot, 0);
    35.             adjustmentRotation = Quaternion.FromToRotation(rotation * Vector3.forward, transform.forward);
    36.          
    37.         }
    38.  
    39.         if (Input.GetMouseButton(0))
    40.         {
    41.             xRot -= Input.GetAxis("Mouse Y") * Time.deltaTime * sensitivity;
    42.             yRot += Input.GetAxis("Mouse X") * Time.deltaTime * sensitivity;
    43.  
    44.             rotation = Quaternion.Euler(xRot, yRot, 0);
    45.  
    46.             transform.position = pivot.position - rotation * (Vector3.forward * distance);
    47.  
    48.             transform.rotation = Quaternion.LookRotation(adjustmentRotation* (rotation * Vector3.forward), Vector3.up);
    49.         }
    50.     }
    51. }
    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
     
  17. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    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.
    Code (CSharp):
    1.  
    2. var pivotDirection = pivotPosition - transform.postion;
    3.  
    4. //save the camera's rotation relative to the look-at-pivot rotation
    5. 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.
    Code (CSharp):
    1.  
    2. //get the current direction to the pivot which we will use as the origin.
    3. var pivotDirection= pivotPostion - transform.postion;
    4.  
    5. //we add in the offset rotation to find the rotation with the correct forward direction but the up direction might be tilted.
    6. var targetLook = Quaternion.LookDirection(pivotDirection) * offsetRotation;
    7.  
    8. //build a new rotation using targetLook's correct foward combined with world Up as the up rotation
    9. trasnform.rotation = Quaternion.LookRotation(Vector3.foward * targetLook, Vector3.up);
    10.  
    this way the rotations don't build on itself. its always rebuilt on the current frame which should prevent visible tilt.
     
  18. macros976

    macros976

    Joined:
    Jun 22, 2017
    Posts:
    20
    Ok! Success!

    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.
     
  19. unity_PsYU5ZX6TLsoTg

    unity_PsYU5ZX6TLsoTg

    Joined:
    Feb 3, 2019
    Posts:
    1
    You could share the final script, I'm trying to do the same.
     
  20. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    Hi,

    ...why not take the code you got for free, and take it further yourself?

    This isn't get code for free service, but a community... there's Unity Connect for contract work.

    Besides you necro'ed a thread that is 2 years old :)
     
  21. macros976

    macros976

    Joined:
    Jun 22, 2017
    Posts:
    20
    Haha...sometimes there are freebies and sometimes not. I struggled for probably 15 hours to get this right...but I've also lifted code from others who struggled as long or longer on other things.

    Overall, it was pretty simple. I have separate scripts for Pan, Zoom and Orbit. Below is the orbit script I currently use

    Some Notes:

    1. The camera is raycasting through the cursor and the Vector3 "pivotpoint" is the raycast hit.
    2. To properly handle the changing input, the Update() checks input and the orbit is done in LateUpdate().

    Code (CSharp):
    1.  
    2. public class CameraOrbit : MonoBehaviour
    3. {
    4.  
    5.     private Vector3 currentMouse;
    6.     private Vector3 mouseDelta;
    7.     private Vector3 lastMouse;
    8.  
    9.     public static bool activeRotate = false;
    10.     public static Vector3 pivotPoint; //
    11.  
    12.     private void Update()
    13.     {
    14.         mouseDelta = lastMouse - currentMouse;
    15.  
    16.         if (Input.GetKey(KeyCode.LeftControl) && Input.GetMouseButtonDown(2))
    17.         {
    18.             activeRotate = true;
    19.         }
    20.  
    21.  
    22.         if (Input.GetKeyUp(KeyCode.LeftControl))
    23.         {
    24.             activeRotate = false;
    25.         }
    26.  
    27.         currentMouse = Input.mousePosition;
    28.  
    29.         mouseDelta = lastMouse - currentMouse;
    30.     }
    31.  
    32.  
    33.     void LateUpdate ()
    34.     {
    35.         if (activeRotate)
    36.         {
    37.             transform.RotateAround(pivotPoint, Vector3.up, mouseDelta.x * -.1f);
    38.             transform.RotateAround(pivotPoint, transform.right, mouseDelta.y * .1f);
    39.         }
    40.  
    41.         lastMouse = Input.mousePosition;
    42.     }
    43. }
     
    moonlock, zanouk, MarkA2049 and 4 others like this.