Search Unity

Limiting an angle given by a normal

Discussion in 'Scripting' started by kikendo, Jul 24, 2017.

  1. kikendo

    kikendo

    Joined:
    Jul 19, 2017
    Posts:
    61
    I will start this topic saying: I forgot most of my math knowledge :eek::confused:

    I am messing with a ball moving inside a cube, bouncing around. I have a shape that I use to affect its trajectory and make it change course.

    Sometimes the angle is really acute and I get unwanted movement, for example, bouncing really hard on Y but barely moving across the Z direction.

    The way I have this setup is that the ball is reflecting via the following script:

    Code (CSharp):
    1. tempVelocity = Vector3.Reflect(other.relativeVelocity*-1.01f, other.contacts[0].normal);// Adds a little force every time
    2.  
    3. rb.velocity = tempVelocity; // assign the ball's velocity the new value
    What kind of operation can I do to the normal I receive to limits its result within a certain constrain, let's say, limit it within 60 degrees? I know the idea behind it: I have to take the normal, take the direction the ball is coming in from, calculate the angle between both, then see if it is between the threshold, otherwise equal it to the min or max number I define. But not really sure how to get the ball's incoming angle and how to operate between the two.

    What units does Unity use anyway, Cartesian or polar? I know you can convert, but I forgot how (I'm sure there's a function that does it already built in!).

    Thanks in advance for the help and sorry for the n00bness. It's been a while since I have done all these things.
     
  2. RoMax92

    RoMax92

    Joined:
    May 29, 2017
    Posts:
    135
    Maybe you can just take the old velocity, and soctract that to the double of the tempVelocity...
    It's a different approach, but maybe work, dont really know.
     
  3. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    1, get the angle from the current velocity (a) to the target vector (b).

    2, if that angle is greater than 60 degrees

    3, get the cross vector of a, b (c)

    4, create a rotation of 60 degrees on the cross vector (r)

    5, multiply a * r to get the outgoing vector 60 degrees in that direction.
     
  4. kikendo

    kikendo

    Joined:
    Jul 19, 2017
    Posts:
    61
    Mino92 hmmm interesting, dunno if that would work really, though.

    bigmisterb: But how do I "get the angle" of the current velocity? I also don't know what you mean with cross vector.
    As I said, I forgot all this math. It's sad, but true.
    I would also how to look up how to rotate a vector. I used to know how.

    Mino92's answer made me think in another way, too. Maybe all I need is to check the .x, .y and .z values of the resulting tempVelocity Vector3, and constrain those if needed, and not deal with angle math at all. Might not be elegant or "correct" but might work.

    Thanks!
     
  5. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    This wound up being very simple.

    lets start by looking at the graphic on the Reflect documenation.

    https://docs.unity3d.com/ScriptReference/Vector3.Reflect.html

    if your incoming vector is -other.relativeVelocity * -1.01f. This heads towards the point.

    the graphic shows the outbound vector is heading away from the point.

    So get the inbound vector, outbound vector and the reverse of the inbound vector (a, b, c)

    Next, simply Rotate the vector towards the new vector at a maximum of 60 degrees. (I know, once I looked at the docs, this was the simple part)

    The key is, who cares if it is above 60 degrees, by doing the RotateTowards with a maximum, if it is above it, it simply makes it that.

    Code (csharp):
    1.  
    2.         // inbound vector
    3.         var a = -other.relativeVelocity * -1.01f;
    4.         // outbound vector
    5.         var b = Vector3.Reflect(a, other.contacts[0].normal);
    6.         // reverse inbound vector
    7.         var c = -a;
    8.  
    9.         // rotate the vector
    10.         b = Vector3.RotateTowards(c, b, 60 * Mathf.Deg2Rad, 0f);
    11.        
    12.         rb.velocity = b;
    13.  
     
  6. kikendo

    kikendo

    Joined:
    Jul 19, 2017
    Posts:
    61
    You're a star, thanks for the patience in explaining this. I will try it out immediately!
     
  7. kikendo

    kikendo

    Joined:
    Jul 19, 2017
    Posts:
    61
    I tried this out and it created some other issues, but I realized something important: not only I should control the max value, I should control the minimum value. A really low value (like, something below 15 degrees) creates as many problems as a too high value.

    RotateTowards seems only to care about a maximum degree, but minimum could be whatever. Setting the code above to use 1 instead of 60 shows the problem (makes the ball go almost straight).

    I'm gonna try my caveman approach I mentioned above and see what happens. But this almost caught it, thanks bigmisterb!
     
  8. kikendo

    kikendo

    Joined:
    Jul 19, 2017
    Posts:
    61
    OK I went ahead and figured this out without the need of using any angle math (but vector logic!)

    Consider a vector P:



    If this was my velocity, it'd be X=2, Y-3, Z=5

    In my example, the value of the velocity force in Z is linearly increasing, and I am looking at this straight on from that plane, so the movement I want to restrict lies in X and Y. If those get too crazy, the ball bounces like in either or both of those axis in a way I don't want it to, and movement becomes complicated to deal with interactively.
    So, the angle between X or Y and Z is what I am trying to contain.

    But then I thought, even if I know which angle this is and I can compare it to a range, how do I change it? Do I rotate? How will this look? Seemed like I was overkilling what should be simpler. So I started looking at the numbers.

    In which cases does the angle get crazy? I thought it was somewhere between 45 and 90 degrees. Probably 3/4 of the way there, so between 0 and 1, let's take 0.75.
    What did that tell me? That for every value of the vector in Z, if X or Y exceed in value 0.75 of that of Z, the angle will be unwanted. If you look at the graphic above, this becomes easy to figure out. Make a (0,0,Z) vector, then increase X or Y and see at which point the angle becomes too narrow.

    So I did this after reflecting my velocity vector:

    Code (CSharp):
    1.  
    2.  
    3.         maxAngle = tempVelocity.z*angleFactor; //angle factor is a float I defined earlier as public so I can change it, in my example, it will be 0.75
    4.         // When the angle is too big, it's because the value of X or Y is higher than an "angleFactor" of what is in Z
    5.         if ( tempVelocity.x  > Mathf.Abs(maxAngle) ) { tempVelocity.x = -maxAngle; }
    6.         else if ( tempVelocity.y > Mathf.Abs(maxAngle) ) { tempVelocity.y = -maxAngle; }
    7.  
    8.  
    Testing so far is very successful!

    Thanks for putting my brain to work, not only have I refreshed a lot of vector and angle math, I learned a damn lot during the process. Hope this helps someone else.
     
    Last edited: Jul 27, 2017