[Tutorial]Particle effects for arrows

Discussion in 'Resources' started by Funergy, Apr 2, 2014.

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

    Funergy

    UPDATED CODE:

    Big credit to Slikey for the method he provided on the second page :D
    Code:
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Map;
     
    import net.minecraft.server.v1_7_R4.Packet;
    import net.minecraft.server.v1_7_R4.PacketPlayOutWorldParticles;
     
    import org.bukkit.Bukkit;
    import org.bukkit.Location;
    import org.bukkit.craftbukkit.v1_7_R4.entity.CraftPlayer;
    import org.bukkit.entity.Arrow;
    import org.bukkit.entity.Entity;
    import org.bukkit.entity.Player;
    import org.bukkit.entity.Projectile;
    import org.bukkit.event.EventHandler;
    import org.bukkit.event.Listener;
    import org.bukkit.event.entity.EntityShootBowEvent;
    import org.bukkit.event.entity.ProjectileHitEvent;
    import org.bukkit.plugin.java.JavaPlugin;
    import org.bukkit.scheduler.BukkitRunnable;
     
    /**
    * @author Funergy
    *
    */
    public class ParticleArrows extends JavaPlugin implements Listener{
        public ArrayList<Projectile> arrows = new ArrayList<Projectile>();
     
        public void onEnable() {
    Bukkit.getPluginManager().registerEvents(this, this);//Removed the Bukkit Runnable
        }
     
        @EventHandler
        public void onShootBow(EntityShootBowEvent e){
            arrows.add((Projectile) e.getProjectile());
            addParticleEffect((Projectile)e.getProjectile());
         
     
     
        }
        @EventHandler
        public void onProjectileHit(ProjectileHitEvent e){
        if(e.getEntity() instanceof Arrow){
            arrows.remove(e.getEntity());
     
      }
        }
      //moved the Bukkit runnable to a single method and not in the onEnable
        @SuppressWarnings("deprecation")
    public void addParticleEffect(final Projectile entity){
        new BukkitRunnable(){
     
    @Override
    public void run() {
    if(arrows.contains(entity)){//if the arrow still is in the air
        Map<Player, Location> locationCache = new HashMap<>();
        for (Player online : Bukkit.getOnlinePlayers()) {
        locationCache.put(online, online.getLocation());
       
      }
      Location loc = entity.getLocation();
      Packet packet = new PacketPlayOutWorldParticles("flame",
          (float) loc.getX(),  (float) loc.getY(), (float) loc.getZ(),
          0, 0, 0,
          0, 1);
     
      for (Map.Entry<Player, Location> entry : locationCache.entrySet()) {
      if(entry.getKey().isOnline()){
     
      //If the player has left when the arrow was going. we should remove him from the map to avoid NPE's
        if (entry.getValue().getWorld() == loc.getWorld() &&
            entry.getValue().distanceSquared(loc) <= 16*16) {
          ((CraftPlayer) entry.getKey()).getHandle().playerConnection.sendPacket(packet);
     
        }
     
      }
        }
    }else{//we cancel the event when the arrow isn't in the air anymore.
    this.cancel();
    return;
    }
        }
        }.runTaskTimer(this, 0, 1);
        }
     
     
    }
    Hey,
    Today I had an Idea to make a tutorial for how to make particle effects for arrows.

    First we begin with making a arraylist.
    In this arraylist we will store the arrow information like the Location,...
    Code:
    public ArrayList<Projectile> arrow = new ArrayList<Projectile>();
    Next step we will make a event to add the arrow to the arraylist when a player fires an arrow.
    You can do this by using the EntityShootBowEvent
    Code:
    @EventHandler
        public void onfirebow(EntityShootBowEvent e){
            arrow.add((Projectile) e.getProjectile());
     
     
        }
    in the onEnable():
    Next step we will make a scheduled sync repeating task to play the particle more then one time.

    And we use a for loop to get the locations of all the arrows in the arraylist. And also we need to get all the players online to let them see the particles.

    To Play particles to a player you can simply use
    Code:
    player.playeffect(effecttype.<What type you want>, <a Integer>)
    But this time we will not use that because we don't have enough choice to choose what particle we exactly want.
    We will use packets.
    you use it like this
    Code:
    ((CraftPlayer) p).getHandle().playerConnection.sendPacket(new PacketPlayOutWorldParticles("reddust", (float) loc.getX(), (float) loc.getY(), (float) loc.getZ(), offsetX, offsetY, offsetZ, speed, amount)
    
    To choose what particle you exactly want is look at this page there you can see the strings you need to use https://forums.bukkit.org/threads/particle-packet-library.138493/

    This is the final thing you need to add to the onEnable()
    Code:
    Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new Runnable(){
     
                @Override
                public void run() {
                    for(Projectile arrows : arrow){
                        for(Player online : Bukkit.getOnlinePlayers()){
                            Location loc = arrows.getLocation();
                            ((CraftPlayer) online).getHandle().playerConnection
                                    .sendPacket(new PacketPlayOutWorldParticles(
                                            "witchMagic", (float) loc.getX(), //"witchMagic" is the particle name
                                            (float) loc.getY(), (float) loc.getZ(), 1,
                                            1, 1,(float) 0, 6));
                        }
                        }
                    }
     
            }, 0, <Here you can choose the delay for spawning a particle but I prefer 1>);

    Now we are to the next step we wanna remove the particle when the arrow hit something
    we do this with the ProjectileHitEvent
    Code:
    @EventHandler
        public void onland(ProjectileHitEvent e){
            arrow.remove(e.getEntity());
     
     
        }
    Now we're done. But don't forget to register the events in the onEnable()!
    This is the full code
    Code:
    package com.Funergy.particlearrows;
     
    import java.util.ArrayList;
     
    import net.minecraft.server.v1_7_R2.PacketPlayOutWorldParticles;
     
    import org.bukkit.Bukkit;
    import org.bukkit.Location;
     
    import org.bukkit.craftbukkit.v1_7_R2.entity.CraftPlayer;
    import org.bukkit.entity.Player;
    import org.bukkit.entity.Projectile;
     
    import org.bukkit.event.EventHandler;
    import org.bukkit.event.Listener;
     
    import org.bukkit.event.entity.EntityShootBowEvent;
    import org.bukkit.event.entity.ProjectileHitEvent;
    import org.bukkit.plugin.java.JavaPlugin;
     
     
    public class main extends JavaPlugin implements Listener {
        public ArrayList<Projectile> arrow = new ArrayList<Projectile>();
     
        public void onEnable() {
            Bukkit.getPluginManager().registerEvents(this, this);
            Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new Runnable(){
     
                @Override
                public void run() {
                    for(Projectile arrows : arrow){
                        for(Player online : Bukkit.getOnlinePlayers()){
                            Location loc = arrows.getLocation();
                            ((CraftPlayer) online).getHandle().playerConnection
                                    .sendPacket(new PacketPlayOutWorldParticles(
                                            "witchMagic", (float) loc.getX(),
                                            (float) loc.getY(), (float) loc.getZ(), 1,
                                            1, 1,(float) 0, 6));
                        }
                        }
                    }
     
            }, 0, 1);
        }
     
        @EventHandler
        public void onfirebow(EntityShootBowEvent e){
            arrow.add((Projectile) e.getProjectile());
     
     
        }
        @EventHandler
        public void onland(ProjectileHitEvent e){
            arrow.remove(e.getEntity());
     
     
        }
     
     
    }
    
    Thanks for reading my tutorial! :D

    If I forgot something PM me!
     
  2. Offline

    ItsLeoFTW

  3. Offline

    Funergy

    Thanks :D
     
  4. Offline

    ampayne2

    It's a little odd that your collection of projectiles is "arrow" and when you loop through them you name each projectile "arrows". Other than that, nice tutorial :p
     
  5. Offline

    Funergy

    haha :p
     
  6. Offline

    Niknea

    Funergy Mind showing us how this would look like? ^_^

    Also why don't you just add the particle effect to the arrow when shot and remove it when it hits a block? Instead of checking 20 times per second?
     
  7. Offline

    chasechocolate

    "Add the particle effect to the arrow"? The way he is currently doing it is less resource intensive if I understand what you are asking.
     
  8. Offline

    Funergy

    You need to do that with a scheduled sync repeating task -.-
     
  9. Offline

    mickedplay

    Good job! But where can I find the names of other effects? :rolleyes:
     
  10. Offline

    Funergy

    https://forums.bukkit.org/threads/particle-packet-library.138493/ copy the string of the effect that suits you ^.^
    Like I used in the tutorial "witchMagic" Replace that with "note" You don't need to use that effect "note" just copy a String. HUGE_EXPLOSION("hugeexplosion"),
    ,..............
     
  11. Offline

    LegitJava

    Good tutorial, but to save resources you might want to cancel the task as well when the arrow hits the ground. If you wanted to do this without referencing things in your onEnable() and just to be a bit more efficient, you can store an ID of a BukkitTask and an entity ID (arrow) in a HashMap in your Listener class.

    When the arrow hits the ground (ProjectileHitEvent) and if the entity ID for the arrow is stored in the HashMap, then it will cancel the task and remove the arrow (entity ID) from the ArrayList.

    In our EntityShootBowEvent, we can call a new BukkitRunnable and assign the variable "task" to it. Then, it will run a task timer every tick. So, as the velocity of the arrow changes, the particles will play behind the arrow! Of course, when we first shoot our bow, it stores the entity ID of the arrow and task ID into the "arrowTasks" HashMap.

    Then, of course, just register your listeners in your Main class as you would for anything else. In terms of the packet, you might want to use reflection. But, that's for another tutorial and you can find plenty resources for that on BukkitDev. :p

    Code:java
    1. private static Map<Integer, Integer> arrowTasks = new HashMap<>();
    2.  
    3. @EventHandler
    4. public void arrowHit(ProjectileHitEvent e) {
    5. if (e.getEntity() instanceof Arrow && arrowTasks.containsKey(e.getEntity().getEntityId())) {
    6. Arrow a = (Arrow) e.getEntity();
    7. Bukkit.getScheduler().cancelTask(arrowTasks.get(a.getEntityId()));
    8. arrowTasks.remove(a.getEntityId());
    9. }
    10. }
    11.  
    12. @EventHandler
    13. public void bowShoot(final EntityShootBowEvent e) {
    14. final Player player = (Player) e.getEntity();
    15.  
    16. BukkitTask task = new BukkitRunnable() {
    17. @Override
    18. public void run() {
    19. Location l = e.getProjectile().getLocation();
    20.  
    21. PacketPlayOutWorldParticles arrowParticles = new PacketPlayOutWorldParticles("flame", (float) l.getX(), (float) l.getY(), (float) l.getZ(), 0, 0, 0, 0, 1);
    22.  
    23. for(Player player : Bukkit.getOnlinePlayers()) {
    24. ((CraftPlayer) player).getHandle().playerConnection.sendPacket(arrowParticles);
    25. }
    26. }
    27. }.runTaskTimer(Main.getInstance(), 0, 1);
    28. arrowTasks.put(e.getProjectile().getEntityId(), task.getTaskId());
    29. }
     
    TigerHix likes this.
  12. Offline

    mr_snake302

    I have got trouble with ((CraftPlayer) online)
    If I import import org.bukkit.craftbukkit.v1_7_R2.entity.CraftPlayer; - crash
    And I can't import import org.bukkit.craftbukkit.v1_7_R3.entity.CraftPlayer;
     
  13. Offline

    LegitJava

    Are you using Craftbukkit? You can't import NMS with the Bukkit API, you must reference the craftbukkit.jar in your project. If you are using Craftbukkit, be sure you're using the correct version.
     
  14. Offline

    DevRosemberg

    Would be better to store UUIDS and check for the UUID of the entitiy in my opinion.
     
  15. Offline

    Bloxcraft

    Instead of .scheduleSyncRepeatingTask() which is now deprecated, use .runTaskTimer()
     
  16. Offline

    mr_snake302

    I use lastest 1.7.9 build
     
  17. Offline

    LegitJava


    Huh, I'm not really sure then as there's not too much background information on what you're using to get project JARs (Maven, etc.). If using Maven, try deleting the 1.7.9 build from your .m2 folder and re-importing. If not, I'm not sure. Just wait for a new build to be released but I don't see what would be causing this.
     
  18. Offline

    MrDplugins

    LegitJava WARNING NOOB QUESTION: How do I add permissions to this?
     
  19. Offline

    LegitJava


    Code:java
    1. @EventHandler
    2. public void bowShoot(final EntityShootBowEvent e) {
    3. final Player player = (Player) e.getEntity();
    4. if (!player.hasPermission("your.permission")) return;
    5.  
    6. BukkitTask task = new BukkitRunnable() {
    7. @Override
    8. public void run() {
    9. Location l = e.getProjectile().getLocation();
    10.  
    11. PacketPlayOutWorldParticles arrowParticles = new PacketPlayOutWorldParticles("flame", (float) l.getX(), (float) l.getY(), (float) l.getZ(), 0, 0, 0, 0, 1);
    12.  
    13. for(Player player : Bukkit.getOnlinePlayers()) {
    14. ((CraftPlayer) player).getHandle().playerConnection.sendPacket(arrowParticles);
    15. }
    16. }
    17. }.runTaskTimer(Main.getInstance(), 0, 1);
    18. arrowTasks.put(e.getProjectile().getEntityId(), task.getTaskId());
    19. }
     
  20. Offline

    MrDplugins

    LegitJava This is different from your previous code did you re-write the code line?

    EDIT: Nope I'm just stupid xD I thought you were using the tut code now your own Thanks anyways! Also .runTaskTimer(Main.getInstance(), 0, 1); Main cannot be resolved? what is it?

    EDIT2: I'm just stupid i'll go home now....
     
  21. Offline

    MinecraftMart


    Im getting 2 errors here
    1: PacketPlayOutWorldParticles cannot be resolved to a type
    2: CraftPlayer cannot be resolved to a type

    How to fix these?
     
  22. Offline

    viper_monster

  23. Offline

    MinecraftMart

    viper_monster

    K i have imported the project and configured it into maven, what now?

    viper_monster

    Im still getting problem 2 altough i did import the craftbukkit jar

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

    mine-care

    Funergy Realy usefull! Thanks!
     
  25. Offline

    Funergy

    Yeah for some reasons if you use packets in your code, put the craftbukkit above bukkit in the order/export.

    And also the import can change in certain Bukkit update like
    1.7.9 bukkit:
    import net.minecraft.server.v1_7_R3.PacketPlayOutWorldParticles;
    1.7.2 bukkit:
    import net.minecraft.server.v1_7_R2.PacketPlayOutWorldParticles;

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

    MinecraftMart

    Funergy
    Yeah i forgot to say it got it working xD but awesome tutorial man. Really helped me
     
  27. Offline

    Phasesaber

    (Not sure if you solved your problem...)
    It is the Plugin instance. You can set it up like this:
    Code:java
    1.  
    2. public class Main{
    3. static Main instance;
    4.  
    5. public void onEnable(){
    6. instance = this;
    7. }
    8.  
    9. //You could just return Plugin here.
    10. public static Main getInstance(){
    11. return instance;
    12. }
    13. }
    14.  


    //Coded on Mobile.
     
  28. Offline

    Peter25715

    Great going, But please when you put a tutorial like this use this :

    Code:java
    1. Use JAVA not general code :)


    Funergy,
     
  29. Offline

    Slikey

    Hey,

    you have a little issue there. If you run a server with multiple worlds and / or large worlds, you might lag your server with all the packets, you create. Also you added a gaussian of 1 to every axis. this adds some sort of randomness. Try this :)

    Code:java
    1. Map<Player, Location> locationCache = new HashMap<>();
    2. for (Player online : Bukkit.getOnlinePlayers()) {
    3. locationCache.put(online, online.getLocation());
    4. }
    5. for (Projectile arrows : arrow) {
    6. Location loc = arrows.getLocation();
    7. Packet packet = new PacketPlayOutWorldParticles("witchMagic",
    8. (float) loc.getX(), (float) loc.getY(), (float) loc.getZ(),
    9. 0, 0, 0,
    10. 0, 1);
    11. for (Map.Entry<Player, Location> entry : locationCache.entrySet()) {
    12. if (entry.getValue().getWorld() == loc.getWorld() &&
    13. entry.getValue().distanceSquared(loc) <= 16*16) {
    14. ((CraftPlayer) entry.getKey()).getHandle().playerConnection.sendPacket(packet);
    15. }
    16. }
    17. }
     
  30. Offline

    MineStein

Thread Status:
Not open for further replies.

Share This Page