本篇教程的视频

(待发布)

本篇教程的源代码

(待发布)

简介

这篇教程我们来制作一个高级物品——探矿器(Prospector)

可以用它来寻找矿物

另外,我们再额外加一个功能:当玩家按下Shift时,探矿器执行精确搜索;否则执行5×5范围搜索

探矿器

自定义物品

那么这个东西是一个高级物品,所以我们需要创建一个自定义物品类ProspectorItem,继承自Item

1
2
3
4
5
public class ProspectorItem extends Item {
public ProspectorItem(Properties pProperties) {
super(pProperties);
}
}

那么,接下来要重写物品使用的方法,因为一般的物品,就像我们之前写的那些物品,并没有使用的效果

物品的使用方法有很多,useOn是使用在方块上的方法,use方法则是常规的右键使用方法

比如一些食物类物品,都是重写的use方法;投掷物像雪球、末影珍珠等,都是重写的use方法

这里我们重写useOn方法,你同样可以重写use方法来实现类似的功能

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
51
52
53
54
55
56
57
@Override
public InteractionResult useOn(UseOnContext pContext) {
BlockPos pos = pContext.getClickedPos();
Player player = pContext.getPlayer();
Level level = pContext.getLevel();

if (!level.isClientSide()) {
boolean found = 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.below(i).north(j).east(k);
BlockState blockState = level.getBlockState(pos1);
String name = blockState.getBlock().getName().getString();

if (isCorrectBlock(blockState)) {
player.sendSystemMessage(Component.literal("Found" + name + "!"));
found = true;
break;
}
}
}
}
if (!found) {
player.sendSystemMessage(Component.literal("No ore found!"));
}
} else {
for (int i = 0; i <= pos.getY() + 64; i++) {
BlockPos pos1 = pos.below(i);
BlockState blockState = level.getBlockState(pos1);
String name = blockState.getBlock().getName().getString();

if (isCorrectBlock(blockState)) {
player.sendSystemMessage(Component.literal("Found" + name + "!"));
found = true;
break;
}
}
if (!found) {
player.sendSystemMessage(Component.literal("No ore found!"));
}
}
pContext.getItemInHand().hurtAndBreak(1, player,
p -> p.broadcastBreakEvent(pContext.getHand()));
return InteractionResult.SUCCESS;
}
return super.useOn(pContext);
}

private boolean isCorrectBlock(BlockState blockState) {
if (blockState.is(Blocks.DIAMOND_ORE) || blockState.is(Blocks.DEEPSLATE_DIAMOND_ORE)) {
return true;
} else {
return false;
}
}

这里我将代码全部放了出来,我们一点点来看

首先是获取玩家点击的方块位置pos,玩家对象player,以及世界对象level

然后我们判断当前代码是否在服务端执行,一些逻辑运算,都是交给服务器去执行的,就算是单人游戏,实际上也是有一个内置的服务器在运行的

客户端主要负责渲染和显示

接下来我们定义了一个found变量,用于标记是否找到了矿物,这个是为找到一个矿物就停止搜索做准备的

然后我们判断玩家是否按下了Shift键,如果没有按下Shift键,就执行5×5范围搜索

Shift键是否按下,可以通过Screen.hasShiftDown()方法来判断,这是一个已经封装好的方法

后面就是一堆循环了,我们从玩家点击的方块位置开始,向下搜索,直到世界底部

我们也封装了一个isCorrectBlock方法,用于判断当前方块是否是我们想要找的矿物

当然这里的矿物只写了钻石矿,因为这个教程是准备和后面一期的Tag教程配合使用的,后面我们会把所有矿物都放到一个Tag里,再来判断

如果找到了矿物,我们就给玩家发送一条消息,告诉他找到了什么矿物;如果没有找到矿物,我们也给玩家发送一条消息,告诉他没有找到矿物

最后,我们让探矿器损坏1点耐久值,然后返回InteractionResult.SUCCESS,表示使用成功

整体上的逻辑并不复杂,主要是循环和判断

至于这个循环是否很耗费性能,其实是没关系的,因为这个物品的使用频率并不会很高,而且搜索的范围也不是特别大,和遍历区块相比,影响可以忽略不计

注册物品

接下来我们在ModItems类中注册这个物品

1
2
public static final RegistryObject<Item> PROSPECTOR =
ITEMS.register("prospector", () -> new ProspectorItem(new Item.Properties().durability(127)));

这里我们注册了一个名为PROSPECTOR的物品,耐久值为127,不过可以使用128次

添加到物品栏

然后,还要把这个物品添加到物品栏中

1
pOutput.accept(ModItems.PROSPECTOR.get());

数据文件

同样,我们还是使用数据生成来添加各种数据文件

语言文件

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

模型文件

1
basicItem(ModItems.PROSPECTOR.get());

材质文件

最后还得将材质文件放到正确的位置