目录
- 自定义物品与方块
- 自动侦测矿藏工具
- 工具功能
- 实现
- 执行结果
- 自定义音乐方块
- 自定义食物
- 自定义燃料
自定义物品与方块
自动侦测矿藏工具
探测器纹理下载地址: https://url.kaupenjoe.net/mbkj57/assets
众所周知,正经人永远不喜欢常规套路挖矿,如果能有一个自定探测脚下矿藏的工具就好了~
接下来我们将会一步步实现该工具
工具功能
耐久 64,右击方块使用,每次使用无论找到矿藏与否均销毁一点耐久
被右击的方块向下勘察 128 个方块,如果存在铁或者钻石矿石,就会在聊天框反馈给玩家
反之,如果找不到,就输出失败信息
实现
首先创建一个单独的 custom 文件夹,存储我们自定义的物品
新建类 MetalDetectorItem.java
具体位置可以查看下图
主要代码如下,相信你一定可以看得懂的!
tips:使用 alt+insert
快捷键可以快速添加构造函数哦
package com.example.item.custom;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemUsageContext;
import net.minecraft.text.Text;
import net.minecraft.util.ActionResult;
import net.minecraft.util.math.BlockPos;
import java.util.Objects;
// extends继承父类Item
public class MetalDetectorItem extends Item {
// 别忘了构造函数
public MetalDetectorItem(Settings settings){
super(settings);
}
// 当物品对方块使用的时候(即右键点击方块)
@Override
public ActionResult useOnBlock(ItemUsageContext context) {
// 如果是客户端
if(!context.getWorld().isClient()){
// 获取方块位置以及玩家位置,并初始化foundBlock变量以确定是否找到方块
BlockPos posClicked = context.getBlockPos();
PlayerEntity player = context.getPlayer();
boolean foundBlock = false;
// 据方块坐标,以Y轴为基准向下勘察128格
// posClicked.down(i)表示当前坐标向下i格
for(int i=0;i<=posClicked.getY()+128;i++){
// 逐个获取对应方块状态
BlockState state = context.getWorld().getBlockState(posClicked.down(i));
// 判断方块状态如果符合条件,输出成功信息
if(isTargetBlock(state)){
assert player != null;
outputTargetBlockCoordinates(
posClicked.down(i),
player,
state.getBlock()
);
foundBlock=true;
break;
}
}
// 如果啥都找不到,输出失败信息
if(!foundBlock){
assert player != null;
player.sendMessage(
Text.literal("No Target Ore Found!!!")
);
}
// 最后别忘了给玩家手上拿着的物品(也就是我探测器)加上1损耗
context.getStack().damage(1, Objects.requireNonNull(context.getPlayer()), player1 -> {
player1.sendToolBreakStatus(player1.getActiveHand());
});
}
return ActionResult.SUCCESS;
}
// 探测指定方块是不是铁矿石或者钻石矿石的函数
private boolean isTargetBlock(BlockState state){
return state.isOf(Blocks.IRON_ORE) || state.isOf(Blocks.DIAMOND_ORE);
}
// 输出矿藏坐标的函数
private void outputTargetBlockCoordinates(BlockPos blockPos, PlayerEntity player, Block block){
player.sendMessage(
Text.literal("Target Ore:"+block.asItem().getName().getString()+" at ("+blockPos.getX()+","+blockPos.getY()+","+blockPos.getZ()+")"),
false
);
}
}
执行结果
自定义音乐方块
和自定义物品一样,我们新建一个 custom 文件夹存储自定义的方块
按照下图所示位置创建自定义音乐盒方块 SoundBlock.java
音乐盒的代码很简单,就是右键点击一次就发出一个声音
代码清单 SoundBlock.java
package com.example.block.custom;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvent;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public class SoundBlock extends Block {
public SoundBlock(Settings settings) {
super(settings);
}
// onUse即为右键点击
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
// 在玩家所在位置播放一个声音
world.playSound(player,pos, SoundEvents.BLOCK_NOTE_BLOCK_BASS.value(), SoundCategory.BLOCKS,1f,1f);
return ActionResult.SUCCESS;
}
}
注意,我们在 ModBlocks 里面注册这个方块的时候,不要人为指定 sounds,否则会导致声音播放失败!
这是注册方块的写法
public static final Block SOUND_BLOCK = regBlock("sound_block",
new SoundBlock(FabricBlockSettings.copyOf(Blocks.WHITE_WOOL)));
自定义食物
番茄纹理下载:https://url.kaupenjoe.net/mbkj61/assets
我们将制作一个番茄,它吃下去会有一定效果
新建类 AwfulTomatoFood.java
用于表示我们的食物
代码清单
package com.example.item.food;
import net.minecraft.command.argument.NbtCompoundArgumentType;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.item.FoodComponent;
import net.minecraft.item.FoodComponents;
import net.minecraft.nbt.NbtCompound;
public class AwfulTomatoFood {
// 定义一个食物,使用FoodComponent.Builder
public static final FoodComponent AWFUL_TOMATO = new FoodComponent.Builder()
// 补充的饥饿值
.hunger(2)
// 设置饱和度(即饥饿值开始掉之前先消耗饱和度)
.saturationModifier(0.25f)
// 添加吃后的药水效果
// 参数一:药水效果,参数二:获得效果的几率
.statusEffect(new StatusEffectInstance(StatusEffects.POISON,200),0.5f)
.statusEffect(new StatusEffectInstance(StatusEffects.INSTANT_DAMAGE,1),0.25f)
// 饥饿值满的时候也可以吃
.alwaysEdible()
// 可以喂狗吃
.meat()
// 指定该食物是一个零食,此时你吃起来就会非常快(大概是吃普通食物耗费时间的一半)
.snack()
.build();
}
注册时不要再像之前注册自定义物品一样直接用这个类!请按照如下代码进行注册
代码清单 ModItems.java
// 要明确告诉编译器你需要注册一个食物,用到FabricItemSettings().food()
public static final Item AWFUL_TOMATO_FOOD = regItem("awful_tomato",
new Item(new FabricItemSettings().food(AwfulTomatoFood.AWFUL_TOMATO)));
自定义燃料
定义燃料很简单,直接像根据定义普通物品一样定义即可
代码清单 ModItems.java
package com.example.item;
import com.example.TutorialMod;
import com.example.item.custom.MetalDetectorItem;
import com.example.item.food.AwfulTomatoFood;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroupEntries;
import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents;
import net.fabricmc.fabric.api.registry.FuelRegistry;
import net.minecraft.item.Item;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
public class ModItems {
...
// 定义燃料物品
public static final Item COAL_BRIQUETTE = regItem("coal_briquette",
new Item(new FabricItemSettings()));
private static Item regItem(String name,Item item){
return Registry.register(Registries.ITEM,new Identifier(TutorialMod.MOD_ID,name),item);
}
public static void registerModItems(){
TutorialMod.LOGGER.debug("TutorialMod正在注册Items,MOD_ID:"+TutorialMod.MOD_ID);
// 在这里使用FuelRegistry注册燃料
// 参数一:欲注册的燃料名称
// 参数二:燃料热值(200即为在普通熔炉下熔炼一个物品的时长,这个时间在应用到高炉内是按比例的)
FuelRegistry.INSTANCE.add(ModItems.COAL_BRIQUETTE,200);
}
}
这里有一个关键概念要搞清楚
FuelRegistry.INSTANCE.add
接收的第二个参数应该理解为燃料热值,即 200 为一个标准值,能熔炼一个物品;
同理,在高炉中,由于热值固定,所以依然只够熔炼一个物品;
不应该将其理解为燃烧时长,否则移动到高炉里面就是两倍的熔炼时长了,这显然不对!!!