本篇教程的视频

本篇教程的源代码

GitHub地址:TutorialMod-Prospector-1.21

介绍

所谓进阶物品,就是一些比较复杂的物品,它们可能需要一些特殊的逻辑,或者是一些特殊的效果

最本质的区别在于它们有自己的类,而不是简单的Item类,实例化的时候用的也是对应的类

原版中的很多物品都可以去看看,看看它们的实现逻辑,然后再去实现自己的物品

在这篇教程中,我们将制作一个进阶物品——探矿器Prospector。探矿器是一种可以用来探测矿石的物品,它可以在玩家使用时输出矿石的位置,帮助玩家更快地找到矿石

这篇教程是和后面的Tag教程配合的,后续我们会使用Tag来优化探矿器的代码

思路

在开始着手编程之前,我们需要先思考一下探矿器的逻辑,你希望它怎么工作。这个过程是非常重要的,先搭框架再填内容。不要写到哪算哪,这样效率不高,甚至面临推翻重写的风险

那么在这里,我给出我的思路:

  1. 探矿器是一种物品,所以我们需要继承Item类,以继承物品的基本属性

  2. 探矿器是一种可以使用的物品,不过要如何使用?我们可以在玩家右键方块的时候使用探矿器,然后输出矿石的位置。这里我们可以重写useOnBlock来实现

    这里说明一下,Item类中的那些公共方法是可以重写的,一些方法Fabric也加了注释,可供参考。具体的实现方法参考源代码或者其他的模组

    其实使用物品的方法不只有useOnBlock,还有usefinishUsing等等,其他的方法可以自己尝试

  3. 那么探矿器是怎么工作的?这个例子在1.20的教程中也有,我们对其进行一个改写。

    现在我们让这个探矿器有两种模式,分别是精确搜索模糊搜索,精确搜索可以找到具体某一个矿石的确切位置,而模糊搜索一片矿石的位置

    我们希望玩家正常使用时是模糊搜索,而蹲下使用时是精确搜索,这样就可以实现两种模式的切换了

  4. 那么这两个模式的范围是多少呢?模糊搜索的范围是以右击的方块为起点,向x轴和z轴方向拓展,各拓展5格,这样就得到了一个5*5的范围,外加竖向的搜索

    精确搜索则就以右击的方块进行竖向搜索,直到找到矿石为止

  5. 蹲下这个东西怎么判断?玩Minecraft的人都知道,蹲下是按Shift,那么我们是否可以判断玩家是否按下了Shift键呢?嘿,巧了,Screen类就提供了这样的方法

  6. 另外则是一些小细节,比如探矿器的耐久矿石的种类屏幕输出等等

编写探矿器

创建Prospector类

首先我们需要创建一个Prospector类,这个类继承Item类,实现super函数

1
2
3
4
5
public class Prospector extends Item {
public Prospector(Settings settings) {
super(settings);
}
}

重写useOnBlock方法

然后我们重写useOnBlock方法,这个方法是在玩家右键方块时调用的,我们可以在这里实现探矿器的逻辑

我们重写方法时,只要输入对应的方法名,用TAB补全就可以了

1
2
3
4
@Override
public ActionResult useOnBlock(ItemUsageContext context) {
return super.useOnBlock(context);
}

里面要写的东西就是探矿器的逻辑了

咋搜索啊?遍历呗,for循环,然后判断是不是矿石,是的话就输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@Override
public ActionResult useOnBlock(ItemUsageContext context) {
BlockPos pos = context.getBlockPos();
PlayerEntity player = context.getPlayer();
World world = context.getWorld();

if (!world.isClient()) {
boolean foundBlock = false;
if (!Screen.hasShiftDown()) {
// 实现模糊搜索
for (int i = 0; i <= pos.getY() + 64; i++) {
for (int j = 0; j < 5; j++) {
for (int k = 0; k < 5; k++) {
BlockPos pos1 = pos.down(i).north(j).east(k);
BlockState blockState = world.getBlockState(pos1);
String name = blockState.getBlock().getName().getString();

if (isRightBlock(blockState)) {
player.sendMessage(Text.of("Found" + name + "!"));
foundBlock = true;
break;
}
}
}
}
if (!foundBlock) {
player.sendMessage(Text.of("No ore found!"));
}
} else {
// 实现精确搜索
for (int i = 0; i <= pos.getY() + 64; i++) {
BlockPos pos1 = pos.down(i);
BlockState blockState = world.getBlockState(pos1);
String name = blockState.getBlock().getName().getString();

if (isRightBlock(blockState)) {
player.sendMessage(Text.of("Found" + name + "!"));
foundBlock = true;
break;
}
}
if (!foundBlock) {
player.sendMessage(Text.of("No ore found!"));
}
}
context.getStack().damage(1, player, EquipmentSlot.MAINHAND);
return ActionResult.SUCCESS;
}
return super.useOnBlock(context);
}

我这里直接放上来了,教程的视频可以看我遍敲遍讲的过程

其逻辑其实就是遍历,找到了就给玩家输出信息,然后返回ActionResult.SUCCESS,表示成功使用

BlockPos posPlayerEntity playerWorld world这几个变量我是后来拎出来的。
在实际开发过程中,有些时候我们并不知道自己需要什么变量,可以到后期优化的时候,将一些重复的代码提取出来,然后再进行优化

模糊搜索有三层循环,精确搜索只有一层循环,这是因为精确搜索只需要竖向搜索,而模糊搜索需要横向搜索

Screen.hasShiftDown()这个方法就是判断玩家是否按下了Shift

这里我们只输出找到的第一个矿石,使用break跳出循环,如果全部输出的话可能有一堆,反倒是不好判断了

player.sendMessage(Text.of("Found" + name + "!"));这个方法是给玩家输出信息,name是矿石的名字;同理没找到则输出No ore found!

context.getStack().damage(1, player, EquipmentSlot.MAINHAND);这个方法是让探矿器的耐久减少1,这条语句和1.20的不太一样,改了一些参数

isRightBlock(blockState)这个方法是判断这个方块是不是矿石,这个方法我们需要自己实现

1
2
3
4
5
6
7
private boolean isRightBlock(BlockState blockState) {
if (blockState.isOf(Blocks.DIAMOND_ORE) || blockState.isOf(Blocks.IRON_ORE)) {
return true;
}else {
return false;
}
}

这个方法很简单,就是判断这个方块是不是钻石矿石或者铁矿石,是的话就返回true,否则返回false

虽然这里采用的是硬编码的形式,但再讲解完Tag之后,我们会使用Tag来优化这个方法

注册探矿器

和注册物品一样,我们需要在ModItems类中注册探矿器

1
public static final Item PROSPECTOR = registerItems("prospector", new Prospector(new Item.Settings().maxDamage(127)));

这里我们注册了一个PROSPECTOR物品,即探矿器,实例化的是我们刚刚写的类,有别于普通的物品,我们还要设置最大耐久,这里设置为127(可以使用128次,0~127)

加入物品栏

1
entries.add(ModItems.PROSPECTOR);

数据生成

语言文件

1
translationBuilder.add(ModItems.PROSPECTOR, "Prospector");

模型文件

1
itemModelGenerator.register(ModItems.PROSPECTOR, Models.GENERATED);

然后记得跑一遍Data Generation,生成数据。当然,贴图也别忘了

测试

然后我们就可以测试了,将探矿器放到物品栏中,右键方块,看看是否能找到矿石