Flandre923
1419 words
7 minutes
26 物品特殊渲染

参考#

https://boson.v2mcdev.com/specialrender/ister.html

物品特殊渲染#

你在模组中经常会看到一些具有动画的物品,我们这次就介绍下如何实现一个简单的具有动画效果的物品。使用的是BlockEntityWithoutLevelRenderer这样的一个类。

首先还是添加一个物品

public class WrenchItem extends Item {
    public WrenchItem() {
        super(new Properties());
    }

    @Override
    public void initializeClient(Consumer<IClientItemExtensions> consumer) {
        consumer.accept(new IClientItemExtensions() {
            @Override
            public BlockEntityWithoutLevelRenderer getCustomRenderer() {
                return new WrenchItemRenderer();
            }
        });
    }
}

这个代码中你可能会有一部分不知道怎么回事,我们之后来讲,首先我们先来看怎么构造这样的一个类WrenchItemRenderer


public class WrenchItemRenderer extends BlockEntityWithoutLevelRenderer {
    private static int degree = 0;
    public WrenchItemRenderer(){
        super(null,null);
    }

    @Override
    public void renderByItem(ItemStack pStack, ItemDisplayContext pDisplayContext, PoseStack pPoseStack, MultiBufferSource pBuffer, int pPackedLight, int pPackedOverlay) {
        if(degree==360){
            degree=0;
        }
        degree++;

        ItemRenderer itemRenderer = Minecraft.getInstance().getItemRenderer();
        BakedModel bakedModel = itemRenderer.getModel(pStack,null,null,1);
        pPoseStack.pushPose();
        pPoseStack.translate(0.5F, 0.5F, 0.5F);
        float xOffset = -1 / 32f;
        float zOffset = 0;
        pPoseStack.translate(-xOffset, 0, -zOffset);
        pPoseStack.mulPose(Axis.YP.rotationDegrees(degree));
        pPoseStack.translate(xOffset, 0, zOffset);
        itemRenderer.render(pStack, ItemDisplayContext.NONE, false, pPoseStack, pBuffer, pPackedLight, pPackedOverlay, bakedModel);
        pPoseStack.popPose();
    }
}

可以看到这样的一个render是BlockEntityWithoutLevelRenderer类是实现的,那么我么先看看BlockEntityWithoutLevelRenderer类,再来看我们写的这个类。

BlockEntityWithoutLevelRenderer用于实现在Minecraft游戏中渲染不需要block的实体渲染器。BlockEntityWithoutLevelRenderer类实现了ResourceManagerReloadListener接口,用于在资源管理器重新加载时,重新加载模型和材质等资源。

在类中声明了几个静态变量,例如SHULKER_BOXES数组,用于存储不同颜色的箱子实体,DEFAULT_SHULKER_BOX变量,用于存储默认的箱子实体,以及几个实体对象,例如chest、trappedChest、enderChest等,用于渲染不同类型的实体。 renderByItem()方法是在渲染物品时调用的方法,用于渲染方块实体。在该方法中,首先获取物品栈的物品对象,根据该物品的类型,选择不同的渲染方式。例如,如果物品是盾牌,则调用盾牌模型渲染方法;如果物品是三叉戟,则调用三叉戟模型渲染方法;如果物品是一个方块,则根据方块的类型和状态,选择相应的实体对象进行渲染。

实际上我们的动画的物品是同样是通过方块实体渲染来实现的,我们要重写方法就是这个renderByItem()方法。

好了,我们来看我们的这个render吧。

    private static int degree = 0;

这里声明了一个字段,这个字段是用于计算物品旋转的情况的。

我们重写了renderByItem方法,以下是内容:

  if(degree==360){
            degree=0;
        }
        degree++;

        ItemRenderer itemRenderer = Minecraft.getInstance().getItemRenderer();
        BakedModel bakedModel = itemRenderer.getModel(pStack,null,null,0);
        pPoseStack.pushPose();
        pPoseStack.translate(0.5F, 0.5F, 0.5F);
        float xOffset = -1 / 32f;
        float zOffset = 0;
        pPoseStack.translate(-xOffset, 0, -zOffset);
        pPoseStack.mulPose(Axis.YP.rotationDegrees(degree));
        pPoseStack.translate(xOffset, 0, zOffset);
        itemRenderer.render(pStack, ItemDisplayContext.NONE, false, pPoseStack, pBuffer, pPackedLight, pPackedOverlay, bakedModel);
        pPoseStack.popPose();

我们做了这些操作。首先判断了角度,如果是360就重置,否则就自增。然后我们获得了ItemRenderer,通过ItemRenderer获得了物品的烘焙模型BakedModel,之后我们调整移动这个模型这个模型,并让他沿着Y轴缓慢旋转

好了我们定义了render但是我们的render现在并没有工作,我们还需要在烘焙模型中开启使用BlockEntityWithoutLevelRenderer,并且和物品进行绑定.

我们先定义我们的烘焙模型,并替换原本的烘焙模型,这些操作和上一个教程类似。


public class WrenchBakeModel implements BakedModel {
    private BakedModel existingModel;

    public WrenchBakeModel(BakedModel existingModel) {
        this.existingModel = existingModel;
    }

