Search Unity

need Vector3.Angle to return a negtive or relative value

Discussion in 'Scripting' started by ulao, Jun 9, 2010.

  1. ulao

    ulao

    Joined:
    Oct 23, 2009
    Posts:
    37
    I'm using Vector3.Angle to get a distance from two rotation. If the target is 25 deg clocwise I'm good I get 25. but its its 25 deg CCW(335), I still get 25. I want 335. I though maybe slerp would give it to me but no luck.
     
  2. dbp

    dbp

    Joined:
    Mar 3, 2010
    Posts:
    324
    there is no clockwise or counter-clockwise in 3d space afaik, you'd have to describe your situation a little bit more so we can think of criteria that define what you mean by clockwise
     
  3. ulao

    ulao

    Joined:
    Oct 23, 2009
    Posts:
    37
    ok I have object A:


    ^
    |
    0


    and b

    0->

    In my mind the difference is 90 degrees. If b was
    <-0
    I would call this 270. But using Vector3.Angle I get 90 for both. I would like to get a -90 or 270 for the later.
     
  4. dbp

    dbp

    Joined:
    Mar 3, 2010
    Posts:
    324
    yeah but how do you know that you aren't looking from the other side and 90 and 270 get flipped? this is easy to answer if the vectors are the coordinate system's axis, but if both vectors are somewhere in space it's hard to define which one would be "90" and which one would be "270".
     
  5. ulao

    ulao

    Joined:
    Oct 23, 2009
    Posts:
    37
    well in my case the source is always in the middle of all the target points.

    I have say 4 targets, and one central point. I need to know what angle to turn to face these targets. The angle must be 1-360 or -180 to 180.

    Vector3.Angle seems to give my the absolute distance. So I dont know what way to turn.


    Update: I think this will work
    http://forum.unity3d.com/viewtopic.php?t=33313
     
  6. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    Are you sure that's important? Degrees are nice for human beings to think about, but they're not helpful to a performant solution (which could also be a lot less code - knowing a bit about quaternions can save you a bunch of trigonometry). I don't know exactly what you need to do, but without more info, it sounds like this fits the bill. If that's not what you need, flip through the other pages of the Quaternion class.

    http://unity3d.com/support/documentation/ScriptReference/Quaternion.SetLookRotation.html
     
  7. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    I've run into this situation before.

    If I may, let me rephrase the initial question:

    What change in rotation, along a certain axis, do I need to apply in order to face an object?

    so if I am looking down on A and B, with A facing to the right,

    A
    B

    A needs to rotate a positive 90 degrees around the Y axis in order to face B

    However, if I have

    B
    A

    With A facing to the right, I need to rotate A -90 degrees around the Y axis to face B


    Is this correct, and does it make sense?

    Regarding using Quaternions...I tried that with a project I was working on, and found it pretty confusing, but maybe I should give it another shot.

    Here's the deal:
    When faced with something easy to grasp (get angle between two objects, tell first object to rotate that many degrees)
    verses
    Quaternions

    I think most people, like myself, look for various possible solutions. The traditional angle/degrees approach, and quaternions.
    They read somewhere "Quaternions are better".
    So they try, and may not have much luck (like me).
    They go to the boards.
    At this point, if they are unable to get an answers, guess which approach they'll take? Personally, my goal isn't to comprehend quaternions, it's to get the darn thing to turn in the direction I want.

    I personally found the answer using angles, but I suspect it's not as elegant or ideal as using quaternions, but I'll probably at some point either put enough posts on the boards, or stick with my less than ideal solution.
     
  8. dbp

    dbp

    Joined:
    Mar 3, 2010
    Posts:
    324
    look at the link ulao posted, it's exactly the situation you described, however, you need to supply a forward and an upward direction, which you need to define first
     
  9. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    Sure, but again, it's kind of a convoluted way of dealing with things in the 3D space that your game objects are in. If you really needed to know the angle between two things, then you can use this:

    http://unity3d.com/support/documentation/ScriptReference/Quaternion.SetFromToRotation.html

    (with Vector3.forward as the first argument, and transform.InverseTransformPoint(otherTransform.position) as the second.

    Or with transform.forward as the first argument, and otherTransform.position - transform.position as the second. I don't know if there's a speed difference between the two; seems to me they should be about equal in performance.)

    Then you can get a nice angly version using this:
    http://unity3d.com/support/documentation/ScriptReference/Quaternion.Euler.html

    However, Unity doesn't need that. It only needs a quaternion. The Euler stuff is purely for our puny human brains.
     
  10. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    Thanks, Jessy :) I'll look into this when I get home, and can dig into the code.
     
  11. Tinus

    Tinus

    Joined:
    Apr 6, 2009
    Posts:
    437
    I've struggled with the angle problem for ages a year ago. Eventually I've worked out this, which has been in my Math library ever since:

    Code (csharp):
    1. /// <summary>
    2. /// Determine the signed angle between two vectors, with normal 'n'
    3. /// as the rotation axis.
    4. /// </summary>
    5. public static float AngleSigned(Vector3 v1, Vector3 v2, Vector3 n)
    6. {
    7.     return Mathf.Atan2(
    8.         Vector3.Dot(n, Vector3.Cross(v1, v2)),
    9.         Vector3.Dot(v1, v2)) * Mathf.Rad2Deg;
    10. }
    The difference with the normal Angle method is that you need to specify a third vector. Think of the two vectors that you want the angle between as lying on a single plane. The normal vector should be the normal sticking out of that plane.

    You can calculate that normal by taking the cross product of your two vectors. Just note that, Cross(v1, v2) gives the opposite result of Cross(v2, v1). Using either will give you the signed angle you want, but the sign of the angle will flip depending on which one you choose. :)


    I'm not sure using this angle test is the most optimal solution for doing something like a target-tracking turret, but is a solution.
     
  12. jonboris

    jonboris

    Joined:
    Jan 8, 2009
    Posts:
    78
    Tinus, thank you for this, works perfectly - I just wish I could understand the maths behind it....
     
  13. Julien-Lynge

    Julien-Lynge

    Joined:
    Nov 5, 2010
    Posts:
    142
    @jonboris,

    Look at the Wikipedia article for dot products - it's actually pretty good, and will get you a fair ways towards understanding this code.

    All a dot product does is get the amount that a vector is pointing the same direction as another vector. So, abstractly, the overlap would be 1 if they're pointing in the same direction, 0 if they're 90 degrees apart, and -1 if they're pointing in opposite directions.

    The cross product in there is pretty cool too. What it does is take two vectors and finds another vector that's 90 degrees off from both of them. Put another way, it pretends that the two vectors are in the same plane, and then finds another vector pointing straight out of that plane.

    Mathf.Atan2 is the Arctangent, which is just a tangent in reverse. Normal tangent: put in an angle, get out the relative lengths of two sides of a triangle. Arctangent: put in the relative lengths of two sides of a triangle, get out the angle. You can find plenty of good intros online for tangents.

    Good luck!
     
    altschuler likes this.
  14. DanJC

    DanJC

    Joined:
    Feb 18, 2013
    Posts:
    15
    Isn't this much simpler?

    theta = Mathf.Asin(Vector3.Cross (v1.normalized, v2.normalized).magnitude);


    We know that:

    Cross(A,B).magnitude is the same as A.magnitude * B.magnitude * sin(theta)

    ...where theta is the angle between A and B on the plane formed by A and B.
    And if you normalise A and B, their magnitudes are just 1, so we have:

    Cross (A.normalized, B.normalized).magnitude is the same as sin(theta).

    Maybe I've done something wrong, because I've checked a bunch of threads answering this and no-one seems to have suggested the single line solution above.
     
    Last edited: Apr 16, 2013
  15. empo

    empo

    Joined:
    Apr 15, 2013
    Posts:
    12
    Yes, but taking the magnitude of the vector you lose the "direction" (whether positive or negative) of the angle
    Angles can be a real PITA to deal with at times. I've switched over to using Dot/Cross products as much as possible when dealing with angles, and trying to avoid the use of the inverse trig functions.

    cheers
    empo
     
  16. DanJC

    DanJC

    Joined:
    Feb 18, 2013
    Posts:
    15
    Doh of course. That would be the bug I'm getting now!
     
  17. empo

    empo

    Joined:
    Apr 15, 2013
    Posts:
    12
    did you get it figured out?
     
  18. seejayjames

    seejayjames

    Joined:
    Jan 28, 2013
    Posts:
    691
    Old thread I know, but it helped me out a moment ago, so wanted to post another solution. This one doesn't use dot or cross products.

    If you have a point (x, y) distance from an arbitrary (0,0), the angle to the center can be found by

    angle_to_center = Mathf.Atan2(y_distance_from_center, x_distance_from_center) * Mathf.Rad2Deg; // note Atan2, not Atan

    The top (positive Y) hemisphere is fine (counter-clockwise: 0 to 180). The bottom (negative Y) hemisphere gives you -180 to 0, so just do this:

    if (angle_to_center < 0) angle_to_center += 360; // convert bottom hemisphere (negative) values to [180 to 360]

    Hope it helps.
     
  19. MD_Reptile

    MD_Reptile

    Joined:
    Jan 19, 2012
    Posts:
    2,664
    seejayjames I have tried a similar solution years ago (never saw this thread at the time, or it didnt exist) and thought that solved my problem... but later down the road I ran into smoothing issues, where it would cause objects to "twitch" slightly at times when moving across the change between the "hemisphere" you speak of.

    Anyway been using the method mentioned higher up in this thread in one variation or another ever since.
     
  20. seejayjames

    seejayjames

    Joined:
    Jan 28, 2013
    Posts:
    691
    Hmm, not sure whether your method was different somehow, here it works smoothly without any jumps. Anyway, lots of options ;)
     
  21. dillon_yeti

    dillon_yeti

    Joined:
    May 11, 2015
    Posts:
    9
    Tinus, I know it's years later. But thank you!
     
  22. shieldgenerator7

    shieldgenerator7

    Joined:
    Dec 20, 2015
    Posts:
    39
    @Tinus
    Thanks! This helped out a lot!

    However, it wasn't immediately obvious to me which parameters were which. I was trying to set a line-like sprite's end points to two position vectors. This is how I called your method to do it:

    float angle = AngleSigned(end - start, Vector3.left, Vector3.back);
    transform.rotation = Quaternion.Euler(0,0,angle);

    End is the head of the "arrow", and start is the tail. I used Vector3.left because my initial sprite faces left.

    Hope this helps!
     
  23. vargata

    vargata

    Joined:
    Nov 26, 2013
    Posts:
    120
    Here is a big big big thanks too. I needed to get earth time from the planets rotation and i havent found a better way. thx
     
  24. joaoborks

    joaoborks

    Joined:
    Jul 7, 2015
    Posts:
    29
  25. ngerbens

    ngerbens

    Joined:
    Nov 20, 2017
    Posts:
    33
    @Tinus Thank you very very much for this solution. Works like a charm.