Flandre923
970 words
5 minutes
09 吐口水的羊驼

09 吐口水的羊驼#

对于吐口水的羊驼,我们需要一个物品,右键生成一个吐口水的羊驼的实体.

代码的主要功能就是生成一个口水的羊驼的实体.


public class LlamaItem extends Item {
    public LlamaItem(Properties properties) {
        super(properties);
    }

    @Override
    public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand usedHand) {
        level.playSound(null,player.getX(),player.getY(),player.getZ(), SoundEvents.LLAMA_SPIT, SoundSource.NEUTRAL,0.5f,0.4f/(level.getRandom().nextFloat() * 0.4f + 0.8f));
        if (!level.isClientSide){
            CustomLlamaSpitEntity llamaSpitEntity = new CustomLlamaSpitEntity(level,player);
            llamaSpitEntity.shootFromRotation(player,player.getXRot(),player.getYRot(),0f,1.5f,1.0f);
            level.addFreshEntity(llamaSpitEntity);
        }
        return super.use(level, player, usedHand);
    }
}

下面我添加一个自己的口水的类,直接继承于原版的类,并修改了一些细节.

// 自定义羊驼唾液实体类,继承自Projectile
public class CustomLlamaSpitEntity extends Projectile {

    // 构造函数,初始化实体类型和所在世界
    protected CustomLlamaSpitEntity(EntityType<? extends Projectile> entityType, Level level) {
        super(entityType, level);
    }

    // 构造函数,初始化世界和所有者
    public CustomLlamaSpitEntity(Level world, Player owner) {
        this(EntityType.LLAMA_SPIT, world);  // 这里使用了原版的type所以,所以渲染和模型都是对应的原版的口水的渲染和模型
        this.setOwner(owner);
        this.setPos(owner.getX() - (double)(owner.getBbWidth() + 1.0F) * 0.5 * (double)Math.sin(owner.yBodyRot * 0.017453292F), owner.getEyeY() - 0.10000000149011612, owner.getZ() + (double)(owner.getBbWidth() + 1.0F) * 0.5 * (double)Math.cos(owner.yBodyRot * 0.017453292F));
    }

    // 每 tick 更新逻辑
    @Override
    public void tick() {
        super.tick();
        Vec3 vec3d = this.getDeltaMovement();
        HitResult hitResult = ProjectileUtil.getHitResultOnMoveVector(this,this::canHitEntity);
        this.onHit(hitResult);
        double d = this.getX() + vec3d.x;
        double e = this.getY() + vec3d.y;
        double f = this.getZ() + vec3d.z;
        this.updateRotation();
        float g = 0.99F;
        float h = 0.06F;
//检查是否碰撞到非空气方块或进入水中
        if (this.level().getBlockStates(this.getBoundingBox()).noneMatch(BlockBehaviour.BlockStateBase::isAir)) {
            this.discard();
        } else if (this.isInWaterOrBubble()) {
            this.discard();
        } else {
//新运动向量和位置
            this.setDeltaMovement(vec3d.scale(0.9900000095367432));
            if (!this.isNoGravity()) {
                this.setDeltaMovement(this.getDeltaMovement().add(0.0, -0.05999999865889549, 0.0));
            }

            this.setPos(d, e, f);
        }
    }

    // 击中实体时的处理逻辑
    @Override
    protected void onHitEntity(EntityHitResult result) {
        super.onHitEntity(result);
        Entity owner = this.getOwner();
        if (owner instanceof LivingEntity livingEntity){
            result.getEntity().hurt(this.damageSources().mobProjectile(this,livingEntity),1f);
        }
    }

    // 击中方块时的处理逻辑
    @Override
    protected void onHitBlock(BlockHitResult result) {
        super.onHitBlock(result);
        if (!this.level().isClientSide){
            this.discard();
        }
    }

    // 定义同步数据
    @Override
    protected void defineSynchedData(SynchedEntityData.Builder builder) {

    }

    // 从数据包中重新创建实体
    @Override
    public void recreateFromPacket(ClientboundAddEntityPacket packet) {
        super.recreateFromPacket(packet);

        double d = packet.getX();
        double e = packet.getY();
        double f = packet.getZ();

        for(int i =0;i<7;i++){
            double g = 0.4 + 0.1 * (double)i;
            this.level().addParticle(ParticleTypes.SPIT,this.getX(),this.getY(),this.getZ(),d*g,e,f*g);
        }

        this.setPos(d,e,f);
    }
}

其他代码请查看之前的自行完成.

我们看下对应的如何实现将实体的渲染作为物品的渲染.使用mixin对renderitem进行了修改,如果renderitem的目标item是我们添加的羊驼的物品,.我们就取消这个渲染,然后写上自己的渲染代码


/**
 * 这个类继承自ItemRenderer,并使用Mixin注解标记,表示它将修改ItemRenderer的行为。
 */
@Mixin(ItemRenderer.class)
public class ItemRendererMixin {

    /**
     * 使用Unique注解标记的成员变量,用于获取Minecraft实例,以便在渲染过程中使用。
     */
    @Unique
    private final Minecraft mc = Minecraft.getInstance();

    /**
     * 使用Inject注解标记的方法,用于在ItemRenderer的render方法执行前注入自定义代码。
     * 这个方法会检查要渲染的物品是否是特定的物品(例如MOD中新增的羊驼项),如果是,则使用自定义的渲染逻辑。
     * 
     * @param itemStack 要渲染的物品栈
     * @param displayContext 显示上下文,定义了物品的展示方式
     * @param leftHand 是否在左手中
     * @param poseStack 用于追踪和修改渲染状态的矩阵堆栈
     * @param bufferSource 提供渲染缓冲区的源
     * @param combinedLight 组合光照值
     * @param combinedOverlay 组合覆盖值
     * @param p_model 要渲染的模型
     * @param ci 回调信息,可以通过设置cancel()来取消原本的渲染逻辑
     */
    @Inject(method = "render", at = @At("HEAD"), cancellable = true)
    public void renderItem(ItemStack itemStack, ItemDisplayContext displayContext, boolean leftHand, PoseStack poseStack, MultiBufferSource bufferSource, int combinedLight, int combinedOverlay, BakedModel p_model, CallbackInfo ci) {
        // 检查是否是MOD中新增的羊驼项
        if (BuiltInRegistries.ITEM.getKey(itemStack.getItem()).equals(ResourceLocation.fromNamespaceAndPath(NeoMafishMod.MODID, "llama_item"))) {
            // 取消默认渲染
            ci.cancel();

            // 创建一个新的羊驼实体实例
            Llama llama = new Llama(EntityType.LLAMA, mc.level);
            poseStack.pushPose();
            // 使用org.joml.Quaternionf进行旋转
            Quaternionf rotation = new Quaternionf().rotateY((float) Math.toRadians(180));
            poseStack.mulPose(rotation);

            // 调整渲染的羊驼模型的缩放比例
            poseStack.scale(0.5F, 0.5F, 0.5F);
            // 调用Minecraft的渲染系统来渲染羊驼模型
            mc.getEntityRenderDispatcher().render(llama, 0, 0, 0, 0.0F, 1.0F, poseStack, bufferSource, combinedLight);
            poseStack.popPose();
        }
    }
}

记得添加对应的类到mixin的json

09 吐口水的羊驼
https://fuwari.vercel.app/posts/minecraft1_21_0/09_吐口水的羊驼/
Author
Flandre923
Published at
2024-08-24