tutorial:projectiles
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| tutorial:projectiles [2021/06/18 12:10] – Update to 1.17 redgrapefruit | tutorial: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:// | + | // If you would like to look over the source code yourself, all of the following code was done [[https:// |
| ===== 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, | entity.damage(DamageSource.thrownProjectile(this, | ||
| - | 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) entity).addStatusEffect((new StatusEffectInstance(StatusEffects.BLINDNESS, | + | livingEntity.addStatusEffect((new StatusEffectInstance(StatusEffects.BLINDNESS, |
| - | ((LivingEntity) entity).addStatusEffect((new StatusEffectInstance(StatusEffects.SLOWNESS, | + | livingEntity.addStatusEffect((new StatusEffectInstance(StatusEffects.SLOWNESS, |
| - | ((LivingEntity) entity).addStatusEffect((new StatusEffectInstance(StatusEffects.POISON, | + | livingEntity.addStatusEffect((new StatusEffectInstance(StatusEffects.POISON, |
| - | entity.playSound(SoundEvents.AMBIENT_CAVE, | + | livingEntity.playSound(SoundEvents.AMBIENT_CAVE, |
| } | } | ||
| } | } | ||
| - | 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, | this.world.sendEntityStatus(this, | ||
| - | this.remove(); // kills the projectile | + | this.kill(); // kills the projectile |
| } | } | ||
| Line 204: | Line 207: | ||
| PackedSnowballEntity snowballEntity = new PackedSnowballEntity(world, | PackedSnowballEntity snowballEntity = new PackedSnowballEntity(world, | ||
| 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, | ||
| + | In 1.17,we will use setProperties instead of setVelocity. | ||
| + | */ | ||
| world.spawnEntity(snowballEntity); | world.spawnEntity(snowballEntity); | ||
| } | } | ||
| Line 223: | Line 230: | ||
| Finally, register your item. | Finally, register your item. | ||
| <code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
| - | 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 '' | + | Your projectile entity is now defined and registered, but we are not done. Without a renderer, the '' |
| - | \\ | + | |
| - | 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, | + | |
| - | </ | + | |
| - | \\ | + | |
| - | First on the list, we should get the '' | + | |
| <code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
| @Override | @Override | ||
| public void onInitializeClient() { | public void onInitializeClient() { | ||
| - | EntityRendererRegistry.INSTANCE.register(ProjectileTutorialMod.PackedSnowballEntityType, | + | EntityRendererRegistry.register(ProjectileTutorialMod.PackedSnowballEntityType, |
| new FlyingItemEntityRenderer(context)); | new FlyingItemEntityRenderer(context)); | ||
| + | // older versions may have to use | ||
| + | /* EntityRendererRegistry.INSTANCE.register(ProjectileTutorialMod.PackedSnowballEntityType, | ||
| + | new FlyingItemEntityRenderer(context)); | ||
| [. . .] | [. . .] | ||
| - | } | ||
| - | </ | ||
| - | |||
| - | In order for the projectileEntity to be registered, we will need a spawn packet. Create a new class named '' | ||
| - | <code java [enable_line_numbers=" | ||
| - | public class EntitySpawnPacket { | ||
| - | public static Packet<?> | ||
| - | if (e.world.isClient) | ||
| - | throw new IllegalStateException(" | ||
| - | 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, | ||
| - | PacketBufUtil.writeAngle(byteBuf, | ||
| - | PacketBufUtil.writeAngle(byteBuf, | ||
| - | return ServerSidePacketRegistry.INSTANCE.toPacket(packetID, | ||
| - | } | ||
| - | 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 | ||
| - | * | ||
| - | * @return angle | ||
| - | */ | ||
| - | public static float unpackAngle(byte angleByte) { | ||
| - | return (angleByte * 360) / 256f; | ||
| - | } | ||
| - | |||
| - | /** | ||
| - | * Writes an angle to a {@link PacketByteBuf}. | ||
| - | * | ||
| - | * @param byteBuf | ||
| - | * | ||
| - | * @param angle | ||
| - | * angle | ||
| - | */ | ||
| - | public static void writeAngle(PacketByteBuf byteBuf, float angle) { | ||
| - | byteBuf.writeByte(packAngle(angle)); | ||
| - | } | ||
| - | |||
| - | /** | ||
| - | * Reads an angle from a {@link PacketByteBuf}. | ||
| - | * | ||
| - | * @param byteBuf | ||
| - | * | ||
| - | * @return angle | ||
| - | */ | ||
| - | public static float readAngle(PacketByteBuf byteBuf) { | ||
| - | return unpackAngle(byteBuf.readByte()); | ||
| - | } | ||
| - | |||
| - | /** | ||
| - | * Writes a {@link Vec3d} to a {@link PacketByteBuf}. | ||
| - | * | ||
| - | * @param byteBuf | ||
| - | * | ||
| - | * @param vec3d | ||
| - | * | ||
| - | */ | ||
| - | 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 | ||
| - | * | ||
| - | * @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); | ||
| - | } | ||
| - | } | ||
| - | } | ||
| - | </ | ||
| - | |||
| - | This will basically read and write vectors and angles that will allow the entity' | ||
| - | \\ | ||
| - | Back to our **ClientModInitializer**, | ||
| - | |||
| - | <code java [enable_line_numbers=" | ||
| - | public void receiveEntityPacket() { | ||
| - | ClientSidePacketRegistry.INSTANCE.register(PacketID, | ||
| - | EntityType<?> | ||
| - | 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(" | ||
| - | Entity e = et.create(MinecraftClient.getInstance().world); | ||
| - | if (e == null) | ||
| - | throw new IllegalStateException(" | ||
| - | e.updateTrackedPosition(pos); | ||
| - | e.setPos(pos.x, | ||
| - | e.pitch = pitch; | ||
| - | e.yaw = yaw; | ||
| - | e.setEntityId(entityId); | ||
| - | e.setUuid(uuid); | ||
| - | MinecraftClient.getInstance().world.addEntity(entityId, | ||
| - | }); | ||
| - | }); | ||
| - | } | ||
| - | </ | ||
| - | |||
| - | Back in our '' | ||
| - | <code java [enable_line_numbers=" | ||
| - | @Override | ||
| - | public Packet createSpawnPacket() { | ||
| - | return EntitySpawnPacket.create(this, | ||
| - | } | ||
| - | </ | ||
| - | |||
| - | Finally, make sure to call this method in the **onInitializeClient()** method. | ||
| - | |||
| - | <code java [enable_line_numbers=" | ||
| - | @Override | ||
| - | public void onInitializeClient() { | ||
| - | EntityRendererRegistry.INSTANCE.register(ProjectileTutorialMod.PackedSnowballEntityType, | ||
| - | new FlyingItemEntityRenderer(dispatcher, | ||
| - | receiveEntityPacket(); | ||
| } | } | ||
| </ | </ | ||
tutorial/projectiles.1624018222.txt.gz · Last modified: 2021/06/18 12:10 by redgrapefruit