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

How do I perform these simple vector math changes?

Discussion in 'Scripting' started by Marscaleb, May 23, 2019.

  1. Marscaleb

    Marscaleb

    Joined:
    Jan 7, 2014
    Posts:
    1,036
    I'm pretty sure there is a simple way to do this, but I'm not that familiar with how vector math works.

    There are two vector adjustments I want to perform, but I don't know how to do them without applying the math to each axis individually, which takes up extra processing time.

    One is to make a vector point from one angle to another, over time.
    So if I have an object moving (1,0) and I want it to move (0,1), on each frame I could have each value grow/shrink by 0.05, and then after 20 frames it will be pointing in the right direction.
    But it would run faster if I could instead do some math on the vector itself, and say adjust its angle by x degrees every frame. It would also be easier to work with odd angles that way.

    The second thing I want to do is simply reduce the magnitude of a vector each frame.
    I can easily apply the math to each axis by taking that value for the individual axis and multiplying it by something like 0.05 each frame, but if I could multiply the whole vector by 0.05 wouldn't that be a faster calculation and thus more efficient? But I don't know how to do that to the vector directly.

    These both sound pretty simple to me, but I just don't have enough experience performing math on vectors to be familiar with how to do this.
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,773
    You can do this! So you have your two velocity vectors, say startVelocity and endVelocity. You can do one of two things with these:
    1) Move from startVelocity to endVelocity in a specified period of time (which I'll show in a coroutine, which is probably the best way to use this. This is referred to as Lerping (short for linear interpolation), and almost every basic data type in Unity has a Lerp function that works about the same way.
    Code (csharp):
    1. StartCoroutine(TurnGradually(startVelocity, endVelocity) );
    2.  
    3. ...
    4. public Vector3 currentVelocity = Vector3.forward; //this is whatever you use for the movement
    5.  
    6. IEnumerator TurnGradually(Vector3 start, Vector3 end) {
    7. float duration = 1f;
    8. for (float t=0f; t < duration; t += Time.deltaTime) {
    9. currentVelocity = Vector3.Lerp(start, end, t / duration);
    10. yield return 0;
    11. }
    12. currentVelocity = end;
    13. }
    2) Move by a specified number of degrees per frame towards your desired velocity - or, by using Time.deltaTime, a specified number of degrees per second (which I'll use here, because you should always use Time.deltaTime to make your code framerate-independent):

    Code (csharp):
    1. public Vector3 currentVelocity = Vector3.forward;
    2. public Vector3 targetVelocity = Vector3.up; //set this to change directions
    3. public float degreesPerSecond = 45f;
    4. void Update() {
    5. currentVelocity = Vector3.RotateTowards(currentVelocity, targetVelocity, degreesPerSecond * Time.deltaTime);
    6. }
    Note that this assumes these vectors are all length of 1 and it may behave oddly if they're not. You may use .normalized to enforce that. You should multiply in your speed outside of this calculation.

    You can just do this, they can be multiplied by numbers directly:
    Code (csharp):
    1. newVector = oldVector * 0.9f;
    It's not more efficient performance-wise but to be honest, you shouldn't care about efficiency on that scale. You'd have to be doing this calculation a few million times per frame before you'd notice any performance hit from math on this level.
     
  3. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    You can multiply a vector by a scalar by just using the normal multiplication operator; e.g.

    myVector = myVector * 0.95f;


    This probably isn't more efficient than multiplying each component separately--the computer still has to update all the components behind the scenes--but it's certainly more readable.

    For slowing changing one vector into another, if you already know the "endpoint", you probably want to use a Slerp. What you described was actually a lerp, but notice that changes the length of the vector while you are "rotating" it; e.g. the vector (5, 5) is not the same length as the vector (10, 0).
     
  4. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,773
    Ah, good call, I probably should've used Slerp in my example #1. I always forget it exists outside of Quaternion. ;)
     
  5. Marscaleb

    Marscaleb

    Joined:
    Jan 7, 2014
    Posts:
    1,036
    Wait, really? I thought for sure that was gonna cause a conflict because it's not the right type, and so I'd have to use some special kind of modifier to go between the float and the vector2.

    ...huh. Guess I'll use that.
     
  6. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    When you define a custom type, you also have the option to define how it interacts with standard operators like + and *. This is called "operator overloading".

    It has a bit of a bad reputation because of cases where someone defined the operator to mean something unusual, which can make code hard to understand. But using it to apply the standard mathematical meaning of the operator to expanded numerical types like vectors or complex numbers is exactly what it's for, and I would be upset with Unity if this didn't work.

    (There's also one weird use that people generally accept anyway, which is overloading + to be used for string concatenation. Lots of languages now do this by default--including C#. Of course that's not mathematically what "+" means, but it's simple and useful and well on its way to being considered standard...despite a few cases where it causes really confusing bugs if you aren't careful with your parentheses.)