User Tools

Site Tools


tutorial:projectiles

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Last revisionBoth sides next revision
tutorial:projectiles [2020/11/16 21:55] – fixing code spxctreofficialtutorial:projectiles [2022/12/21 01:38] – itemgroup for 1.19.3 haykam
Line 10: Line 10:
 We will be creating a custom snowball-like projectile that applies some very nasty effects to the entity that has been hit. We will be creating a custom snowball-like projectile that applies some very nasty effects to the entity that has been hit.
  
-// If you would like to look over the source code yourself, all of the following code was done [[https://github.com/spxctreofficial/fabric-projectile-tutorial|here]]. Before the tutorial begins, I would like to let you know that I would be using PascalCase to name the methods. Feel free to change the naming scheme to however you like, but I personally swear by PascalCase. //+// If you would like to look over the source code yourself, all of the following code was done [[https://github.com/spxctreofficial/fabric-projectile-tutorial|here]]. Before the tutorial begins, I would like to let you know that I would be using PascalCase to name the methods. Feel free to change the naming scheme to however you like, but I personally swear by PascalCase. Note that this may be outdated //
  
 ===== Creating & Registering a Projectile Entity ===== ===== Creating & Registering a Projectile Entity =====
Line 83: Line 83:
  return null; // We will configure this later, once we have created the ProjectileItem.  return null; // We will configure this later, once we have created the ProjectileItem.
  }  }
 + @Override
  
  @Environment(EnvType.CLIENT)  @Environment(EnvType.CLIENT)
Line 108: Line 109:
  entity.damage(DamageSource.thrownProjectile(this, this.getOwner()), (float)i); // deals damage  entity.damage(DamageSource.thrownProjectile(this, this.getOwner()), (float)i); // deals damage
  
- if (entity instanceof LivingEntity) { // checks if entity is an instance of LivingEntity (meaning it is not a boat or minecart) + if (entity instanceof LivingEntity livingEntity) { // checks if entity is an instance of LivingEntity (meaning it is not a boat or minecart) 
- ((LivingEntity) entity).addStatusEffect((new StatusEffectInstance(StatusEffects.BLINDNESS, 20 * 3, 0))); // applies a status effect + livingEntity.addStatusEffect((new StatusEffectInstance(StatusEffects.BLINDNESS, 20 * 3, 0))); // applies a status effect 
- ((LivingEntity) entity).addStatusEffect((new StatusEffectInstance(StatusEffects.SLOWNESS, 20 * 3, 2))); // applies a status effect + livingEntity.addStatusEffect((new StatusEffectInstance(StatusEffects.SLOWNESS, 20 * 3, 2))); // applies a status effect 
- ((LivingEntity) entity).addStatusEffect((new StatusEffectInstance(StatusEffects.POISON, 20 * 3, 1))); // applies a status effect + livingEntity.addStatusEffect((new StatusEffectInstance(StatusEffects.POISON, 20 * 3, 1))); // applies a status effect 
- entity.playSound(SoundEvents.AMBIENT_CAVE, 2F, 1F); // plays a sound for the entity hit only+ livingEntity.playSound(SoundEvents.AMBIENT_CAVE, 2F, 1F); // plays a sound for the entity hit only
  }  }
  }  }
Line 120: Line 121:
  if (!this.world.isClient) { // checks if the world is client  if (!this.world.isClient) { // checks if the world is client
  this.world.sendEntityStatus(this, (byte)3); // particle?  this.world.sendEntityStatus(this, (byte)3); // particle?
- this.remove(); // kills the projectile+ this.kill(); // kills the projectile
  }  }
  
Line 202: Line 203:
  */  */
  if (!world.isClient) {  if (!world.isClient) {
- SnowballEntity snowballEntity = new SnowballEntity(world, user);+ PackedSnowballEntity snowballEntity = new PackedSnowballEntity(world, user);
  snowballEntity.setItem(itemStack);  snowballEntity.setItem(itemStack);
- snowballEntity.setProperties(user, user.pitch, user.yaw, 0.0F, 1.5F, 0F);+ snowballEntity.setVelocity(user, user.pitch, user.yaw, 0.0F, 1.5F, 0F); 
 +                        /* 
 +                        snowballEntity.setProperties(user, user.getPitch(), user.getYaw(), 0.0F, 1.5F, 1.0F); 
 +                        In 1.17,we will use setProperties instead of setVelocity.                                                         
 +                        */
  world.spawnEntity(snowballEntity); // spawns entity  world.spawnEntity(snowballEntity); // spawns entity
  }  }
