tutorial:projectiles
Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
tutorial:projectiles [2020/11/16 19:47] – created spxctreofficial | 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 202: | Line 205: | ||
*/ | */ | ||
if (!world.isClient) { | if (!world.isClient) { | ||
- | SnowballEntity | + | PackedSnowballEntity |
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 217: | Line 224: | ||
} | } | ||
</ | </ | ||
+ | Make sure that the projectile that you are launching with this item is indeed your custom '' | ||
+ | \\ | ||
Now, we are finished with creating an item for the '' | Now, we are finished with creating an item for the '' | ||
\\ | \\ | ||
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 244: | 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 '' |
- | \\ | + | |
- | 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(dispatcher, | + | new FlyingItemEntityRenderer(context)); |
+ | // older versions may have to use | ||
+ | /* EntityRendererRegistry.INSTANCE.register(ProjectileTutorialMod.PackedSnowballEntityType, | ||
+ | new FlyingItemEntityRenderer(context)); */ | ||
[. . .] | [. . .] | ||
} | } | ||
</ | </ | ||
- | In order for the projectileEntity | + | ===== Hoping |
- | <code java [enable_line_numbers=" | + | |
- | public class EntitySpawnPacket { | + | |
- | public static Packet<?> | + | |
- | if (e.world.isClient) | + | |
- | throw new IllegalStateException(" | + | |
- | PacketByteBuf byteBuf | + | |
- | byteBuf.writeVarInt(Registry.ENTITY_TYPE.getRawId(e.getType())); | + | |
- | byteBuf.writeUuid(e.getUuid()); | + | |
- | byteBuf.writeVarInt(e.getEntityId()); | + | |
- | PacketBufUtil.writeVec3d(byteBuf, e.getPos()); | + | 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. |
- | PacketBufUtil.writeAngle(byteBuf, e.pitch); | + | |
- | PacketBufUtil.writeAngle(byteBuf, | + | |
- | return ServerSidePacketRegistry.INSTANCE.toPacket(packetID, | + | |
- | } | + | |
- | public static final class PacketBufUtil { | + | |
- | /** | + | If you would like to try out this projectile, download [[https://github.com/spxctreofficial/ |
- | * Packs a floating-point angle into a {@code byte}. | + | |
- | * | + | |
- | * @param angle | + | |
- | * | + | |
- | * @return packed angle | + | |
- | */ | + | |
- | public static byte packAngle(float angle) { | + | |
- | return (byte) MathHelper.floor(angle * 256 / 360); | + | |
- | } | + | |
- | /** | + | [INSERT USABLE PICTURE HERE] |
- | * 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 | + | |
- | * | + | |
- | */ | + | |
- | 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(new Identifier(ProjectileTutorialMod.ModID, | + | |
- | 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, | + | |
- | }); | + | |
- | }); | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | 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(); | + | |
- | } | + | |
- | </ | + | |
- | + | ||
- | ===== Hoping to God It Works ===== | + | |
- | + | ||
- | 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. | + |
tutorial/projectiles.txt · Last modified: 2024/03/08 01:56 by netuserget