Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Issues with camera orbit around planet

Discussion in 'Scripting' started by Doxs_Roxs, Sep 7, 2020.

  1. Doxs_Roxs

    Doxs_Roxs

    Joined:
    Aug 25, 2020
    Posts:
    5
    I'm pretty new at this so there are probably some issues here that are easy for you guys to spot :)

    I have a planet at 0,0,0 and a camera that orbits the planet.
    My goal is to have zoom independent movement of the camera around the planet where the feeling is that you grab the planet and spin it. I also wanted input to be viable anywhere on the screen.

    My current (like 20th...) solution is to convert mouse clicks to world coordinates, measure the distance and diameter of the planet and compensate for the change in distance.

    It works perfectly if I only rotate around one axis. I can rotate around as much as I like along both x and y, even over the top of the planet so that my camera is upside down in worldcoordinates.
    But as soon as have both transforms active in the code so I rotate around 2 axis at the same time I run into issues.

    Moving it carefully I notice I have issues when the angle for movement between mouse position this frame and last frame passes from positive to negative etc. I am not quite sure how that creates the large jumps and subsequent complete loss of control, but something strange is happening.

    I am also equally convinced I am just making a stupid mistake and have been staring at the trees for too long :p
    I am starting to think that I might have to handle edge cases when the mouse passes over 0 degrees. That is, when last mouse angle was negative and current angle is positive.
    However, I am not quite sure why that is an issue.

    I hope this rambling makes sense, here is the code, please let me know if I could simplify this as well:
    Code (CSharp):
    1. void Update()
    2.     {
    3.         if (Input.GetMouseButtonDown(1))
    4.         {
    5.             //start timer
    6.             mousedownTime = Time.time;
    7.  
    8.             lastMousePosition = Input.mousePosition;
    9.  
    10.             return;
    11.         }
    12.  
    13.         if (Input.GetMouseButtonUp(1))
    14.         {
    15.             //clear timer
    16.             mousedownTime = 0;
    17.  
    18.             xAngle = 0;
    19.             yAngle = 0;
    20.  
    21.             return;
    22.         }
    23.  
    24.  
    25.         if (mousedownTime != 0 && ((mousedownTime + clickDuration) < Time.time))
    26.         {
    27.             currentCameraPosition = transform.position;
    28.  
    29.             currentMousePosition = Input.mousePosition;
    30.  
    31.             fdistance = Vector3.Distance(target.transform.position, transform.position) - planetDiameter;
    32.  
    33.             //Get current world coordinates for the mouse pointer in relation to the planet surface for current and last click
    34.             Vector3 currentMousePositionWorld = thisCamera.ScreenToWorldPoint(new Vector3(currentMousePosition.x, currentMousePosition.y, fdistance));
    35.             Vector3 lastMousePositionWorld = thisCamera.ScreenToWorldPoint(new Vector3(lastMousePosition.x, lastMousePosition.y, fdistance));
    36.  
    37.             //Save copies since we have to manage x and y rotation separately
    38.             currentXMousePositionWorld = currentMousePositionWorld;
    39.             lastXMousePositionWorld = lastMousePositionWorld;
    40.  
    41.             currentYMousePositionWorld = currentMousePositionWorld;
    42.             lastYMousePositionWorld = lastMousePositionWorld;
    43.  
    44.             currentXMousePositionWorld.y = 0;
    45.             lastXMousePositionWorld.y = 0;
    46.  
    47.             currentYMousePositionWorld.x = 0;
    48.             lastYMousePositionWorld.x = 0;
    49.  
    50.             //Angles between click and camera to planet centerline
    51.             lastxAngle = Vector3.SignedAngle(thisCamera.transform.position, (lastXMousePositionWorld + currentCameraPosition), Vector3.up);
    52.             xAngle = Vector3.SignedAngle(thisCamera.transform.position, (currentXMousePositionWorld + currentCameraPosition), Vector3.up);
    53.  
    54.             //Angles between click and camera to planet centerline
    55.             lastyAngle = Vector3.SignedAngle(thisCamera.transform.position, (lastYMousePositionWorld + currentCameraPosition), Vector3.right);
    56.             yAngle = Vector3.SignedAngle(thisCamera.transform.position, (currentYMousePositionWorld + currentCameraPosition), Vector3.right);
    57.  
    58.             //Orbit the camera in the X? axis
    59.             transform.RotateAround(target.transform.position, Vector3.up, ((lastxAngle - xAngle) * rotationSpeed));
    60.  
    61.             //Orbit the camera in the Y? axis
    62.             transform.RotateAround(target.transform.position, Vector3.right, ((lastyAngle - yAngle) * rotationSpeed));
    63.  
    64.             lastMousePosition = currentMousePosition;
    65.         }
     
  2. Doxs_Roxs

    Doxs_Roxs

    Joined:
    Aug 25, 2020
    Posts:
    5
    On closer inspection, rotating over the top of the planet seems to mess things up as soon as I am not in the default orientation. I think I need to use Quaternions?
    However, I have no idea how to combine that with rotatearound, or do I need to handle that manually?
     
  3. Zer0Cool

    Zer0Cool

    Joined:
    Oct 24, 2014
    Posts:
    203
    It's just an attempt (because i dont checked your full code), but try to rotate about the local axes of the planet:
    Code (CSharp):
    1. //Orbit the camera in the X? axis
    2. transform.RotateAround(target.transform.position, target.transform.up, ((lastxAngle - xAngle) * rotationSpeed));
    3. //Orbit the camera in the Y? axis
    4. transform.RotateAround(target.transform.position, target.transform.right, ((lastyAngle - yAngle) * rotationSpeed));
    5.  
    But i think you have to use the local axes for these variables then too:
    lastxAngle
    xAngle
    lastyAngle
    yAngle

    Perhaps try with i fixed degree first:
    Code (CSharp):
    1. //Orbit the camera in the X? axis
    2. transform.RotateAround(target.transform.position, target.transform.up, 5f * rotationSpeed));
    3. //Orbit the camera in the Y? axis
    4. transform.RotateAround(target.transform.position, target.transform.right, 5f * rotationSpeed))
     
    Last edited: Sep 7, 2020
  4. Doxs_Roxs

    Doxs_Roxs

    Joined:
    Aug 25, 2020
    Posts:
    5
    Thanks, Ill give it a try, but I am not quite sure it will help.
    The lastxangle etc. are just floats since the transform.rotatearound takes that as an input for the angle.
     
  5. Doxs_Roxs

    Doxs_Roxs

    Joined:
    Aug 25, 2020
    Posts:
    5
    I just tried changing to the target.transform and the result is exactly the same.
    I am not sure why, but I think this might be connected to gimbal lock or some other sort of issue connected to the coordinate system angles when passing 0 angle.
     
  6. Zer0Cool

    Zer0Cool

    Joined:
    Oct 24, 2014
    Posts:
    203
    Doxs_Roxs likes this.
  7. Zer0Cool

    Zer0Cool

    Joined:
    Oct 24, 2014
    Posts:
    203
    But if you want this feeling:
    "My goal is to have zoom independent movement of the camera around the planet where the feeling is that you grab the planet and spin it. I also wanted input to be viable anywhere on the screen."

    I had short time ago programmed a rotation for looking at an object that the player is holding in his hands in front of the camera.

    Here is the code (shortened and simplified). The position of the planet must be placed in front of the camera. Here you spin the planet and not the camera:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class SelectionManager : MonoBehaviour
    6. {
    7.     public GameObject CurrentSelection;
    8.  
    9.     private Camera m_Camera;
    10.  
    11.     private void Start()
    12.     {
    13.         m_Camera = GameObject.FindObjectOfType<Camera>();
    14.     }
    15.  
    16.     // Update is called once per frame
    17.     private void Update()
    18.     {
    19.         if (CurrentSelection != null)
    20.         {
    21.             RotateObject();
    22.         }
    23.     }
    24.  
    25.     public void RotateObject()
    26.     {
    27.         float InputX = Input.GetAxis("Mouse X");    
    28.         float InputY = Input.GetAxis("Mouse Y");
    29.         CurrentSelection.transform.RotateAround(CurrentSelection.transform.position, m_Camera.transform.up, InputX * 550f * Time.deltaTime);
    30.         CurrentSelection.transform.RotateAround(CurrentSelection.transform.position, m_Camera.transform.right, InputY * 550f * Time.deltaTime);
    31.     }
    32. }
    33.  
     
  8. Doxs_Roxs

    Doxs_Roxs

    Joined:
    Aug 25, 2020
    Posts:
    5
    Thank you, I think this is part of the issue and the explanation is very good.
    However, I think I have more issues related to the angles when passing zero for each axis.
    Specifically when one angle is negative and the other is positive.

    Ill try and make this work and post here in the thread once I have some progress.

    As for your other suggestion to rotate the planet, I do not think it is suitable for me since I might need moons etc. around the planet and it will be full of objects on its surface as well. I have no idea if my assumption is correct, but I am worried that rotating hundreds or perhaps thousands of objects to shift the view might be a bad idea.
     
    Zer0Cool likes this.