Line 217: Line 222:
 } }
 </code> </code>
 +Make sure that the projectile that you are launching with this item is indeed your custom ''ProjectileEntity''. Verify this by checking ''PackedSnowballEntity snowballEntity = new PackedSnowballEntity(world, user);''
 +\\
 Now, we are finished with creating an item for the ''ProjectileEntity''. Keep in mind that if you do not understand how to create an item, refer to the [[https://fabricmc.net/wiki/tutorial:items|"Item" tutorial]]. Now, we are finished with creating an item for the ''ProjectileEntity''. Keep in mind that if you do not understand how to create an item, refer to the [[https://fabricmc.net/wiki/tutorial:items|"Item" tutorial]].
 \\ \\
 Finally, register your item. Finally, register your item.
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
-public static final Item PackedSnowballItem = new PackedSnowballItem(new Item.Settings().group(ItemGroup.MISC).maxCount(16));+public static final Item PackedSnowballItem = new PackedSnowballItem(new Item.Settings().maxCount(16));
  
 [...] [...]
Line 244: Line 250:
 ===== Rendering your Projectile Entity ===== ===== Rendering your Projectile Entity =====
  
-Your projectile entity is now defined and registered, but we are not done. Without a renderer, the ''ProjectileEntity'' will crash Minecraft. To fix this, we will define and register the ''EntityRenderer'' for our ''ProjectileEntity''. To do this, we will need a ''EntityRenderer'' in the **ClientModInitializer** and a spawn packet to make sure the texture is rendered correctly. +Your projectile entity is now defined and registered, but we are not done. Without a renderer, the ''ProjectileEntity'' will crash Minecraft. To fix this, we will define and register the ''EntityRenderer'' for our ''ProjectileEntity''. To do this, we will need a ''EntityRenderer'' in the **ClientModInitializer**. Go into your **ClientModInitializer** and write the following:
-\\ +
-Before we start, we will quickly define an Identifier that we will be using a lot: our PacketID. +
-<code java> +
-public static final Identifier PacketID = new Identifier(ProjectileTutorialMod.ModID, "spawn_packet"); +
-</code> +
-\\ +
-First on the list, we should get the ''EntityRenderer'' out of the way. Go into your **ClientModInitializer** and write the following:+
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
  @Override  @Override
  public void onInitializeClient() {  public void onInitializeClient() {
- EntityRendererRegistry.INSTANCE.register(ProjectileTutorialMod.PackedSnowballEntityType, (dispatcher, context) -> + EntityRendererRegistry.register(ProjectileTutorialMod.PackedSnowballEntityType, (context) -> 
- new FlyingItemEntityRenderer(dispatcher, context.getItemRenderer()));+  new FlyingItemEntityRenderer(context)); 
 + // older versions may have to use 
 + /* EntityRendererRegistry.INSTANCE.register(ProjectileTutorialMod.PackedSnowballEntityType, (context-> 
 + new FlyingItemEntityRenderer(context)); */
  [. . .]  [. . .]
- } 
-</code> 
- 
-In order for the projectileEntity to be registered, we will need a spawn packet. Create a new class named ''EntitySpawnPacket'', and put this in that class. 
-<code java [enable_line_numbers="true"]> 
-public class EntitySpawnPacket { 
- public static Packet<?> create(Entity e, Identifier packetID) { 
- if (e.world.isClient) 
- throw new IllegalStateException("SpawnPacketUtil.create called on the logical client!"); 
- PacketByteBuf byteBuf = new PacketByteBuf(Unpooled.buffer()); 
- byteBuf.writeVarInt(Registry.ENTITY_TYPE.getRawId(e.getType())); 
- byteBuf.writeUuid(e.getUuid()); 
- byteBuf.writeVarInt(e.getEntityId()); 
- 
- PacketBufUtil.writeVec3d(byteBuf, e.getPos()); 
- PacketBufUtil.writeAngle(byteBuf, e.pitch); 
- PacketBufUtil.writeAngle(byteBuf, e.yaw); 
- return ServerSidePacketRegistry.INSTANCE.toPacket(packetID, byteBuf); 
- } 
- public static final class PacketBufUtil { 
- 
- /** 
- * Packs a floating-point angle into a {@code byte}. 
- * 
- * @param angle 
- *         angle 
- * @return packed angle 
- */ 
- public static byte packAngle(float angle) { 
- return (byte) MathHelper.floor(angle * 256 / 360); 
- } 
- 
- /** 
- * Unpacks a floating-point angle from a {@code byte}. 
- * 
- * @param angleByte 
- *         packed angle 
- * @return angle 
- */ 
- public static float unpackAngle(byte angleByte) { 
- return (angleByte * 360) / 256f; 
- } 
- 
- /** 
- * Writes an angle to a {@link PacketByteBuf}. 
- * 
- * @param byteBuf 
- *         destination buffer 
- * @param angle 
- *         angle 
- */ 
- public static void writeAngle(PacketByteBuf byteBuf, float angle) { 
- byteBuf.writeByte(packAngle(angle)); 
- } 
- 
- /** 
- * Reads an angle from a {@link PacketByteBuf}. 
- * 
- * @param byteBuf 
- *         source buffer 
- * @return angle 
- */ 
- public static float readAngle(PacketByteBuf byteBuf) { 
- return unpackAngle(byteBuf.readByte()); 
- } 
- 
- /** 
- * Writes a {@link Vec3d} to a {@link PacketByteBuf}. 
- * 
- * @param byteBuf 
- *         destination buffer 
- * @param vec3d 
- *         vector 
- */ 
- public static void writeVec3d(PacketByteBuf byteBuf, Vec3d vec3d) { 
- byteBuf.writeDouble(vec3d.x); 
- byteBuf.writeDouble(vec3d.y); 
- byteBuf.writeDouble(vec3d.z); 
- } 
- 
- /** 
- * Reads a {@link Vec3d} from a {@link PacketByteBuf}. 
- * 
- * @param byteBuf 
- *         source buffer 
- * @return vector 
- */ 
- public static Vec3d readVec3d(PacketByteBuf byteBuf) { 
- double x = byteBuf.readDouble(); 
- double y = byteBuf.readDouble(); 
- double z = byteBuf.readDouble(); 
- return new Vec3d(x, y, z); 
- } 
- } 
-} 
-</code> 
- 
-This will basically read and write vectors and angles that will allow the entity's texture to be rendered correctly. I will not go in-depth about spawn packets here, but you could read up on what they do and how they function. For now, we can include this and move on. 
-\\ 
-Back to our **ClientModInitializer**, we will create a new method and put the following in that method. 
- 
-<code java [enable_line_numbers="true"]> 
- public void receiveEntityPacket() { 
- ClientSidePacketRegistry.INSTANCE.register(PacketID, (ctx, byteBuf) -> { 
- EntityType<?> et = Registry.ENTITY_TYPE.get(byteBuf.readVarInt()); 
- UUID uuid = byteBuf.readUuid(); 
- int entityId = byteBuf.readVarInt(); 
- Vec3d pos = EntitySpawnPacket.PacketBufUtil.readVec3d(byteBuf); 
- float pitch = EntitySpawnPacket.PacketBufUtil.readAngle(byteBuf); 
- float yaw = EntitySpawnPacket.PacketBufUtil.readAngle(byteBuf); 
- ctx.getTaskQueue().execute(() -> { 
- if (MinecraftClient.getInstance().world == null) 
- throw new IllegalStateException("Tried to spawn entity in a null world!"); 
- Entity e = et.create(MinecraftClient.getInstance().world); 
- if (e == null) 
- throw new IllegalStateException("Failed to create instance of entity \"" + Registry.ENTITY_TYPE.getId(et) + "\"!"); 
- e.updateTrackedPosition(pos); 
- e.setPos(pos.x, pos.y, pos.z); 
- e.pitch = pitch; 
- e.yaw = yaw; 
- e.setEntityId(entityId); 
- e.setUuid(uuid); 
- MinecraftClient.getInstance().world.addEntity(entityId, e); 
- }); 
- }); 
- } 
-</code> 
- 
-Back in our ''ProjectileEntity'' class, we must add a method to make sure everything works correctly. 
-<code java [enable_line_numbers="true"]> 
-        @Override 
- public Packet createSpawnPacket() { 
- return EntitySpawnPacket.create(this, ProjectileTutorialClient.PacketID); 
- } 
-</code> 
- 
-Finally, make sure to call this method in the **onInitializeClient()** method. 
- 
-<code java [enable_line_numbers="true"]> 
-@Override 
- public void onInitializeClient() { 
- EntityRendererRegistry.INSTANCE.register(ProjectileTutorialMod.PackedSnowballEntityType, (dispatcher, context) -> 
- new FlyingItemEntityRenderer(dispatcher, context.getItemRenderer())); 
- receiveEntityPacket(); 
  }  }
 </code> </code>
Line 408: Line 266:
  
 Now, your projectile should be working in-game! Just make sure your textures are in the right place, and your item and projectile should be working. Now, your projectile should be working in-game! Just make sure your textures are in the right place, and your item and projectile should be working.
 +
 +If you would like to try out this projectile, download [[https://github.com/spxctreofficial/fabric-projectile-tutorial/releases/download/v1.0.0/projectile-tutorial-1.0.0.jar|here]].
  
 [INSERT USABLE PICTURE HERE] [INSERT USABLE PICTURE HERE]
tutorial/projectiles.txt · Last modified: 2024/03/08 01:56 by netuserget