3D Trigonometry

Discussion in 'Plugin Development' started by AGuyWhoSkis, Oct 2, 2013.

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

    AGuyWhoSkis

    Hey guys! I'm making a plugin which launches TNT via cannons. Right now the TNT has predesignated paths, but I'm working to improve that by making it launch to wherever you look at. Specifically I need a way to calculate a vector of TNT which goes from 1 block to another in an arc.
    I've made a drawing to demonstrate the process: http://sketchtoy.com/51223008 ;)
    Any ideas? Even just pointing me in the right direction to learn this would be great.
     
  2. Offline

    chasechocolate

    You would need to know the gravity of a falling primed TNT entity, but I did this for potions (yay for quadratics):
    Code:java
    1. public static final double POTION_GRAVITY = 0.115;
    2.  
    3. public static Vector getPotionVelocity(Location fromLoc, Location toLoc, int heightGain){
    4. Vector from = fromLoc.toVector();
    5. Vector to = toLoc.toVector();
    6.  
    7. //Block locations
    8. int endGain = to.getBlockY() - from.getBlockY();
    9. double horizDist = fromLoc.distance(toLoc);
    10.  
    11. //Height gain
    12. int gain = heightGain;
    13. double maxGain = (gain > (endGain + gain) ? gain : (endGain + gain));
    14.  
    15. //Solve quadratic equation for velocity
    16. double a = -horizDist * horizDist / (4 * maxGain);
    17. double b = horizDist;
    18. double c = -endGain;
    19. double slope = -b / (2 * a) - Math.sqrt(b * b - 4 * a * c) / (2 * a);
    20.  
    21. //Vertical velocity
    22. double veloY = Math.sqrt(maxGain * POTION_GRAVITY);
    23.  
    24. //Horizontal velocity
    25. double vH = veloY / slope;
    26.  
    27. //Calculate horizontal direction
    28. int distX = to.getBlockX() - from.getBlockX();
    29. int distZ = to.getBlockZ() - from.getBlockZ();
    30. double mag = Math.sqrt(distX * distX + distZ * distZ);
    31. double dirX = distX / mag;
    32. double dirZ = distZ / mag;
    33.  
    34. //Horizontal velocity components
    35. double veloX = vH * dirX;
    36. double veloZ = vH * dirZ;
    37.  
    38. //Actual velocity
    39. Vector velocity = new Vector(veloX, veloY, veloZ);
    40.  
    41. return velocity;
    42. }

    //EDIT: Perhaps this is possible with vectors? LucasEmanuel :p
     
    AGuyWhoSkis and 1Achmed1 like this.
  3. Offline

    se1by

    This just needs a bit of vector magic.
    Get the view vector with "player.getLocation().getDirection()".
    Launch the tnt in this direction with a specific force, and offset this force with the gravity and a final value for traveled blocks.
     
  4. Offline

    AGuyWhoSkis

    How would I go about making vectors which aim TNT just to the left and right of a vector produced by this method?
    By the way, thanks a ton for the code, I really appreciate it!
     
  5. Offline

    chasechocolate

    AGuyWhoSkis I wouldn't change the returned vector, but change the location parameters in the method.
     
  6. Offline

    blablubbabc

    chasechocolate Do you know how precise this is? Because as far as I am aware, there is "air drag" in minecraft, so the entities don't fly in a perfect parabolic curve, but more in a curve like shown in this image: http://de.wikipedia.org/wiki/Datei:Ballistik_bei_schuss_mit_gelaendewinkel.svg

    The drag for the different entities (and their acceleration values) can be found in the minecraft wiki: http://minecraft.gamepedia.com/Entity#Motion_of_entities

    In the past I also tried to find some equation for this (but I failed). So I solved it in a different attempt:
    In my case my vector's length (projectile's speed) is fixed (so I can't shoot infinite far targets).
    The idea is to prepare some sort of table with precalculated angles for the different locations (in range/each entry in the table represents one block location) and later pick the right one from it.

    I first create a table of fixed size (I think in my case I limited it to 100 blocks range in x-z-direction and 50 blocks up and 50 blocks down in y direction -> results in a 100 * 100 large table).

    I also limited the angles I am interested in to -45 degree to 45 degree and I limited the flight duration to something like 100 ticks.

    Then I took the acceleration and drag values from the minecraft wiki and "simulated" the entities motion in a loop, tick by tick, for each 1-angle-step between -45 and 45 degree, and inserted the current angle into the entry of my table, which represents the projectiles current location after 1 tick.

    After that I fill the remaining empty entries in my table with the nearest upper angle.

    And that's it. If I now need the angle to shoot a certain location in the range of my calculated table, I just do a quick lookup in my table.


    And here is the code (note, that, while it seems to work quite well, I have written this a long time ago and then never took a clear look on it again. So it is very likly that it can be improved.)

    Creating the table:

    Code:java
    1.  
    2. /**
    3.  * You are welcome to use, redistribute and modify your own copies of this source as long as you provide credit publicly to the original source.
    4.  *
    5.  * @author blablubbabc
    6.  */
    7.  
    8. private Double[][] table;
    9. private int ySize;
    10. private double fixedSpeed;
    11.  
    12. calculateTable(angleMin, angleMax, ticks, xSize, ySize, fixedSpeed);
    13.  
    14. private void calculateTable(int angleMin, int angleMax, int ticks,
    15. int xSize, int ySize, double fixedSpeed) {
    16. this.ySize = ySize;
    17.  
    18. // if ySize = 50 -> size = 2*50: y-Size in both directions, up and down:
    19. // 0=> 50
    20. table = new Double[xSize][2 * ySize];
    21.  
    22. // this are the acceleration and drag values for snowballs from the minecraft wiki
    23. Double drag = 0.01D;
    24. Double g = 0.03D;
    25. Double vyMin = -3.0D;
    26.  
    27. // default values
    28. for (int i = 0; i < table.length; i++) {
    29. for (int j = 0; j < table[i].length; j++) {
    30. table[i][j] = null;
    31. }
    32. }
    33.  
    34. for (int a = angleMin; a <= angleMax; a++) {
    35. Double tan = Math.tan(a * Math.PI / 180.0);
    36. // t=0:
    37. Double vx = Math.cos(a * Math.PI / 180.0) * fixedSpeed;
    38. Double vy = Math.sin(a * Math.PI / 180.0) * fixedSpeed;
    39. Double x = 0.0D;
    40. Double y = 0.0D;
    41.  
    42. for (int t = 1; t <= ticks; t++) {
    43. x += vx;
    44. y += vy;
    45. vx = vx * (1 - drag);
    46. if (vy > vyMin)
    47. vy = vy - g;
    48. vy = vy * (1 - drag);
    49.  
    50. int tx = x.intValue();
    51. int ty = y.intValue();
    52. if (tx < table.length && (ty + ySize) < 2 * ySize
    53. && (ty + ySize) >= 0) {
    54. table[tx][ty + ySize] = tan;
    55. }
    56. }
    57. }
    58. // 0 Location gets angle 0
    59. table[0][ySize] = 0.0D;
    60.  
    61. // fill empty squares between max and min angle curve:
    62. for (int x = 0; x < xSize; x++) {
    63. int yMin = 0;
    64. int yMax = 2 * ySize - 1;
    65. // find min:
    66. for (int y = 0; y < 2 * ySize; y++) {
    67. if (table[x][y] != null) {
    68. yMin = y;
    69. break;
    70. }
    71. }
    72. // find max:
    73. for (int y = 2 * ySize - 1; y >= 0; y--) {
    74. if (table[x][y] != null) {
    75. yMax = y;
    76. break;
    77. }
    78. }
    79. // fill empty squares between:
    80. for (int y = yMax - 1; y > yMin; y--) {
    81. if (table[x][y] == null) {
    82. // insert value from above
    83. table[x][y] = table[x][y + 1];
    84. }
    85. }
    86. }
    87. }
    88. [/i][/i]


    Can-be-shot-check and getAimVector:

    Code:java
    1.  
    2. private boolean canBeShoot(Vector pos, Vector target, Vector dir) {
    3. int x = ((Double) target.clone().setY(0).distance(pos.clone().setY(0))).intValue();
    4. int y = ((Double) (target.getY() - pos.getY())).intValue();
    5. if (x < table.length && (y + ySize) < 2 * ySize && (y + ySize) >= 0) {
    6. return (table[x][y + ySize] != null);
    7. } else {
    8. return false;
    9. }
    10. }
    11.  
    12. private Vector getAimVector(Vector pos, Vector target, Vector dir) {
    13. Vector aim = dir.clone().normalize();
    14. int x = ((Double) target.clone().setY(0).distance(pos.clone().setY(0))).intValue();
    15. int y = ((Double) (target.getY() - pos.getY())).intValue();
    16. Double tan = null;
    17.  
    18. if (canBeShoot(pos, target, dir)) {
    19. tan = table[x][y + ySize];
    20. } else {
    21. int yTarget;
    22. int xTarget;
    23. // find nearest y
    24. if ((y + ySize) >= 2 * ySize) {
    25. yTarget = 2 * ySize - 1;
    26. } else if ((y + ySize) < 0) {
    27. yTarget = 0;
    28. } else {
    29. yTarget = (y + ySize);
    30. }
    31. // find nearest x (x is always positive because of direction change)
    32. if (x > table.length) {
    33. xTarget = 0;
    34. } else {
    35. xTarget = x;
    36. }
    37.  
    38. // find max X for this y
    39. for (int tablex = table.length - 1; tablex >= 0; tablex--) {
    40. if (table[tablex][yTarget] != null) {
    41. tan = table[tablex][yTarget];
    42. break;
    43. }
    44. }
    45. if (tan == null) {
    46. // find max y for this x
    47. for (int tabley = 2 * ySize - 1; tabley >= 0; tabley--) {
    48. if (table[xTarget][tabley] != null) {
    49. tan = table[xTarget][tabley];
    50. break;
    51. }
    52. }
    53. }
    54. // shoot with nearly nearest angle:
    55. }
    56. if (tan == null) {
    57. // default angle 45; tan 45 = 1.619
    58. tan = 1.619D;
    59. }
    60. return aim.setY(tan).normalize().multiply(fixedSpeed);
    61. }
    62.  
    63. }
    64.  


    Between, the array of Double's does requires about 200kb I guess. So having one of those tables in memory shouldn't be a problem.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 4, 2016
  7. Offline

    chasechocolate

    blablubbabc good point, I hadn't really considered that.
     
  8. Offline

    Scizzr

    Loc
    Here's some code I used to fire snowballs from one player to another. No trig needed. Maybe it can be slightly adjusted (by adding a calculated height and extra multiply() value).
    Code:
    Player p1 = event.getPlayer();
    Player p2 = ???;//other player
    Location locSrc = p1.getEyeLocation().add(0.0D, 0.25D, 0.0D);
    Location locDest = p2.getEyeLocation();
    Vector trans = new Vector(locDest.getBlockX()-locSrc.getBlockX(), locDest.getBlockY()-locSrc.getBlockY(), locDest.getBlockZ()-locSrc.getBlockZ());
    Snowball snowball = (Snowball)locSrc.getWorld().spawnEntity(locSrc, EntityType.SNOWBALL);
    snowball.setShooter(p1);
    snowball.setVelocity(trans.normalize().multiply(2.0D));
    
     
  9. Offline

    LucasEmanuel


    Assuming that Notch based the air-resistance somewhat on reality, you might be able to use real-world math to calculate the trajectory. By doing so, you could be able to calculate what the initial velocity needs to be and the inclination needed in order to get the object from one spot to the other. :)
     
  10. Offline

    blablubbabc

    Also note that there are multiple possible arcs from the source to the target, if both, angle and velocity are variable:

    You could either shoot a low, more direct arc with a very high velocity, or you shot a higher arc, with a lower velocity.
     
  11. Offline

    LucasEmanuel

    Precisely, you could replicate a modern artillery-cannon that way and make it fire the TNT with different inclination and force to make several explosives hit the target at the same time :)
     
Thread Status:
Not open for further replies.

Share This Page