tutorial:projectiles
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision | ||
tutorial:projectiles [2021/06/18 14:08] – Update to 1.17 part 2 redgrapefruit | tutorial:projectiles [2022/09/29 14:37] – Remove spawn packet code, since createSpawnPacket is already overriden by ProjectileEntity. patrickmsm | ||
---|---|---|---|
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. | ||
} | } | ||
+ | @Override | ||
@Environment(EnvType.CLIENT) | @Environment(EnvType.CLIENT) | ||
Line 108: | Line 109: | ||
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, |
} | } | ||
} | } | ||
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, | this.world.sendEntityStatus(this, | ||
- | this.remove(); // kills the projectile | + | this.kill(); // kills the projectile |
} | } | ||
Line 204: | Line 205: | ||
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 245: | 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 '' | + | 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(context)); | ||
- | receiveEntityPacket(); | ||
} | } | ||
</ | </ |
tutorial/projectiles.txt · Last modified: 2024/03/08 01:56 by netuserget