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
tutorial:projectiles [2020/11/16 22:17] – tutorial-download spxctreofficialtutorial:projectiles [2024/03/08 01:56] (current) – [Creating & Registering a Projectile Entity] Update names, add @Override, and change some parameters to match 1.20.4 yarn netuserget
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.
  }  }
 +
  
  @Environment(EnvType.CLIENT)  @Environment(EnvType.CLIENT)
Line 102: Line 103:
  }  }
  
 + @Override
  protected void onEntityHit(EntityHitResult entityHitResult) { // called on entity hit.  protected void onEntityHit(EntityHitResult entityHitResult) { // called on entity hit.
  super.onEntityHit(entityHitResult);  super.onEntityHit(entityHitResult);
Line 108: Line 110:
  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
  }  }
  }  }
  
- protected void onCollision(HitResult hitResult) { // called on collision with a block + @Override 
- super.onCollision(hitResult);+ protected void onBlockCollision(BlockState state) { // called on collision with a block 
 + super.onBlockCollision(state);
  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 204: Line 207:
  PackedSnowballEntity snowballEntity = new PackedSnowballEntity(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 223: Line 230:
 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 245: Line 252:
 ===== 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>
tutorial/projectiles.1605565078.txt.gz · Last modified: 2020/11/16 22:17 by spxctreofficial