Search Unity

What does MaxMagnitudeDelta in Vector3.RotateTowards even do?

Discussion in 'Scripting' started by SVAFnemesis_, Apr 17, 2021.

  1. SVAFnemesis_

    SVAFnemesis_

    Joined:
    Jul 15, 2020
    Posts:
    21
    I've been doing a lot of googling and everyone says the same thing without pointing out the obvious confusion here, let me present it as simple as I can.

    here are the vec3.rotatetowards parameters:
    Vector3 RotateTowards(Vector3 current, Vector3 target, float maxRadiansDelta, float maxMagnitudeDelta)

    we all know the first three. But the fourth, maxMagnitudeDelta, it means the max magnitude you are allow to rotate, correct? This is as in, if there's a tip on your vector3, that's saying how far you are allowing the tip to travel in a 3d space, correct?
    Assuming I was being right, then if I set maxMagnitudeDelta to 0f, my RotateTowards will rotate nothing, since I'm not allowing the tip to move at all. But in every sample code given by Unity, it is set as 0.0f.

    So would anyone who has some light to shed, please enlighten me. Thank you.
     
  2. No. If your two vectors are different in magnitude, the maxMagnitudeDelta communicates how much change in magnitude can be applied in one step. It's like the maxRadiansDelta but for the length of the vectors.
     
  3. SVAFnemesis_

    SVAFnemesis_

    Joined:
    Jul 15, 2020
    Posts:
    21
    shouldn't current and target always be different in magnitude if there are rotations to be made? If I understand this correctly, in here magnitude really means the distance between current and target? or is it entirely something else?
     
  4. screenshot1.png screenshot2.png

    Sorry for the crude drawing, I'm not an artist. So, explanation: black are axis. Red: current vector. Blue: target vector. Greens: in-between results turning counter-clockwise over time.
    The left drawing shows when the
    maxMagnitudeDelta
    is 0f. The right drawing shows when the delta allows the magnitude to reach the target's magnitude.

    We usually don't care about magnitude since we care about normalized vectors (rotation is about direction not magnitude), but there are some cases when we do. In those cases this parameter allow us to change the magnitude gradually, like the direction. That's why usually this parameter is zero.
     
  5. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,997
    Rephrasing what LN wrote, RotateTowards has a second osbcure feature -- it can also make your vector longer or shorter to match the target vector's length. It's seriously weird -- I'm using RotateTowards because rotations are tricky and I need it to do them. But changing the length of vectors is easy: not only can I do that myself, I'd prefer to do it myself, and it would never occur to me to look for it in RotateTowards. But it's there.

    Another reason not to like it, you'd assume being together means there's an easy way to have the vector reach it's final rotation and length at the same time. Nope. You have to do the math yourself to find the matching maxMagnitudeDelta.

    To test, try
    v=RotateTowards(v, v*2, 0, 0.02f);
    . v should just get 0.02 longer each frame (and, to be fair, that takes 2 lines of math so this may be easier for some people to use).
     
  6. SVAFnemesis_

    SVAFnemesis_

    Joined:
    Jul 15, 2020
    Posts:
    21
    Thank you for the detail explanation, apologize that I'm going to have to further challenge your statement. You were saying what others from my google search has been saying. However with my own testing, I am consistently getting the observation that if maxMagnitude is zero, RotateTowards DO NOT ROTATE AT ALL. However that does not apply to maxRadian, that is if you have a zero maxRadian, RotateTowards simply ignore it, and follow the maxMagnitude argument instead. So in your drawing, if you did set maxMag to zero, there will be no green line at all.

    And like I said, this defies what Unity Doc is claiming what it is.
     
  7. SVAFnemesis_

    SVAFnemesis_

    Joined:
    Jul 15, 2020
    Posts:
    21
    Isn't Vector3.RotateTowards calculate Euler Angle changes by radian (in 3d space) while interpolating? Thus vector direction is probably not part of the cauculation?

    Another thing would be I just realize magnitude here means delta angle in degree, not length as we thought it would be. That means if maxMagnitude is 1, you're only allow to rotate 1 degree at a time. I have tested it.

    So to sums it up, maxRadian and MaxMagnitude really should be: do you want to clamp it with radian, or degree? Pick one. And that's where it stop making sense, because we don't want two conflicting argument.
     
  8. SVAFnemesis_

    SVAFnemesis_

    Joined:
    Jul 15, 2020
    Posts:
    21
    public Transform current;
    public Transform target;
    public float maxRadian;
    public float maxMagnitude;
    public bool stepRun = false;

    private Vector3 stepvec;

    private void Update()
    {
    if (stepRun == true)
    {
    stepRun = false;
    DoThis();
    }
    }
    private void DoThis()
    {
    stepvec = Vector3.RotateTowards(current.eulerAngles, target.eulerAngles, maxRadian, maxMagnitude);
    current.eulerAngles = stepvec;
    }
     
  9. Well, according to the example code on the documentation page it does rotate when the maxMagnitudeDelta is 0f.
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Rotator : MonoBehaviour
    4. {
    5.     [SerializeField] private Transform target;
    6.     [SerializeField] private float speed;
    7.  
    8.     private void Update()
    9.     {
    10.         var targetDirection = target.position - transform.position;
    11.         var singleStep = speed * Time.deltaTime;
    12.       // ------
    13.         var newDirection = Vector3.RotateTowards(transform.forward, targetDirection,
    14.                 singleStep, 0.0f);
    15.       // ------
    16.         Debug.DrawRay(transform.position, newDirection * 2f, Color.red);
    17.         transform.rotation = Quaternion.LookRotation(newDirection);
    18.     }
    19. }
    screenshot1.png

    As you can see on the screenshot the cube is following the target, which is the sphere in this case.
     
    Last edited by a moderator: Apr 18, 2021
  10. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,997
    The 4th input has nothing to do with rotation. It's difficult to understand since RotateTowards makes so little sense. It does 2 different things. It RotatesTowards, but it's also a GrowTowards! An example (which I actually ran this time), we can "rotate" a sideways x-vector to point up, but give it a rotation speed of 0 and a grow speed of 0.7. It doesn't rotate at all, it only grows sideways:

    Code (CSharp):
    1. Vector3 vvv=Vector3.right; // (1,0,0)
    2.  
    3.   void Awake() {
    4.     string w="";
    5.     for(int i=0;i<10;i++) {
    6.     vvv=Vector3.RotateTowards(vvv, Vector3.up*5, 0, 0.7f);
    7.     w+=vvv+" ";
    8.   }
    9.   Debug.Log(w);
    The output has no rotation, since the speed is 0. vvv is always pointing along +x. But the target vector had length 5, so vvv it grows to length 5 (while still pointing sideways), at a rate of 0.7 each tick, stopping at 5:

    (1.7, 0.0, 0.0) (2.4, 0.0, 0.0) (3.1, 0.0, 0.0) (3.8, 0.0, 0.0) (4.5, 0.0, 0.0) (5.0, 0.0, 0.0) (5.0, 0.0, 0.0)
    (5.0, 0.0, 0.0) (5.0, 0.0, 0.0) (5.0, 0.0, 0.0)

    If we change the rotation to 0.5 (which is interpreted in radians) we can see it rotates to face along +y over 4 frames (y is going up, x is going down), and continues to grew to length 5 once it gets there:

    (1.5, 0.8, 0.0) (1.3, 2.0, 0.0) (0.2, 3.1, 0.0) (0.0, 3.8, 0.0) (0.0, 4.5, 0.0) (0.0, 5.0, 0.0) (0.0, 5.0, 0.0)
    (0.0, 5.0, 0.0) (0.0, 5.0, 0.0) (0.0, 5.0, 0.0)

    If we drop the rotation to 0.1 so it won't reach the target angle, we get a length 5 (I assume) vector pointing diagonally:

    (1.7, 0.2, 0.0) (2.4, 0.5, 0.0) (3.0, 0.9, 0.0) (3.5, 1.5, 0.0) (3.9, 2.2, 0.0) (4.1, 2.8, 0.0) (3.8, 3.2, 0.0)
    (3.5, 3.6, 0.0) (3.1, 3.9, 0.0) (2.7, 4.2, 0.0)

    We can see the numbers are getting larger at first, and y is gaining and surpassing x, then for the last few (we're at length 5) it's just trading x for y as it rotats without growing any more.
     
  11. SVAFnemesis_

    SVAFnemesis_

    Joined:
    Jul 15, 2020
    Posts:
    21
    I think I understand this better now, thank you very much for your testing, I really appreciate your help.

    Also, yes this is really weird, for now I will just convert any eulerAngle to Rotation, and use quaternion RotateTowards, at least they only have one clamp argument.
     
  12. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,997
    Well sure, often it's better to just work with quaternions directly, but adding a 0 at the end of MoveTowards isn't too much work.
    arrow=V3.RotateTowards(arrow, toTarget, 3.14*Time.deltaTime, 0)
    will spin
    arrow
    towards the toTarget direction, keeping
    arrow
    the same length (i.e.: ignoring the actual length of toTarget).

    It is weird the last parm doesn't default to 0.