    @Override
    public List<BakedQuad> getQuads(@Nullable BlockState pState, @Nullable Direction pDirection, RandomSource pRandom) {
        return this.existingModel.getQuads(pState, pDirection, pRandom);
    }

    @Override
    public @NotNull List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, @NotNull RandomSource rand, @NotNull ModelData data, @Nullable RenderType renderType) {
        throw new AssertionError("IForgeBakedModel::getQuads should never be called, only IForgeBakedModel::getQuads");
    }

    @Override
    public boolean useAmbientOcclusion() {
        return this.existingModel.useAmbientOcclusion();
    }

    @Override
    public boolean isGui3d() {
        return this.existingModel.isGui3d();
    }

    @Override
    public boolean usesBlockLight() {
        return this.existingModel.usesBlockLight();
    }

    @Override
    public boolean isCustomRenderer() {
        return true;
    }



    @Override
    public TextureAtlasSprite getParticleIcon() {
        return this.existingModel.getParticleIcon();
    }

    @Override
    public ItemOverrides getOverrides() {
        return this.existingModel.getOverrides();
    }

    @Override
    public BakedModel applyTransform(ItemDisplayContext transformType, PoseStack poseStack, boolean applyLeftHandTransform) {
        if (transformType == ItemDisplayContext.FIRST_PERSON_RIGHT_HAND || transformType == ItemDisplayContext.FIRST_PERSON_LEFT_HAND){
            return this;
        }
        return this.existingModel.applyTransform(transformType,poseStack,applyLeftHandTransform);
    }
}

第一个需要说的是这里,对于物品获得对于的顶点数据,是getQuads(@Nullable BlockState pState, @Nullable Direction pDirection, RandomSource pRandom) 方法,而 getQuads(@Nullable BlockState state, @Nullable Direction side, @NotNull RandomSource rand, @NotNull ModelData data, @Nullable RenderType renderType)方法是不会生效的,所以这里我们直接抛出一个异常。

@Override
    public List<BakedQuad> getQuads(@Nullable BlockState pState, @Nullable Direction pDirection, RandomSource pRandom) {
        return this.existingModel.getQuads(pState, pDirection, pRandom);
    }

    @Override
    public @NotNull List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, @NotNull RandomSource rand, @NotNull ModelData data, @Nullable RenderType renderType) {
        throw new AssertionError("IForgeBakedModel::getQuads should never be called, only IForgeBakedModel::getQuads");
    }

第二个要说就是这个, 这个表示了我们的模型要使用自己定义的渲染,而不是使用默认的渲染。

    @Override
    public boolean isCustomRenderer() {
        return true;
    }

第三个就是这里,这里的意识是说在在不同的ItemDisplayContext下返回的烘焙模型不一样,这里我们在玩家的手持的时候返回我们这个模型,也是就是自定义渲染的这个模型,而且时候返回默认的模型。


    @Override
    public BakedModel applyTransform(ItemDisplayContext transformType, PoseStack poseStack, boolean applyLeftHandTransform) {
        if (transformType == ItemDisplayContext.FIRST_PERSON_RIGHT_HAND || transformType == ItemDisplayContext.FIRST_PERSON_LEFT_HAND){
            return this;
        }
        return this.existingModel.applyTransform(transformType,poseStack,applyLeftHandTransform);
    }```

使用我们的模型替换掉原本的烘焙模型

```java

    @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD,value = Dist.CLIENT)
    public static class ModEventBus{
        @SubscribeEvent
        public static void onModelBaked(ModelEvent.ModifyBakingResult event){
            // wrench item model
            Map<ResourceLocation, BakedModel> modelRegistry = event.getModels();
            ModelResourceLocation location = new ModelResourceLocation(BuiltInRegistries.ITEM.getKey(ModItems.WRENCH_ITEM.get()), "inventory");
            BakedModel existingModel = modelRegistry.get(location);
            if (existingModel == null) {
                throw new RuntimeException("Did not find Obsidian Hidden in registry");
            } else if (existingModel instanceof WrenchBakeModel) {
                throw new RuntimeException("Tried to replaceObsidian Hidden twice");
            } else {
                WrenchBakeModel obsidianWrenchBakedModel = new WrenchBakeModel(existingModel);
                event.getModels().put(location, obsidianWrenchBakedModel);
            }
        }
    }

和之前的内容大致相同,这里就不多加赘述了。

好了回到我们最开始的那个物品的类的中,我可以解释那段代码了。

    @Override
    public void initializeClient(Consumer<IClientItemExtensions> consumer) {
        consumer.accept(new IClientItemExtensions() {
            @Override
            public BlockEntityWithoutLevelRenderer getCustomRenderer() {
                return new WrenchItemRenderer();
            }
        });
    }

public BlockEntityWithoutLevelRenderer getCustomRenderer(): 这个方法返回一个自定义的渲染器,用于在客户端渲染 WrenchItem 对象。代码中创建了一个 WrenchItemRenderer 对象并返回。

26 物品特殊渲染
https://fuwari.vercel.app/posts/minecraft1_20_4/out_26-物品特殊渲染/
Author
Flandre923
Published at
2024-04-14