Solved Spawn Particle trail from Player to Location

Discussion in 'Spigot Plugin Development' started by gordanb740, Nov 20, 2020.

  1. I want to make it so whenever you throw a trident, particles will spawn as a "trail" from where the player is standing to where the trident landed (Just to be clear, I don't want particles to follow the trident exactly, it should just go straight from the player to where it landed). I'm not quite sure how to do this though. I would have to save the two locations of course. The player and the location where the trident landed, but what then?

    I thought I would have to make a for loop that spawns particles if "i" is smaller or greater than the second location, right? But how would I do that considering both X and Z need to be looked at, and also whether or not they are negative. Am I complicating things too much here or am I on the right track?

    If it's interesting at all, this is what I have so far: It spawns redstone particles in the direction the player is looking. It's just a little experiment and it's a start, but I'm not sure how to proceed.
    Code (Java):
    @EventHandler
        public void onRightClick(PlayerInteractEvent e) {
            if(e.getAction() == Action.RIGHT_CLICK_AIR || e.getAction() == Action.RIGHT_CLICK_BLOCK) {
                if(e.getPlayer().getInventory().getItemInMainHand().getType() != null && e.getPlayer().getInventory().getItemInMainHand().getType() == Material.BRICK) {
                    e.getPlayer().getWorld().playSound(e.getPlayer().getEyeLocation(), Sound.ENTITY_WITHER_SHOOT, 0.3f, 0f);
             
                    for(int i = 0; i < 1000; i++) {
                        Location loc = e.getPlayer().getEyeLocation().add(e.getPlayer().getEyeLocation().getDirection().multiply(0.2 * i));
                 
                        loc.getWorld().spawnParticle(Particle.REDSTONE, loc.getX(), loc.getY(), loc.getZ(), 0, 0.001, 1, 0, 1, new Particle.DustOptions(Color.PURPLE, 1));
                    }
                }
            }
        }
    Also, would it be best to use the ProjectileHitEvent for this? It might be a bit difficult since I would have to mark the projectile with the Player's name in order to get their location, but that's the least of my worries right now.
     
    #1 gordanb740, Nov 20, 2020
    Last edited: Nov 21, 2020
  2. Here is how I would approach this:

    1. Subtract the player location from the trident location. This gives you a vector that points from the player to the trident.
    2. Store the length of that vector in a variable
    3. Normalize the vector, then multiply it by how frequently you want the particles to spawn (suggest like 0.5 to start with, so one particle every 1/2 block)
    4. Start with a clone of the player's location
    5. Make a for() loop from 0 to the vector length that you store, incrementing by the frequency (0.5)
    6. Spawn a particle in the loop at the current location, then add your vector to the location

    Good luck!
     
    • Like Like x 1
    • Agree Agree x 1
    • Useful Useful x 1
  3. Thanks for the help. How do you subtract a Location from a Location though? Just subtracting each XYZ integers? Sorry, I'm a bit overwhelmed since I've always had difficulties with Vectors in programming.
     
    #3 gordanb740, Nov 21, 2020
    Last edited: Nov 21, 2020
  4. You get the 2 locations as Vectors, substract, and then loop. A bit like this

    Code (Java):
            Vector playerLocVector = player.getLocation().toVector();
            Vector tridentLocVector = trident.getLocation().toVector();
            Vector directionVector = playerLocVector.subtract(tridentLocVector).normalize();
           
            for(...) {
                // Loop here with playerLocVector + directionVector * 0.5
                // Until the vector has passed tridentLocVector. You could do this
                // by doing playerLocVector.subtract(tridentLocVector).length() for example.
            }
     
     
    • Funny Funny x 1
    • Winner Winner x 1
    • Friendly Friendly x 1
  5. Thanks! So on what coordinates do I spawn the particle effects at? I can't use "i" in the loop obviously since it only goes from 0 to the tridentLoc position. This is a really tricky one I must say, here is what I have so far:
    Code (Java):
    @EventHandler
        public void onProjectileContact(ProjectileHitEvent e) {
            if(e.getEntityType() == EntityType.TRIDENT && e.getEntity().hasMetadata("Explosive")) {
                Projectile trident = e.getEntity();
                World world = e.getEntity().getWorld();
                Player player = (Player) e.getEntity().getShooter();
                Location playerLoc = player.getLocation();
                Location tridentLoc = trident.getLocation();
             
                Vector playerLocVector = playerLoc.toVector();
                Vector tridentLocVector = tridentLoc.toVector();
                Vector directionVector = playerLocVector.subtract(tridentLocVector.normalize());
             
                for(double i = 0; i < playerLocVector.subtract(tridentLocVector).length(); i++) {
                    world.spawnParticle(Particle.REDSTONE, X, Y, Z, 0, 0.001, 1, 0, 1, new Particle.DustOptions(Color.PURPLE, 1));
                                                       // X Y Z ^ ?
                }
            }
        }
     
  6. So some basic Vector math. What the formula for a location on the vector looks like is normally
    Code (Text):
    (pX,pY) = (sX, sY) + Lambda * (dX, dY);
    with p as current point, s as starting point, d as direction, lambda is amount.

    So to get the points, you can use i as the lambda in the formula. So you'd do
    Code (Text):
            Vector addedDirectionVector = directionVector.multiply(i);
            Vector particlePointVector = playerLocVector.add(addedDirectionVector);
            // use particlePointVector.getX(), getY(), getZ();
     
    Also in your line
    Code (Java):
               Vector directionVector = playerLocVector.subtract(tridentLocVector.normalize());
    Make sure you put the #normalize after substract(), not inside substract. What normalize does is change the length of the vector to 1.
     
    • Useful Useful x 1
  7. Thanks! Sorry to keep bothering you with this but there are still some weird things happening. For example:
    Code (Text):
    playerLocVector.subtract(tridentLocVector).length()
    This for some reason returns "Infinity" and therefore the for loop never stops which of course crashes the server. Any ideas why?
     
  8. Yeah I took over your code, tested it a bit and had some weird issues as well. It turns out that manipulating vectors also changes the vector object, rather than returning a clone. I've rewritten it with the clone() function called whenever I would change it, so I could still use the old object.

    Also as update from a mistake from my side, I switched the direction to trident - player rather than player - trident.
    Code (Java):
        @EventHandler
        public void onProjectileContact(ProjectileHitEvent e) {
            if (e.getEntityType() == EntityType.TRIDENT && e.getEntity().hasMetadata("Explosive")) {

                Projectile trident = e.getEntity();
                World world = e.getEntity().getWorld();
                Player player = (Player) e.getEntity().getShooter();

                //Make sure player hasn't disconnected mid throw.
                if (player == null) return;

                Vector playerLocVector = player.getLocation().toVector();
                Vector tridentLocVector = trident.getLocation().toVector();
             
                // Calculate vector between with "direction = b - a"
                Vector betweenPandTVector = tridentLocVector.clone().subtract(playerLocVector);
                Vector directionVector = betweenPandTVector.clone().normalize();

                for (int i = 0; i < betweenPandTVector.length(); i++) {
                    Vector particlePoint = playerLocVector.clone().add(directionVector.clone().multiply(i));
                    world.spawnParticle(Particle.REDSTONE,
                            particlePoint.getX(),
                         
                            //Make it a bit higher so it doesn't go through the feet.
                            particlePoint.getY() + 1,
                            particlePoint.getZ(),
                            0, 0.001, 1, 0, 1,
                            new Particle.DustOptions(Color.PURPLE, 1));
                }
            }
        }
     
     
    • Winner Winner x 1
  9. Wow it really works now, thank you so much! I marked the thread as Solved since you solved the issue I had, however there is one last thing I need to do. Instead of the particles spawning in a completely straight line, I want them to spawn in a randomized ziczac type of line towards the goal. I tried to fix that by doing this:
    Code (Text):
                    Random randomNum = new Random();
                    Location randomEffectLocation = new Location(world, trident.getLocation().getX() + randomNum.nextInt(8) - 4, trident.getLocation().getY(), trident.getLocation().getZ() + randomNum.nextInt(8) - 4);
    But this makes it so the particles aren't connected, so it's not a line anymore. This is basically how I want it to look:

    [​IMG]
     
  10. You could create an offset, and only modify that. So if you'd change that by 0.2-0.3 per time. It could eventually look something like

    iterations - offset
    0 - 0.2
    1 - 0.1
    2 - 0.3
    3 - 0.4
    4 - 0.3
    5 - 0.5
    6 - 0.3
    7 - 0.3
    etc.
    Then you add it like getX() + offsetX, getY() + offsetY(), getZ() + offsetZ