Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Calculate relative position and rotation based on offset from initial values

Discussion in 'Scripting' started by SorneBachse, Jan 9, 2023.

  1. SorneBachse

    SorneBachse

    Joined:
    Dec 27, 2012
    Posts:
    62
    Hey all.

    I've been stuck all day trying to solve this problem I'm having.

    It's as following:
    I have a 2D ortho camera, which is supposed to receive some offset data input (an X offset, Y offset and rotation in Z axis offset). These offsets are then used to position the camera as well as rotate it, based on its initial position and rotation when the scene starts up (its default values).
    So essentially I've been trying to calculate the final position and rotation from the initial values + new offset that comes in from the outside data. In this way, the data is "deterministic", so if given the same input data, it will always result in the same position and rotation.

    The tricky part is, since this is the camera we are rotating, and we are receiving input as X and Y (horizontal and vertical), the user would always expect the horizontal input moves the X axis, and the vertical input moves the Y axis, no matter what way the camera is oriented. And of course when dealing with rotating the camera, this is no longer true.

    My approach to this so far has been to calculate the new position and rotation from the offset data, apply the rotation, and then transform the new position from local to world via
    Code (CSharp):
    1. transform.TransformVector(targetPosition);
    And this works great for movement. The camera then moves up and down in its own local y axis, but the rotation is not correct. It seems to have shifted its origin/rotation pivot to the origin of the world itself. And I cannot understand why.

    Here is my simplified sandbox project code to demonstrate the issue:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class CameraController : MonoBehaviour
    4. {
    5.     [SerializeField] private OffsetData offsetData;
    6.  
    7.     private OffsetData defaultOffset;
    8.  
    9.     private void Start()
    10.     {
    11.         defaultOffset = new OffsetData
    12.         {
    13.             HorizontalOffset = transform.position.x,
    14.             VerticalOffset = transform.position.y,
    15.             RotationOffset = transform.rotation.eulerAngles.z,
    16.         };
    17.     }
    18.  
    19.     private void OnValidate()
    20.     {
    21.         if (!Application.isPlaying || defaultOffset == null)
    22.             return;
    23.  
    24.         ApplyOffsetData(offsetData);
    25.     }
    26.  
    27.     private void ApplyOffsetData(OffsetData newData)
    28.     {
    29.         var targetRotation = Quaternion.Euler(new Vector3(0.0f, 0.0f, defaultOffset.RotationOffset + newData.RotationOffset));
    30.        
    31.         var targetPosition = new Vector3(defaultOffset.HorizontalOffset + newData.HorizontalOffset, defaultOffset.VerticalOffset + newData.VerticalOffset, transform.position.z);
    32.  
    33.         transform.rotation = targetRotation;
    34.         transform.position = transform.TransformVector(targetPosition);
    35.     }
    36.  
    37.     [System.Serializable]
    38.     private class OffsetData
    39.     {
    40.         [Range(-20.0f, 20.0f)] public float HorizontalOffset;
    41.         [Range(-20.0f, 20.0f)] public float VerticalOffset;
    42.         [Range(0.0f, 359.0f)] public float RotationOffset;
    43.     }
    44. }
    45.  


    In the video, I'm playing around with the horizontal and vertical offset and it works. But the rotation is now wrong as you can see in the video. The rotation is no longer around the camera itself, but the origin of the world (0, 0, 0).

    Any help here is greatly appreciated!
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,522
    When you rotate a vector quantity you are implicitly rotating it around (0,0)

    if you want to rotate it around a different center besides (0,0), you must subtract that center, rotate the remainder vector, then re-add that center back in after the rotation.

    As is mathematically obvious, technically you still do it even when the center is (0,0) but adding and subtracting (0,0) is simply a non-operation.
     
  3. SorneBachse

    SorneBachse

    Joined:
    Dec 27, 2012
    Posts:
    62
    Hey Kurt, thanks for the reply. I'm trying to wrap my head around it. It sounds like the way to do it is essentially translate the position of the camera back to world origin, rotate it, and then translate it back. Is that correct assumed?

    I've tried to do so but seems to be very jittery, moving back and fourth between origin and itself.

    Code (CSharp):
    1.     private void ApplyOffsetData(OffsetData newData)
    2.     {
    3.         var targetRotation = Quaternion.Euler(new Vector3(0.0f, 0.0f, defaultOffset.RotationOffset + newData.RotationOffset));
    4.         var targetPosition = new Vector3(defaultOffset.HorizontalOffset + newData.HorizontalOffset, defaultOffset.VerticalOffset + newData.VerticalOffset, transform.position.z);
    5.  
    6.         var offset = Vector3.zero - transform.position;
    7.         transform.position -= offset;
    8.         transform.rotation = targetRotation;
    9.         transform.position = targetPosition + offset;
    10.     }
    Also, it still seems to be translating in world space and not local space.