本篇教程的视频

本篇教程的源代码

GitHub地址:TutorialMod-Food-1.20.1

本篇教程目标

  • 理解原版食物食物组件相关注册、方法
  • 学会创建食物组件并为模组加入食物

查看源代码

这期教程我们来写食物,食物在游戏中是一类用来吃的物品(也有方块,如蛋糕

一些食物不仅仅能为玩家回复饥饿值饱和度,也还带有一些正面或者负面效果

这里我们先来看看原版的食物是怎么写的

食物注册

这里我们先到Items类来看看食物相关的注册,这里以APPLE为例

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

前面的不用管它了,我们也见得多了,我们可以看到最后有一个food方法,传入了一个FoodComponents.APPLE,这个就是食物组件

食物组件

接下来我们来看看食物组件是怎么写的

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

这里我们看到APPLE这个食物组件FoodComponent的实例化,用Builder来创建食物相关的一些属性

我们看后面几个方法,hunger用来设置食物的饥饿值saturationModifier用来设置食物的饱和度系数,最后build()用来完成食物组件的创建

饥饿值就是在我们HUD上显示的那些鸡腿,而饱和度在你不加其他模组的情况下是看不见的

不过,在saturationModifier方法中设置的,并不是最终食物回复的饱和度

一路挖到HungerManager类中,我们可以看到饱和度最终的计算公式

1
this.saturationLevel = Math.min(this.saturationLevel + (float)food * saturationModifier * 2.0F, (float)this.foodLevel);

可以看到,最终回复的饱和度是食物饥饿值 * 饱和度系数 * 2,然后因为饱和度是不能大于当前的饥饿值,所有是要取它们两个值的最小值的

所以对于APPLE而言,回复的饥饿值是4,饱和度系数是0.3,所以回复的饱和度就是4 * 0.3 * 2 = 2

而金胡萝卜

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

回复的饥饿值是6,饱和度系数是1.2,饱和度呢就来到了14.4

关于饥饿值饱和度相关的内容可以在Wiki进一步查阅

只是Wiki上并没有写出饱和度具体的计算公式,所以这里就单独提一下

效果及其他的方法

这里我们再来看看其他的方法

1
2
3
4
5
6
7
8
9
public static final FoodComponent ENCHANTED_GOLDEN_APPLE = new FoodComponent.Builder()
.hunger(4)
.saturationModifier(1.2F)
.statusEffect(new StatusEffectInstance(StatusEffects.REGENERATION, 400, 1), 1.0F)
.statusEffect(new StatusEffectInstance(StatusEffects.RESISTANCE, 6000, 0), 1.0F)
.statusEffect(new StatusEffectInstance(StatusEffects.FIRE_RESISTANCE, 6000, 0), 1.0F)
.statusEffect(new StatusEffectInstance(StatusEffects.ABSORPTION, 2400, 3), 1.0F)
.alwaysEdible()
.build();

最具代表性的就是附魔金苹果

可以看到,除了hungersaturationModifier方法,还有statusEffect方法,这个方法用来设置食物的效果,
传入一个StatusEffectInstance,设置效果、效果持续时间tick)、效果等级,最后一个参数是效果的触发几率100%就是1.0F

再一个这里的alwaysEdible()方法,这个方法用来设置食物可以在饥饿值满的情况下继续食用,没有这个方法的食物到玩家饥饿值满的时候就不能再食用了

1
public static final FoodComponent BEEF = new FoodComponent.Builder().hunger(3).saturationModifier(0.3F).meat().build();

再比如这里有个meat方法,这个方法用来设置食物是肉类,也就是可以给你驯服后的食用

1
public static final FoodComponent DRIED_KELP = new FoodComponent.Builder().hunger(1).saturationModifier(0.3F).snack().build();

再比如这里有个snack方法,这个方法用来设置食物是小吃,它可以让食物吃得更快

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

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

还有就是像蘑菇煲谜之炖菜这样的各种煲,它们这里有单独的方法,这倒是没什么特殊的

不过在1.21中,它改了一点,你将会发现还有个碗在下面的方法中,因为煲类食物总不能把碗也吃了吧,碗是得留下的

但在1.20中,倒是还没这样,返回空碗的方法是写在它们的类中的

另外,再多说几句,谜之炖菜的具体效果并不是在食物组件中定义的,它是根据合成谜之炖菜时使用的所带的不同效果,来决定最终谜之炖菜的效果

嗯,花的效果我们将在未来的教程中讲到

那么食物组件大概就这些内容,接下来我们就开始写我们自己的食物

食物注册

创建食物组件

和原版一样,我们先创建一个ModFoodComponents类,用来注册食物组件

1
2
3
public class ModFoodComponents {

}

然后我们在里面创建食物组件

1
2
3
4
5
6
7
8
public static final FoodComponent CORN = new FoodComponent.Builder().hunger(3).saturationModifier(0.7F).build();

public static final FoodComponent STRAWBERRY = new FoodComponent.Builder().hunger(1).saturationModifier(0.3F)
.statusEffect(new StatusEffectInstance(StatusEffects.SPEED, 200), 0.2F).build();

public static final FoodComponent CHEESE = new FoodComponent.Builder().hunger(4).saturationModifier(0.8F)
.statusEffect(new StatusEffectInstance(StatusEffects.JUMP_BOOST, 200), 0.2F)
.statusEffect(new StatusEffectInstance(StatusEffects.FIRE_RESISTANCE, 200), 0.2F).build();

这里我们创建CORNSTRAWBERRYCHEESE三个食物组件,然后定义它们不同的饥饿值饱和度还有效果

这里类的话,不用像物品、方块注册这样拿主类来调用完成初始化,因为我们待会注册食物物品的时候,还是会用到它们的

食物物品注册

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

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

这里我们创建CORNSTRAWBERRYCHEESE三个食物物品,然后设置它们的食物组件

加入物品栏

别忘了将我们创建的食物加入物品栏

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

资源文件

接下来我们搞定它们的各种数据文件

语言文件数据生成

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

食物物品模型数据生成

食物物品的模型和一般的物品一样,父模型也是GENERATED

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

那么其他的数据文件之类就不写了,大家可以根据自己的需求来编写

测试

数据生成记得跑一下,然后将贴图文件放到对应的textures中,然后就可以启动游戏进行测试了