[Tutorial] How to calculate vectors

Discussion in 'Resources' started by LucasEmanuel, Apr 2, 2013.

Thread Status:
Not open for further replies.
  1. Offline

    LucasEmanuel

    There seemed to be a lack of good guides on how vectors in minecraft work. So here is one now. :)

    Index:

    0. About this.
    1. What is a vector?
    2. What is pitch and yaw?
    3. Calculate a vector based on pitch and yaw.
    4. Calculate a vector between two locations in the world.
    4.1 Using pitch and yaw (not very performance friendly)
    4.2 Using vector subtraction (recommended)
    5. Some other fun stuff in regards to vectors.
    5.1 Shoot something through the air in a parabolic motion


    0. About this.
    Most of the calculations in this tutorial can be performance heavy and should not be done frequently on a popular server or a server with limited resources.

    If something can be done using the built in methods in the Vector-object, use those instead.

    1. What is a vector?
    Lets start from the beginning.

    A vector is something with a direction. It could be a velocity in a certain direction, an acceleration in a direction, a force in a direction, etc.

    Vectors are mostly used in physics to describe movements for example. In minecraft vectors are a velocity in a direction.

    You can think of vectors as big arrows that point in the direction an object or an entity is moving.
    The length of the vector determines the force/acceleration/velocity the object is moving.

    To calculate a vector in Minecraft we have to work with spherical polar coordinates (we'll get to that in point three). These are not the same coordinates as the players or some other entities location in the world. For example, a players X, Y and Z coordinates in the world is not the same as the players vector X, Y and Z coordinates.

    2. What is pitch and yaw?
    The pitch determines the angle up/down the player is looking in. This is in degrees.
    For some reason Notch has made it so that when a player is looking straight ahead (horizontally) the angle is 0 degrees. Straight up is -90 degrees and straight down is 90 degrees. We will have to correct for that when we calculate the vector later.

    The yaw determines the angle the player is rotating horizontally in. This is also in degrees.
    This is also rotated 90 degrees which we have to correct for. A full rotation is 360 degrees.

    3. Calculate a vector based on pitch and yaw.

    NOTE: This is more of an example, use location.getDirection() to get the same result.
    Alright, lets get to the fun stuff!

    For an in depth look: http://en.wikipedia.org/wiki/Spherical_coordinates

    To calculate a vector we need to calculate three new coordinates, X, Y & Z (please note what i said in point 1), these three coordinates determines the direction and size of the vector.

    But first we need to convert the players pitch and yaw from degrees into radians and correct for what i stated above as well as one more thing.

    To convert degrees into radians we do this.

    Lets convert then.
    Code:
    double pitch = ((player_location.getPitch() + 90) * Math.PI) / 180;
    double yaw  = ((player_location.getYaw() + 90)  * Math.PI) / 180;
    The "+ 90" adds 90 degrees to the players pitch/yaw which corrects for the 90 degree rotation Notch added.

    Now we can start calculating the coordinates for the vector.

    Code:
    x = Math.sin(pitch) * Math.cos(yaw);
    y = Math.sin(pitch) * Math.sin(yaw);
    z = Math.cos(pitch);
    These three lines retrieves the Cartesian coordinates "hidden" in the spherical polar coordinates.

    Congratulations! You have now calculated your very own vector! :D
    Lets use it. :)

    Code:
    Vector vector = new Vector(x, z, y);
    If you take a closer look on the line above, you can see that i switched place of the Z and Y coordinates. This is the third correction I spoke about. Normally the Z-axis defines the height, but in Minecraft it is the Y-axis that does that. So we have to "rotate" our vector to correct for that.

    Lets do something fun with our new vector, like fire an arrow in the direction the player is looking:
    Code:
    player.shootArrow().setVelocity(vector);
    Or why not throw the player in the direction he's looking with a magnitude of 10:
    Code:
    player.setVelocity(vector.multiply(10));


    Or we could throw the player backwards, but then we have to add 180 degrees to the players yaw and "invert" the pitch so that the vector points directly backwards relative to the player.

    Code:
    double pitch = ((90 - player_location.getPitch()) * Math.PI) / 180;
    double yaw  = ((player_location.getYaw() + 90 + 180) * Math.PI) / 180;
    4. Calculate a vector between two locations in the world.

    Lets say we have two locations in the world and we want to calculate a vector that could be used to fire an arrow from one of the locations to the other.

    4.1 Using pitch and yaw (performance heavy!)
    First we have to calculate ΔX, ΔY and ΔZ between the locations (in the code they are called dX, dY and dZ):

    Code:
    double dX = first_location.getX() - second_location.getX();
    double dY = first_location.getY() - second_location.getY();
    double dZ = first_location.getZ() - second_location.getZ();
    Then we have to calculate the pitch and yaw between them aswell.

    The yaw is rather simple:
    Code:
    double yaw = Math.atan2(dZ, dX);
    It's a little bit more tricky with the pitch:
    Code:
    double pitch = Math.atan2(Math.sqrt(dZ * dZ + dX * dX), dY) + Math.PI;
    In order to get an accurate pitch we have to use the Pythagorean theorem which uses a square-root function. I would not recommend calculating the square-root of something too often on a big server since it could potentially be a performance issue.

    Alright, we now have what we need, lets calculate the vector as in point 3 and fire an arrow from the first location:
    Code:
    double X = Math.sin(pitch) * Math.cos(yaw);
    double Y = Math.sin(pitch) * Math.sin(yaw);
    double Z = Math.cos(pitch);
     
    Vector vector = new Vector(X, Z, Y);
     
    first_location.getWorld().spawnArrow(first_location, vector, (float) 3, (float) 0);
    4.2 Using vector subtraction (recommended)
    This method does not require using a performance heavy square root function to calculate the vector and is then the method i recommend.

    First i want to say thanks to blablubbabc for this clever method of converting two locations into vectors and then using simple subtraction to generate a vector pointing from one location to the other. :)

    It's as simple as this:
    Code:
    Vector vector = second_location.toVector().subtract(first_location.toVector());
    Basically what that line does is:
    Code:
    Vector from = new Vector(first_location.getX(), first_location.getY(), first_location.getZ());
    Vector to  = new Vector(second_location.getX(), second_location.getY(), second_location.getZ());
     
    Vector vector = to.subtract(from);
    Then you could fire an arrow:
    Code:
    first_location.getWorld().spawnArrow(first_location, vector, (float) 3, (float) 0);

    5. Some other fun stuff in regards to vectors.

    5.1 Shoot something through the air in a parabolic motion
    http://forums.bukkit.org/threads/setvelocity-vector-and-parabolic-motion.86661/


    If you think "I'm out taking a ride on the bike" (as we say in Sweden, means that I have no idea of what I'm doing. ;)) Let me know and I'll correct it :)
     
    MarinD99, MrJossy, Skionz and 29 others like this.
  2. Offline

    microgeek

    LucasEmanuel
    Great tutorial! I'm sure this will help countless people, and is very well written and easy to follow.
     
    LucasEmanuel likes this.
  3. Offline

    chasechocolate

    LucasEmanuel Nice tutorial :) Very well written and helped me understand Vectors a little bit more :D Thanks!
     
    Stigosaurus likes this.
  4. Offline

    LucasEmanuel

    Here is a great youtube video with a better in depth description of spherical coordinates:
     
    chasechocolate and microgeek like this.
  5. Offline

    blablubbabc

    Hey, first, good idea for this tutorial.
    Second:
    You could shorten the:
    "4. Calculate a vector between two locations in the world."-part to:

    Vector getFromTo(Location locFrom, Location locTo) {
    return locTo.toVector().subtract(locFrom.toVector());
    }

    Which is basicly what you are doing here:
    double dX = first_location.getX() - second_location.getX();
    double dY = first_location.getY() - second_location.getY();
    double dZ = first_location.getZ() - second_location.getZ();

    Just the other way round, and without all this pitch and yaw stuff which comes afterwards.

    Also something else, which is nice and I often use: get a Vector to length 1 -> vector.normalize()

    If a vector is at length 1 you can simple multiply it with any number to get it on this length. This is usefull for setting the speed of, for example, arrows, when launching them.
     
  6. Offline

    LucasEmanuel

    Thanks, I'll fix it when i get time for it. :)

    To be honest, i haven't been looking so much at the vector API, I'm mostly using what i learned in high school about polar coordinates. :)
     
  7. Offline

    nisovin

    For part 3, while that works, it is also already built in to Bukkit:

    Code:
    Vector vector = player_location.getDirection();
     
  8. Offline

    LucasEmanuel

    I knew that, but as the title said, it's how to calculate a vector based on pitch and yaw. :) That way you could alter the pitch or yaw to make it point in any direction relative to the player by just adding or removing radians :)
     
  9. Offline

    DarkBladee12

    LucasEmanuel Thanks for this great and detailed tutorial, I was already looking for something like this but found nothing, but then I saw yours recently! ;)
     
  10. Offline

    zeddio

    Great Tutorial!
    And your from Sweden too?! :)
     
  11. Offline

    Scizzr

    Nice guide, but pitch is actually the opposite of what you have. Straight up is -90 and straight down is 90. A simple PlayerMoveEvent listener will verify:
    Code:
    @EventHandler(priority = EventPriority.NORMAL)
    public void onPlayerMove(PlayerMoveEvent e) {
        Player p = e.getPlayer();
        p.sendMessage("" + p.getLocation().getPitch());
    }
    
     
  12. Offline

    LucasEmanuel

    You are correct, I need to start checking my posts for typos a little better ;)
     
  13. Offline

    Regenwurm97

    Hey I have a quick question to your tutorial :)

    In Point 3 you convert the players pitch & yaw to "radian coordinates", not?
    So now double pitch & double yaw are these radians, right? But arent they what we wanted? In the next step, you convert them back to the "normal coordinates"

    But why did we then first convert the pitch & yaw to radians and then back to their original state? Think I didnt understand right :l

    Another thing I wanted to know is: What is the x, the z and the y of the vector.
    When the players pitch & yaw is converted to a vector, mustn't the length of the vector arrow equals 0?

    SO I mean converting the pitch and yaw to a vector results what exactly? How does the arrow look like?
    And (lastly) what do the x, z and y describe? So x is the length on one direction and y the height and z the length or what?
     
  14. Offline

    LucasEmanuel

    The pitch and yaw are angles that represent at what angle the player is looking, they are not coordinates, when i convert them into radians (an alternative to degrees) they are still angles.

    I have to convert them into radians because the Math.cos() and Math.sin() methods can't work with degrees, they only accept radians.

    Then i have to convert the pitch and yaw into coordinates (not world-coordinates as in a players location but as in coordinates that define a vector, which is a separate coordinate-system).

    I drew an illustration to help :)

    X-coordinates = blue line
    Y-coordinates = yellow line
    Z-coordinates = green line

    Yaw = grey line
    Pitch = brown line

    In the picture i accidentally called the pitch for yaw (just below the illustration) and vice versa so just ignore that ;)

    The vector = red line

    [​IMG]



    An illustration from wikipedia:
    [​IMG]
     
    Garris0n likes this.
  15. Offline

    Regenwurm97

    Wow your illustration is awesome :eek:
    Took me some time to get into the 3D perspective but finally understood it! Thx so much man!

    Just one last question for understanding:
    So the blue, green and yellow line are the "normal player coordinates" right?
    Do the x, y and z of vectors (I now know they dont fit into the normal player coordinate system) describe the point of the end of the arrow? Or what do they describe?
     
  16. Offline

    LucasEmanuel

    1) No, this is an entirely separate coordinate-system, the X, Y and Z coordinates for the vector has absolutely nothing to do with the X, Y and Z coordinates for the player.

    2) Yes, the X, Y and Z coordinates describe the position of the end of the arrow. The vector (arrow) starts in the origin of the coordinate-system (where the X, Y and Z coordinates are all zero) and ends where the X, Y and Z coordinates meet up, as you can see in the wikipedia illustration :)
     
  17. Offline

    Regenwurm97

    Ahh :)
    Ok, thx alot ;)
     
  18. Offline

    CeramicTitan

    LucasEmanuel

    So if I wanted to create a scatter effect with arrows, I know I would use vectors, I'm just not sure how I would go about it.

    Here is what I am trying to do: http://tinypic.com/r/vy45mo/5
     
  19. Offline

    Ultimate_n00b

    This is very nice and useful, it will help me a lot.
     
  20. 2. What is pitch and yaw?
    The pitch determines the angle up/down the player is looking in. This is in degrees.
    For some reason Notch has made it so that when a player is looking straight ahead (horizontally) the angle is 0 degrees.
    I think you meant to say (vertically) instead of (horizontally)
     
  21. Offline

    xTrollxDudex

    iiHeroo and Acer_Mortem like this.
  22. Offline

    iiHeroo


    Not only is that such a bump, but if I'm correct, horizontally is correct, but that might explain again why I fail math, anyways, if it was vertically, wouldn't that be 0 degree's everywhere then? YET AGAIN, that might explain why I fail math.
     
  23. Offline

    LucasEmanuel

    No I meant horizontally as in the line from the players eyes and straight forward is exactly horizontal :)
     
  24. Ah okay, but shouldn't that be after you define yaw?
     
  25. Offline

    M0n0clink

    Nice tutorial ! I like your tornado too :)
     
  26. Offline

    unon1100

    Let's say that you want to set a player's velocity to go towards a point on the map. When you create the vector, you get really huge numbers, and the server will rubber-band you back to where you started, because you moved too fast. You want people to move a specific speed, though, no matter how far away they are from the point, so multiplying by a decimal won't work. Also, there is no setLength() method. How do you do this?
    What you need to do is first normalize the vector. "Normalize" is a term that is used in pre-calculus when dealing with vectors, it means to set the length equal to 1. Bukkit luckily has a built-in method for this! After normalizing, you can easily multiply it by whatever you want your speed to be! (Note that in the code below, I just declared the vector with large numbers, rather than using 2 points, since you already learned how to do that).
    Code:java
    1. Vector v = new Vector(30.135, 4.123, 44.98854); //Declare our vector
    2. v.normalize(); //This sets the vector length to 1, and makes sure that the vector is the same angle
    3. v.multiply(3); //This multiplies each value by 3.
    4. player.setVelocity(v);

    Or, because normalizing returns a vector, you can even do this for shorthand:
    Code:java
    1. Vector v = new Vector(30.135, 4.123, 44.98854); //Declare our vector
    2. v.normalize().multiply(3); //This sets the vector length to 1, and makes sure that the vector is the same angle
    3. player.setVelocity(v);
     
  27. Offline

    LucasEmanuel

    Update:
    Added link to thread in regards to parabolic motion. :)
     
    Garris0n and DSH105 like this.
  28. Offline

    DevRosemberg

    LucasEmanuel I was trying to make a method to be able to rotate a vector around a certain axis so i could set the velocity of an entity and make it rotate in its location though when i apply the velocity it seems to fly away at a really fast speed making a king of cone that goes getting smaller each time (as i could tell from the tons of Locations i debugged), here is the code:

    Code:java
    1. public static Vector rotateVectorAroundAxis(Vector vector, VectorAxis axis, double whatAngle) {
    2. double x;
    3. double y;
    4. double z;
    5. double sin;
    6. double cos;
    7.  
    8. switch (axis){
    9. case X:
    10. cos = Math.cos(whatAngle);
    11. sin = Math.sin(whatAngle);
    12.  
    13. y = vector.getY() * cos - vector.getZ() * sin;
    14. z = vector.getY() * sin + vector.getZ() * cos;
    15.  
    16. return vector.setY(y).setZ(z);
    17. case Y:
    18. cos = Math.cos(whatAngle);
    19. sin = Math.sin(whatAngle);
    20.  
    21. x = vector.getX() * cos + vector.getZ() * sin;
    22. z = vector.getX() * -sin + vector.getZ() * cos;
    23.  
    24. return vector.setX(x).setZ(z);
    25. case Z:
    26. cos = Math.cos(whatAngle);
    27. sin = Math.sin(whatAngle);
    28.  
    29. x = vector.getX() * cos - vector.getY() * sin;
    30. y = vector.getX() * sin + vector.getY() * cos;
    31.  
    32. return vector.setX(x).setY(y);
    33. }
    34.  
    35. return null;
    36. }

    And here is the part where i was actually setting the velocity:

    Code:java
    1. s.setVelocity(MathUtil.rotateVectorAroundAxis(s.getLocation().toVector(), VectorAxis.Y, 45));
     
  29. Offline

    Scorpion_vn

    Hi, I thought better ask here, rather than start new thread:

    Why vector add / subtract does not work as expected?

    My code is at https://github.com/minetonight/Port...etonight/portalunstuck/PortalUnstuck.java#L72

    I wish to check blocks around the player, so I use unity vectors of +/-1 in X and Z directions.
    But #add does not add negative vectors and #subtract does not subtract positive vectors, what is going on?

    Some output:
    Expected values are 403 / -272 and 404/-273, but not.

    What am I missing? Thanks in advance



    [edit]
    The more I debug, the weirder it gets:
    Next code change https://github.com/minetonight/Port...etonight/portalunstuck/PortalUnstuck.java#L72 and now my coordinates move by TWO units, how comes:

     
  30. Offline

    RingOfStorms

    Non primitive data types are passed by reference and not cloned when you make a new variable and set equal to it.

    My hint to you is the location.clone() method. When you add/subtract it will keep changes through your entire looping.
     
Thread Status:
Not open for further replies.

Share This Page