Resource Drawing a line

Discussion in 'Spigot Plugin Development' started by finnbon, Aug 29, 2016.

  1. Hello people of spigot!

    Today, as a start of my new & last high school year, I decided to write a tutorial on how to display a line of particles using a Vector. If you don't know what a vector is, you should read the text down below, but I also recommend googling some stuff about vectors.

    Vectors

    A vector can be used for different things, but in our case, you should imagine it as an arrow with a direction and a size. A vector has 3 values, x, y and z. These are the numbers which represent the direction.

    We're gonna use a 3D graph as an example. Let's say we have a vector with x = 3, y = 2 and z = 5. Let's put a dot at that coördinate; 3, 2, 5. Now, we draw a line from the origin (0, 0, 0) to our coördinate (3, 2, 5). This line is our vector. It's a arrow pointing from one point to another. If we'd multiple our vector by 2, we'd get
    x = 3 * 2 = 6
    y = 2 * 2 = 4
    z = 5 * 2 = 10
    This gives us different values, but the same direction. The direction of the vector stayed the same, the size changed.

    Vectors are also used for moving objects, such as cars or falling objects. The faster the object is moving, the greater the size of the vector.

    In Bukkit, we can apply vectors to entities, to move them. By reading the above, you should have an idea on how this works now.

    In Bukkit, you can add vectors to locations. This simpl moves the location in the vector's direction. If we'd have a vector with x = 5, y = 3, z = 6, and we'd add that to a location at x = 42, y = 92, z = 102, we'd get:
    x = 42 + 5 = 47
    y = 92 + 3 = 95
    z = 102 + 6 = 108
    The location would now be at x = 47, y = 95, z = 108. For drawing a line, we're going to make use of this.

    How we draw the line

    Now, like every particle shape/animation, we can't make a full shape. We have to pick parts on our shape, and place particles on a certain interval. This also goes for the line, we will be placing particles on a line with a certain space between them. In this tutorial we'll leave a gap of 0.5 blocks between each particle.

    The way of drawing the line is rather simple if you think about it. First, we get a location for the start position of the line. Then, we need a vector for the direction. After this, we make a for-loop with a double which will run until it has reached the desired lenght of the line. Then each iteration, add the desired size of the gap between each particle to the for-loop's double.

    Then each iteration, we multiply the vector by the for-loop's double. Then we add the vector to our start location to move it in the vector's direction, with the current size (the size is the for-loop's double). Then we display a particle at that location, and subtract the vector from that location to move it back to the start again. Then we call Vector#normalize() on the vector. This set's the length to 1, so we can use it next iteration as well. If we wouldn't do this, something like this would happen:

    Lenght of the vector starts at 1.
    First iteration:
    - Multiply vector by 0.1.
    - The lenght is now 0.1
    Second iteration:
    - Multiply vector by 0.2.
    - The lenght is now 0.1 * 0.2 = 0.02.
    Third iteration:
    - Multiply vector by 0.3
    - The length is now 0.02 * 0.3 = 0.006.
    etc

    However, on the second iteration the lenght should be 0.2 and on the third it should be 0.3

    Coding

    Now let's move on to coding. Like it says above, we need a start location, a vector for direction, and a for-loop.
    Code (Java):
    Location start = /* whatever */;
    Vector dir = /* whatever */;
    for (double i = 0; i < 10 /* 10 is the length of the line */; i += 0.5 /* 0.5 is the gap between each particle */) {
       
    }
    Now, we can start moving our location and displaying and stuff. Again, like it says above, we multiple, add, display, subtract, normalize.
    Code (Java):
    Location start = /* whatever */;
    Vector dir = /* whatever */;
    for (double i = 0; i < 10 /* 10 is the length of the line */; i += 0.5 /* 0.5 is the gap between each particle */) {
        dir.multiple(i); // multiply
        start.add(dir); // add
        // display particle at 'start' (display)
        start.subtract(dir); // subtract
        dir.normalize(); // normalize
    }
    This is all you have to do! As you can see it's easier than you might think. As a little addition, I'll also explain how to draw a line from a given direction to another.

    Between 2 points

    This is as simple as getting the vector between both locations. Simply get the end location, subtract the start location from it and turn it into a vector:
    Code (Java):
    end.subtract(start).toVector();
    NOTE: This will modify the end location. If you don't want this, you can clone end by putting #clone() in between end and #subtract(start).

    Thanks for reading! I hope this was useful and if it was don't hesitate to leave a rating! :)

    - Finn
     
    #1 finnbon, Aug 29, 2016
    Last edited: Dec 28, 2016
    • Useful Useful x 20
    • Like Like x 8
    • Winner Winner x 2
    • Friendly Friendly x 2
  2. Choco

    Moderator

    :OOO You lil' bugger! I was just going to make a resource similar to this! Haha. Well done though ^-^
    EDIT: I have learned to leave the mathematical resources to you ;)
     
    • Like Like x 2
    • Friendly Friendly x 1
  3. YEES!!! I HAVE BEEN WAITING FOR THIS MOMENT! I NEEDED THIS TUTORIAL SO BAD... TANK U GOD
     
    • Like Like x 2
  4. Great tutorial! I love how this actually makes sense now because I'm taking higher level geo :p
     
    • Like Like x 2
  5. I have a quick question, if I want to make an entity go in the same direction forever, how would I do that with vectors? Its just a random question that popped up in my mind so I can understand vectors better :)
     
  6. Choco

    Moderator

    You could create a vector and using a runnable, constantly set the entity's velocity to the vector you had created ^-^ If you really wanted to have fun, you could use sin/cos to set different x and z values of the Vector to make the entity move in circles ;) (EDIT: Actually... I want to do that now. Lol)
     
    • Agree Agree x 1
  7. As Choco said, simply get a vector for the direction and multiple it by your desired speed. Then apply it to the entity (Entity#serVelocity(Vector);) within a (Bukkit)Runnable.
     
    • Like Like x 1
  8. Thank you!
     
    • Like Like x 1
    • Friendly Friendly x 1
  9. Choco

    Moderator

    @avighnash @finnbon
    I have taught the Pigs how to properly move in circles... You will thank me later



    Code (Java):
        private Pig pig;
        private BukkitRunnable pigRunnable;
       
        private final double radius = 3;
       
        @EventHandler
        public void onToggleSneak(PlayerToggleSneakEvent event){
            final Player player = event.getPlayer();
            final Location loc = player.getLocation();
           
            // Sneak is on
            if (event.isSneaking()){
                pig = (Pig) player.getWorld().spawnEntity(loc.subtract(0, 0, radius), EntityType.PIG);
                this.pigRunnable = new BukkitRunnable(){
                   
                    private double theta = 0.0;
                    private final Vector velocity = new Vector(0, 0, 0);
                   
                    @Override
                    public void run() {
                        this.velocity.setX(Math.cos(theta) * radius);
                        this.velocity.setZ(Math.sin(theta) * radius);
                        this.velocity.normalize().multiply(0.4);
                        pig.setVelocity(velocity);
                       
                        theta += Math.PI / 16;
                    }
                };
                this.pigRunnable.runTaskTimer(this, 0L, 2L);
            }
           
            // Sneak is off
            else{
                this.pig.remove();
                this.pig = null;
                this.pigRunnable.cancel();
                this.pigRunnable = null;
            }
        }
    Probably not the best way to do this, but oh well ;)
     
    • Winner Winner x 2
  10. What have you done.
    The pigs will now take over the world! :eek:
     
    • Like Like x 1
  11. Excellent resource fin! :D
     
    • Like Like x 1
  12. Already done that with my MiniMe



    couple seconds into the video

    we got babies in mc running around in circles.

    Edit: Dat lag
     
    • Funny Funny x 1
  13. Choco

    Moderator

    Haha HE'S SO CUTE :D Kind of creepy though. I think it's just the face that constantly bobs side to side that just gives me the chills ;P
     
    • Agree Agree x 3
    • Like Like x 2
  14. this was a bug i had while making a pokemon arena plugin a loonnngg time ago:
    https://i.gyazo.com/9103902e1608cb075a67ee2bbedeb5d6.gif


    yes those are full circles and it was basically a blackhole
     
    • Funny Funny x 1
  15. I followed the tutorial and when I put player.getLocation() for the start and player.getDirection() for the direction I spawned particles only at my feet and not in a line?
     
  16. use getEyeLocation
     
  17. @CJP10 Tried it, still only spawned 1 particle at my feet. Here's my code if you need it.
    Code (Text):
    Location start = event.getPlayer().getLocation()
                            Vector dir = event.getPlayer().getEyeLocation().toVector();
                            for(double i = 0; i < 30; i += 0.5) {
                                dir.multiply(i);
                                start.add(dir);
                                start.getWorld().spawnParticle(Particle.REDSTONE, start, 10);
                                start.subtract(dir);
                                dir.normalize();
                            }
     
    #19 Squanchy, Aug 30, 2016
    Last edited: Aug 30, 2016
  18. Honestly, I never really understood Vectors.. but then I found this tutorial, and now it all makes sense. Thanks @finnbon <3
     
    • Like Like x 1