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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question Find angle between C - B - T

Discussion in 'Scripting' started by Selocanus, Jun 1, 2023.

  1. Selocanus

    Selocanus

    Joined:
    Mar 10, 2019
    Posts:
    4
    Hi there I need to find the B's angle for a game. The game will be played from mobile devices. For thinking 2D things are simpler but we will play the game from 3D perspective and height of all objects can change. Also getting touch position in 3D was fine but I dont know if I am doing the right thing to find the angle.
    The balls will be speed and direction will be defined with "Cos(theta)" coefficient. Therefore I need to find angle. I would be really grateful if anyone can help me.
    Code (CSharp):
    1. public int CalculateDirection(Vector3 LastTouchpos, Vector3 CurrentTouchPos, Transform targetBall)
    2.     {
    3.         //Find Center to Ball as Vector X
    4.         //Find Ball to Touch as Vector Y
    5.         //Angle = cos^-1[( X * Y ) / ( |X||Y| )]
    6.         // X * Y is dot product, |X||Y| is cross product
    7.         //OR Mathf.Atan2(X,Y);
    8.         Vector2 DeltaSwipe = LastTouchpos - CurrentTouchPos;
    9.         Vector2 A = targetBall.position - Computers[ActiveSplineList].transform.position;
    10.         Vector2 B = CurrentTouchPos - targetBall.position;
    11.         //var angle = Mathf.Acos((Vector3.Dot(X, Y)) / (Vector3.Cross(X,Y)));
    12.  
    13.         //var angle = Mathf.Atan2(B.y - A.y, B.x - A.x);
    14.  
    15.         //var angle = Vector3.Angle(A, B);
    16.         var angle = Vector2.Angle(A, B);
    17.         Debug.Log("The Angle is:" + angle);
    18.         return (int)(angle / Mathf.Abs(angle));
    19.     }
     
    Last edited: Jun 5, 2023
  2. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Can you explain this a little better?
    I can see you already tried every common approach, but you failed to explain what's wrong with it.
     
  3. Selocanus

    Selocanus

    Joined:
    Mar 10, 2019
    Posts:
    4
    Hi, I am getting the touch position and last frame's touch position on Update, then if we hit one of the balls at first touch to screen we start dragging the balls on spline. However what I want to do is build up a good movement system that finds which way to move and how much of power should be used from Cos coefficient. Therefore I need to find a way to get exact angle on C-B-T point.

    What I am having as issue is calculating angle wrong. Or maybe using update? I don't exactly know right now.
     
  4. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    I still don't understand what is the exact angle that you're trying to find.
    In 2D, finding the angle is as simple as this
    Code (csharp):
    1. var angle = Mathf.Atan2(dy, dx);
    But because I can see you already did this and got a wrong result, I can infer that it was wrong because your dx and dy were wrong.

    If C - B - T means the angle between two vectors, where B (or ball) is at the center of the two. This simply means that your origin is shifted, so for atan2 to work, you really need to bring the origin back to zero.

    So you either work with vectors and compute the angle from that (via dot product and linear algebra), or you compute your end points around the world origin (which is now at ball's center) and use trigonometry (arc tan). Which is further simplified because vectors are assumed to originate at (0, 0) anyway.

    With atan2 you don't have to worry about vectors being unit, so if you have
    ball.transform
    ,
    center
    , and
    currentTouch
    , you can build 2D vectors out of these.
    Code (csharp):
    1. var ball = toV2(ball.transform.position);
    2. var bc = toV2(center) - ball; // vector going from ball to center
    3. var bt = currentTouch - ball; // vector going from ball to touch
    4.  
    5. Vector2 toV2(Vector3 v) => new Vector2(v.x, v.y); // change this to your actual 2D plane
    upload_2023-6-4_15-37-45.png
    (Edit: I think I got C and T swapped in this image, but it doesn't matter)

    This is how absolute angles (in degrees) work in math, and you can treat them as signed or unsigned values, where it's easy to convert one into another if you consider them as modular values. Angle cover is typically expressed in unsigned values, but angle comparison/difference is typically expressed in signed values. But you can always change this on the spot and it's sometimes important to make sure which is which.

    upload_2023-6-4_15-33-56.png

    Now consider that atan2 returns an absolute angle in radians, so you need to measure both vectors and convert the results to degrees.
    Code (csharp):
    1. var bca = Mathf.Atan2(bc.y, bc.x) * Mathf.Rad2Deg;
    2. var bta = Mathf.Atan2(bt.y, bt.x) * Mathf.Rad2Deg;
    Now you have two angles that you want to subtract to get the difference between them, however the order of subtraction matters.
    Code (csharp):
    1. var diff = bta - bca;
    Here you can see some combinations and what their difference would get you.

    upload_2023-6-4_15-51-47.png


    90 - 0 = 90
    135 - (-45) = 180
    135 - (-135) = 270

    However there are combinations where this could fail, so let's be smarter about the whole thing. We can turn all signed results to unsigned, then turn the final difference to unsigned angle as well.
    Code (csharp):
    1. float angleInDegrees(Vector2 a, Vector2 b) {
    2.   const float REV = 360f;
    3.   float a1 = mod(todeg(atan2(a.y, a.x)), REV);
    4.   float a2 = mod(todeg(atan2(b.y, b.x)), REV);
    5.   return mod(a2 - a1, REV);
    6.  
    7.   static float todeg(float n) => n * Mathf.Rad2Deg;
    8.   static float atan2(float y, float x) => System.MathF.Atan2(y, x);
    9.   static float mod(float n, float m) => (n %= m) < 0f? n + m : n;
    10. }
    What you get
    90 - 0 = 90
    135 - (-45) => 135 - 315 = -180 => 180
    135 - (-135) => 135 - 225 = -90 => 270

    mod
    here stands for the general form of 'true modulo' and it's there to make sure that the values always come out in the [0..360) interval.
    %
    is blazingly fast, but if you want it to be really fast, you can also do this (only in this particular context, because the results of Atan2 will never exceed ±Pi).
    Code (csharp):
    1. static float mod(float n, float m) => n < 0f? n + m : n;
    This isn't a commutative operation, you can get the other angle difference by swapping the vectors.
    0 - 90 = -90 => 270
    -45 - 135 => 315 - 135 = 180
    -135 - 135 => 225 - 135 = 90

    Finally, if you want the results to be signed, this boils down to
    Code (csharp):
    1. // returns an angle in degrees
    2. float angleBetween(Vector2 a, Vector2 b, bool signedAngle = false) {
    3.   const float REV = 360f;
    4.   var diff = mod(angle(b) - angle(a), REV);
    5.   if(signedAngle && diff >= REV * .5f) diff -= REV;
    6.   return diff;
    7.  
    8.   float angle(Vector2 v) => mod(System.MathF.Atan2(v.y, v.x) * Mathf.Rad2Deg, REV);
    9.   static float mod(float n, float m) => n < 0f? n + m : n;
    10. }
    Btw this Atan2 function is faster than UnityEngine.Mathf.Atan2 because it works natively with single-precision floating point.

    Edit: fixed a typo in new Vector2 at the beginning
     
    Last edited: Jun 8, 2023
    Selocanus and Nad_B like this.
  5. Selocanus

    Selocanus

    Joined:
    Mar 10, 2019
    Posts:
    4
    Dude you are such a life saver thank you, but still I couldn't complete the system. I guess I will look into that in a short time. Thank you.
     
  6. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Here I've golfed this further.
    Code (csharp):
    1. // returns an angle in degrees; vectors need not be of unit length
    2. // non-commutative: angleBetween(a, b) == 360 - angleBetween(b, a)
    3. float angleBetween(Vector2 a, Vector2 b, bool signedAngle = false) {
    4.   const float D180 = 180f, D360 = 360f;
    5.   float d = unsgn(angle(b) - angle(a));
    6.   return signedAngle? sgn(d) : d;
    7.   float angle(Vector2 v) => unsgn(MathF.Atan2(v.y, v.x) * D180 / MathF.PI);
    8.   float unsgn(float n) => n < 0f? n + D360 : n;
    9.   float sgn(float n) => n >= D180? n - D360 : n;
    10. }
    With all that said, you can also use Vector2.Angle and Vector2.SignedAngle :)