本篇教程的视频

本篇教程的源代码

GitHub地址:TutorialMod-Food-1.21

介绍

食物Food是Minecraft中的一种物品,可以用来恢复玩家的饥饿值和饱食度

一些食物可以让玩家获得一些效果,正面的亦或是负面的。比如生食直接食用可能导致中毒,让玩家更加饥饿;附魔金苹果可以提供大量正面效果

其具体的内容可在Wiki上查看

查看源代码

食物说白了本质还是物品,只是这种物品可以吃(蛋糕是个例外,它是方块,感兴趣的同学可以自行研究)。我们这里依旧来查阅Items中的食物注册,以APPLE为例

物品注册

1
public static final Item APPLE = register("apple", new Item(new Item.Settings().food(FoodComponents.APPLE)));

注册什么的我们就不再赘述了,这里主要看后面的food(FoodComponents.APPLE),它使用了food方法,这个方法是Item.Settings中的一个方法,用于设置食物的属性。
它里面的参数是FoodComponent,这个类是用于设置食物的属性的,比如饥饿值、饱食度、吃完后的效果等等

所以现在我们来查看FoodComponents中的APPLE

食物组件

1
public static final FoodComponent APPLE = new FoodComponent.Builder().nutrition(4).saturationModifier(0.3F).build();

这里的方法是链式调用,nutrition是饥饿值,saturationModifier是饱食度乘数,build是构建这个FoodComponent对象

这里我们来谈谈饥饿值饱食度这两个东西:

饥饿值是我们在游戏中能看到的那个鸡腿(HUD),它的最大值是20

饱食度是我们在游戏中看不到的,不过有模组可以将其显示出来,它的初始值为5,并且其值不能超过目前的饥饿值,在消耗饥饿值之前,它是最先被消耗的

更详细的说明同样可以在Wiki上查看

不过在这里的饱食度(saturationModifier)是一个乘数,并不是饱食度的具体值,因为这个饱食度是根据一定的算法计算得到的。

1
public static final FoodComponent GOLDEN_CARROT = new FoodComponent.Builder().nutrition(6).saturationModifier(1.2F).build();

如果你看到Wiki上的例子,金胡萝卜(回6点饥饿值,14.4饱和度),但在相关代码中却是这样

回6点饥饿值,这倒是没错;但14.4饱和度,这个值是怎么算出来的呢?

我们可以进一步追溯至HungerConstants

1
2
3
public static float calculateSaturation(int nutrition, float saturationModifier) {
return (float)nutrition * saturationModifier * 2.0F;
}

这个方法就是用来计算饱和度的,nutrition是饥饿值,saturationModifier是饱食度乘数,这个方法返回的值就是饱食度

而金胡萝卜的饱食度就是6 * 1.2 * 2 = 14.4,便是这么来的

那么除了简简单单写一个饥饿值和一个饱和度乘数,还有其他的参数可以加

1
2
3
4
5
6
7
public static final FoodComponent GOLDEN_APPLE = new FoodComponent.Builder()
.nutrition(4)
.saturationModifier(1.2F)
.statusEffect(new StatusEffectInstance(StatusEffects.REGENERATION, 100, 1), 1.0F)
.statusEffect(new StatusEffectInstance(StatusEffects.ABSORPTION, 2400, 0), 1.0F)
.alwaysEdible()
.build();

比如附魔金苹果,它除了回4点饥饿值,1.2倍饱食度乘数外,
还有两个效果,一个是生命恢复REGENERATION,一个是伤害吸收ABSORPTION
这两个效果的参数分别是持续时间(tick)和等级,最后一个是alwaysEdible
这个方法是用来设置食物是否可以在饥饿值满的时候依旧可以食用

另外,还有像谜之炖菜蘑菇煲等这类带的食物

1
2
3
4
5
6
7
public static final FoodComponent MUSHROOM_STEW = createStew(6).build();

public static final FoodComponent SUSPICIOUS_STEW = createStew(6).alwaysEdible().build();

private static FoodComponent.Builder createStew(int hunger) {
return new FoodComponent.Builder().nutrition(hunger).saturationModifier(0.6F).usingConvertsTo(Items.BOWL);
}

这类食物单独列出来了,因为它们除了要设置饥饿值和饱和度乘数之外,还要把返回回来(毕竟你不能把碗吃了吧?)这里就是用usingConvertsTo方法来设置的

谜之炖菜是一个有趣的食物,它的效果根据制作的材料自带的效果来决定的,可能是正面效果,也可能是负面效果,比如后面讲到的时候,我们会讲到

编写食物

创建ModFoodComponents类

首先我们创建一个ModFoodComponents类,用于存放我们的食物属性

1
2
3
public class ModFoodComponents {

}

注册食物组件

我们在ModFoodComponents类中注册我们的食物组件,直接参考FoodComponents中的写法

1
2
3
public static final FoodComponent CHEESE = new FoodComponent.Builder().nutrition(8).saturationModifier(0.8f).build();
public static final FoodComponent STRAWBERRY = new FoodComponent.Builder().nutrition(4).saturationModifier(0.6f)
.statusEffect(new StatusEffectInstance(StatusEffects.FIRE_RESISTANCE,600),0.5f).build();

这里我们注册了两个食物组件,一个是CHEESE,一个是STRAWBERRYCHEESE8点饥饿值,0.8倍饱食度乘数,
STRAWBERRY4点饥饿值,0.6倍饱食度乘数,带有抗火保护效果,持续600tick,获得几率0.5

这个类可以不写初始化方法,因为这个类的东西是在注册物品的时候还会调用的

注册食物

接下来我们在Items中注册我们的食物,同样参考Items中的写法

1
2
public static final Item CHEESE = registerItems("cheese", new Item(new Item.Settings().food(ModFoodComponents.CHEESE)));
public static final Item STRAWBERRY = registerItems("strawberry", new Item(new Item.Settings().food(ModFoodComponents.STRAWBERRY)));

这里我们注册了两个食物,一个是CHEESE,一个是STRAWBERRYCHEESE使用了CHEESE的食物组件,STRAWBERRY使用了STRAWBERRY的食物组件

加入物品栏

1
2
entries.add(ModItems.CHEESE);
entries.add(ModItems.STRAWBERRY);

这里我们把CHEESESTRAWBERRY加入到物品栏中

语言文件数据生成

1
2
translationBuilder.add(ModItems.STRAWBERRY, "Strawberry");
translationBuilder.add(ModItems.CHEESE, "Cheese");

模型文件数据生成

1
2
itemModelGenerator.register(ModItems.CHEESE, Models.GENERATED);
itemModelGenerator.register(ModItems.STRAWBERRY, Models.GENERATED);

这两个物品也是使用GENERATED模型,和一般物品一样

测试

跑玩数据生成以后,放好对应的材质文件,我们就可以在游戏中看到我们的食物了

在你饥饿值未满的情况下,应该可以正常食用,食用后应该可以回复对应的饥饿值和饱食度,以及获得对应的效果