本篇教程的视频

本篇教程的源代码

Github地址:TutorialMod-RenderLayer-1.21

介绍

在前一篇教程中,我们加入了一堆建筑类的方块,其中可能有不少同学给门或者活板门的贴图是带有透明半透明区域的。
但是在实际游戏中,这些透明区域不仅没有呈现出透明的样子,反而会被渲染成黑色

这里我们就要单独为这些方块设置特殊的渲染方法,使其能够正确的呈现出透明或半透明的效果

为这些方块设置特殊的渲染方法,也有两种方法,一种是使用Fabric提供的API,另外一种则是Mixin

查看源代码

我们这里先讲解Mixin的方法,所以我们得去查看源代码

Blocks类中的OAK_DOOR为例

1
2
3
4
5
6
7
8
9
10
11
12
13
public static final Block OAK_DOOR = register(
"oak_door",
new DoorBlock(
BlockSetType.OAK,
AbstractBlock.Settings.create()
.mapColor(OAK_PLANKS.getDefaultMapColor())
.instrument(NoteBlockInstrument.BASS)
.strength(3.0F)
.nonOpaque()
.burnable()
.pistonBehavior(PistonBehavior.DESTROY)
)
);

这里我们看其设置了nonOpaque(),这个方法就是设置方块为非不透明的,也就是透明的

这个方法其实也可以说是声明这个方块是非实心的,一些不规则的、透明的方块都会为其设置。不然,当你放下我们的方块的时候,会变成这样(这里我语言组织不好了,放个图)

pic1

也就是我们可以通过其透明区域进行透视了,当然你要这种效果也不是不行

但是,即便你设置了这个方法,进入游戏还是黑色的区域,并不是透明的,因为我们还得设置其渲染层级

点击OAK_DOOR进行跳转,我们看到其在RenderLayers中被调用了

1
2
3
4
5
...
RenderLayer renderLayer3 = RenderLayer.getCutout();
...
map.put(Blocks.OAK_DOOR, renderLayer3);
...

这里我们看到OAK_DOOR的渲染层级是Cutout,这个渲染层级是用于透明区域是完全透明的方块的

另外我们也可以看到各种染色玻璃

1
2
3
4
5
...
RenderLayer renderLayer4 = RenderLayer.getTranslucent();
...
map.put(Blocks.WHITE_STAINED_GLASS, renderLayer4);
...

这里我们看到WHITE_STAINED_GLASS的渲染层级是Translucent,这个渲染层级是用于透明区域是半透明的方块的

其实还有两个不同的渲染层级,感兴趣的可以自行研究,对比着查看其效果

而后,我们退回到最外面

1
2
3
private static final Map<Block, RenderLayer> BLOCKS = Util.make(Maps.<Block, RenderLayer>newHashMap(), map -> {
...
});

这里我们看到BLOCKS是一个Map,里面存储了方块和其渲染层级的对应关系。那它既然是Map,等到这里的语句初始化完毕以后,我们是否可以通过put方法来添加我们自己的方块和渲染层级的对应关系呢?当然可以

Mixin方法

创建RenderLayersMixin类

1
2
3
4
@Mixin(RenderLayers.class)
public class RenderLayersMixin {

}

我们通过插件将Mixin类放到那个对应的json文件中的时候,我们发现其是在"client"下面的

而目标类也是用了@Environment(EnvType.CLIENT)注解的,说明渲染这种东西是仅在客户端进行的。其实也可以进一步思考,如果说光影、材质包这种东西交给服务端去进行渲染,那么这个服务器得有多强大啊

注入代码

1
2
3
4
5
6
7
@Shadow @Final private static Map<Block, RenderLayer> BLOCKS;

@Inject(method = "<clinit>", at = @At("RETURN"))
private static void onBlockInit(CallbackInfo ci) {
BLOCKS.put(ModBlocks.ICE_ETHER_DOOR, RenderLayer.getCutout());
BLOCKS.put(ModBlocks.ICE_ETHER_TRAPDOOR, RenderLayer.getCutout());
}

我们先通过@Shadow注解,将目标类中的BLOCKS字段拉过来

这里我们通过@Inject注解,将我们的方块和渲染层级的对应关系添加到BLOCKS

这里的method<clinit>,这个是表示静态初始化代码块的意思,atRETURN,这个是表示在静态代码块初始化完成后插入我们的代码

这种代码块我们已经写了很多了,对不对?就是我们的那些注册

为注册语句加nonOpaque()

1
2
3
4
public static final Block ICE_ETHER_DOOR = register("ice_ether_door",
new DoorBlock(BlockSetType.STONE, AbstractBlock.Settings.copy(ICE_ETHER_BLOCK).nonOpaque()));
public static final Block ICE_ETHER_TRAPDOOR = register("ice_ether_trapdoor",
new TrapdoorBlock(BlockSetType.IRON, AbstractBlock.Settings.copy(ICE_ETHER_BLOCK).nonOpaque()));

这个方法得加上,不然待会透视了

随后我们进入游戏,你就会发现原本黑色的区域变成了透明的,你也可以透过这些区域看到外面的世界了

Fabric API方法

当然,其实Fabric也给了我们一个API,我们可以通过这个API来设置渲染层级

我们去到TutorialModClient这个类下面,这个类是在开篇我们手动创建的,如果你这里没有请自行查看第一篇教程

1
2
3
4
5
@Override
public void onInitializeClient() {
BlockRenderLayerMap.INSTANCE.putBlock(ModBlocks.ICE_ETHER_DOOR, RenderLayer.getCutout());
BlockRenderLayerMap.INSTANCE.putBlock(ModBlocks.ICE_ETHER_TRAPDOOR, RenderLayer.getCutout());
}

我们在其onInitializeClient()方法下加入这两句代码,这样我们的方块就能正常渲染了

嗯对,有轮子就是这么方便,当然如果你想了解其中的原理,也可以进一步挖掘