本篇教程的视频
(待发布)
本篇教程的源代码
(待发布)
本篇教程目标
让自定义配方兼容REI,让REI能够显示自定义配方
介绍
上一期教程我们完成了自定义配方的编写,也让我们的方块实体能够通过配方去加工物品
那么,按照常规惯例,我们肯定希望像REI、JEI这样的物品管理器能显示我们的配方
而这个就需要我们写适配代码了
注:Fabric系列的教程只讲REI,JEI会在Forge和NeoForge的教程中讲,实际上两者差不多
添加依赖
那么首先我们需要添加REI的依赖,添加的教程实际上它的仓库里也有,可参见:REI
在build.gradle中添加依赖
1 2 3 4 5 6 7 8
| repositories { maven { url "https://maven.shedaniel.me" } }
dependencies { modCompileOnly "me.shedaniel:RoughlyEnoughItems-api-fabric:12.1.785" modRuntimeOnly "me.shedaniel:RoughlyEnoughItems-fabric:12.1.785" }
|
版本的话同样还是可以找一个下载量大的版本
然后重新构建项目,下载相应的依赖,同样出现BUILD SUCCESSFUL即可
ModREIClientPlugins 类
接下来我们新建一个ModREIClientPlugins,并实现REIClientPlugin接口
1 2 3 4 5 6 7 8 9 10 11 12
| public class ModREIClientPlugins implements REIClientPlugin {
@Override public void registerCategories(CategoryRegistry registry) { REIClientPlugin.super.registerCategories(registry); }
@Override public void registerDisplays(DisplayRegistry registry) { REIClientPlugin.super.registerDisplays(registry); } }
|
顺便再重新两个方法,一个registerCategories,一个registerDisplays,待会再用
然后,我们还需要到fabric.mod.json文件中的entrypoints中添加这个类
1 2 3 4 5 6
| "entrypoints": { ... "rei_client": [ "com.besson.tutorial.compat.ModREIClientPlugins" ] }
|
PortableOriginiumRigDisplay 类
新建类
接下来我们新建一个PortableOriginiumRigDisplay类,实现Display接口(注意是REI的类)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class PortableOriginiumRigDisplay implements Display { @Override public List<EntryIngredient> getInputEntries() { return List.of(); }
@Override public List<EntryIngredient> getOutputEntries() { return List.of(); }
@Override public CategoryIdentifier<?> getCategoryIdentifier() { return null; } }
|
还需要实现三个方法
这个类是用来做展示我们的配方的,简单来说,也就是展示输入和对应的输出
添加字段
1 2 3 4 5 6 7
| private final EntryIngredient input; private final EntryIngredient output;
public PortableOriginiumRigDisplay(OreRigRecipe recipe) { this.input = EntryIngredients.ofIngredient(recipe.getInput()); this.output = EntryIngredients.of(recipe.getOutput(null)); }
|
这里我们添加了两个字段,分别对应输入和输出,并创建相应的构造函数
EntryIngredient也同样是REI的类
构造函数中,用我们自定义配方的输入和输出来初始化这里的输入和输出
当然,记得在之前的配方中加上getInput方法,因为之前没有这个方法
1 2 3
| public Ingredient getInput() { return this.input; }
|
1 2 3 4 5 6 7 8 9
| @Override public List<EntryIngredient> getInputEntries() { return List.of(input); }
@Override public List<EntryIngredient> getOutputEntries() { return List.of(output); }
|
重写这两个方法,传入我们刚刚创建的输入和输出
至于另外一个方法我们等会再回过头来写
PortableOriginiumRigCategory 类
新建类
新建一个PortableOriginiumRigCategory类,实现DisplayCategory接口,泛型对应我们刚才写的PortableOriginiumRigDisplay类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class PortableOriginiumRigCategory implements DisplayCategory<PortableOriginiumRigDisplay> { @Override public CategoryIdentifier<? extends PortableOriginiumRigDisplay> getCategoryIdentifier() { return null; }
@Override public Text getTitle() { return null; }
@Override public Renderer getIcon() { return null; } }
|
另外再实现三个方法
相比较前面的Display类,这个类是用来创建一个标签页的,即在REI中我们方块实体专属的GUI界面
添加字段
1 2 3 4 5 6 7
| public static final CategoryIdentifier<PortableOriginiumRigDisplay> PORTABLE_ORIGINIUM_RIG = CategoryIdentifier.of(TutorialModRe.MOD_ID, "portable_originium_rig");
@Override public CategoryIdentifier<? extends PortableOriginiumRigDisplay> getCategoryIdentifier() { return PORTABLE_ORIGINIUM_RIG; }
|
这里我们添加了一个字段,用来表示这个标签页的ID
并重写getCategoryIdentifier方法,返回这个字段
另外,我们现在可以回过头去写PortableOriginiumRigDisplay类中的getCategoryIdentifier方法了
1 2 3 4
| @Override public CategoryIdentifier<?> getCategoryIdentifier() { return PortableOriginiumRigCategory.PORTABLE_ORIGINIUM_RIG; }
|
重写 getTitle & getIcon 方法
1 2 3 4 5 6 7 8 9
| @Override public Text getTitle() { return Text.translatable("blockEntity.portable_originium_rig"); }
@Override public Renderer getIcon() { return EntryStacks.of(ModBlocks.PORTABLE_ORIGINIUM_RIG.asItem().getDefaultStack()); }
|
getTitle方法,返回这个标签页的标题,另一个就是返回这个标签页的图标
重写 setupDisplay 方法
接下来我们还需要手动重写一个setupDisplay,用于编辑这个界面上的配方的展示形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Override public List<Widget> setupDisplay(PortableOriginiumRigDisplay display, Rectangle bounds) { Point startPoint = new Point(bounds.getCenterX() - 41, bounds.getCenterY() - 8); List<Widget> widgets = new ArrayList<>(); widgets.add(Widgets.createRecipeBase(bounds)); widgets.add(Widgets.createArrow(new Point(startPoint.x + 25, startPoint.y))); if (!display.getInputEntries().isEmpty()) { widgets.add(Widgets.createSlot(new Point(startPoint.x, startPoint.y)) .entries(display.getInputEntries().get(0)) .markInput()); } if (!display.getOutputEntries().isEmpty()) { widgets.add(Widgets.createSlot(new Point(startPoint.x + 55, startPoint.y)) .entries(display.getOutputEntries().get(0)) .markOutput()); } return widgets; }
|
注意这里的Point、Widget都是REI的类,导入时不要搞错
这里其实和绘制GUI的逻辑差不多,总体上来说是:
- 设置绘制起始点坐标(根据需要自己调整);
- 创建一个空的列表,用于存放绘制的元素;
- 绘制背景,这个
createRecipeBase会创建一个默认的背景,也可以用createTexturedWidget,这样就可以使用自己的GUI贴图;
- 绘制箭头,这里也是用
REI提供的默认箭头,如果你使用自定义GUI材质,上面有箭头的话就可以跳过了;
- 当输入、输出不为空时,各自添加一个
Slot,并设置输入、输出标记;
- 最后返回这个列表,由其他方法根据这个列表绘制界面;
重写 registerCategories 方法
编写完两个类之后,我们还要回到ModREIPlugins类中重写registerCategories方法
1 2 3 4 5
| @Override public void registerCategories(CategoryRegistry registry) { registry.add(new PortableOriginiumRigCategory()); registry.addWorkstations(PortableOriginiumRigCategory.PORTABLE_ORIGINIUM_RIG, EntryStacks.of(ModBlocks.PORTABLE_ORIGINIUM_RIG)); }
|
这里我们写两条语句,一个是注册PortableOriginiumRigCategory类,一个是设置这个类对应的标签页的工作台
重写 registerDisplays 方法
另一个方法也需要重写
1 2 3 4
| @Override public void registerDisplays(DisplayRegistry registry) { registry.registerRecipeFiller(OreRigRecipe.class, OreRigRecipe.Type.INSTANCE, PortableOriginiumRigDisplay::new); }
|
这里我们把OreRigRecipe类中的Type字段传入,REI自己会寻找匹配的配方并填充到PortableOriginiumRigDisplay类中
这样我们的适配层就写完了,现在可以启动游戏进行测试了