文章目录
我的世界1.21.5 Fabric模组开发02 - 方块和物品
前言
- 在我的世界中,世界主要由方块、物品、环境、实体,这四种要素组成,它们互相影响,相互组合,构成五彩纷纷的世界。所以,本节课详细讲述如何创建并注册方块和物品,还要了解如何运用数据生成,以及什么是CRD模式等
- 如果你是经验比较丰富程序员,可以直接跳转到运用数据生成器即可
流程图
- 箭头表示编写步骤
- 不使用数据生成器情况下
- 常用流程(使用数据生成器)
文件结构
在创建方块物品之前,有必要搞懂文件结构,每个文件夹含义,知道这个文件应该放在哪里。
-
java包下文件结构
-
resources包下文件结构
- fabric.mod.json: 这个文件告诉这个项目/游戏,通过配置文件寻找模组主入口(实现
ModInitializer
接口的class类),来初始化创建新的方块和物品 - tutorial-mod.mixins.json: 这个文件用来配置Mixin类,用于原版代码注入操作
- fabric.mod.json: 这个文件告诉这个项目/游戏,通过配置文件寻找模组主入口(实现
-
assets包下文件结构
-
data包下文件结构(以后再说)
CRD模式
了解文件结构(项目结构)后,在了解CRG模式
- 什么是CRD模式
- CRD模式是Create Register Datagen,意思是创建、注册、数据生成
- 无论是类是方块还是物品,或者其他类,大多数都满足这三步,并且还有先后顺序
- 第一步:创建(方块、物品、药水、地物等)
- 第二步:注册(方块、物品等)
- 第三步:数据生成(方块模型、物品模型、物品模型映射等)
- 如果是简单方块类,或者物品类时,无需创建类(class 文件),只需调用原版对应类即可
- 什么是简单类
- 这个类继承原版的类(例如Block类和Item类)且只有构造方法存在,没有添加状态方法,实体方法,使用类等操作时,不用创建新的类,直接注册即可,防止类爆炸问题**
创建并注册方块
创建方块
在main/java/com/crystal/tutorial/block
包下新建CruciateStoneBlock
类,继承(extends)方块类(Block),而且每个方块必须要有含一个形式参数(Settings)的构造方法(构造函数)。
public class CruciateStoneBlock {
public CruciateStoneBlock(Settings settings) {
super(settings);
}
}
- 类的命名必须使用驼峰命名规则,且通常情况下是以Block单词结尾,例如:ExampleBlock、StoneBlock、SandBlock等,这样做目的是加以区分那个是方块?那个是物品?这样每个类有明确的含义。
注册方块
在main/java/com/crystal/tutorial
包下的TutorialMod
类,在onInitialize()
方法完成方块注册。
在注册方块之前,建议写of (String)
方法,如下
public static Identifier of(String path) {
return Identifier.of("modid", path);
}
这样方便后续注册操作,避免模组modid使用过于频繁,只需填入一个字符串即可。
public void onInitialize() {
/* Block Register */
RegistryKey<Block> key = RegistryKey.of(RegistryKeys.BLOCK, TutorialMod.of("cruciate_stone"));
Function<Settings, Block> factory = new Function<Settings, Block>() {
@Override
public Block apply(Settings settings) {
return new CruciateStoneBlock(settings);
}
};
Settings settings = Settings.copy(Blocks.STONE);
Block block = Blocks.register(key, factory, settings);
/* Block Item Register */
Items.register(block);
}
- RegistryKey(注册关键): 注册、标记类的类型,类型为方块(RegistryKeys.BLOCK),并记录模组id和方块名称,然后生成默认名称为
block.tutorial.cruciate_stone
,可以通过lang
包使默认名称覆盖新的(方块)名字。 - Function<Settings, Block>(方块函数): 在1.21.2之后,方块注册改为函数注册,将方块对象放在方块函数之后输出结果
- Settings(设置): 用来设置方块基本属性,例如:方块声音、方块硬度、方块材质等。
- Items.register(block) 方块物品注册: 将方块添加
register(Block)
中,即可完成方块物品(BlockItem)注册
方块外观
方块状态
- 在
src/main/resources/assets/tutorial/blockstates/
包下编写cruciate_stone.json
文件如下
{
"variants": {
"": {
"model": "tutorial:block/cruciate_stone"
}
}
}
- variants(变种): 通常情况下,简单方块没有指定任何方块属性,则方块模型只有一种。不像:红石灯、熔炉、覆雪草方块等,有多种不同情况下的模型
- model(模型): 简单方块六个面都对应六个模型,如果
""
空字符串,则六个面均应用一个模型,并指向models/block/
包下的模型Json文件
方块物品模型映射
- 在
src/main/resources/assets/tutorial/items/
包下编写cruciate_stone.json
文件
{
"model": {
"type": "minecraft:model",
"model": "tutorial:block/cruciate_stone"
}
}
- model(模型): 在1.21.4之后,可以为物品创建对应的物品模型映射,让物品直接使用方块的模型,并指向
models/block/
包下的模型Json文件
方块模型
- 在
src/main/resources/assets/tutorial/models/block/
包下编写cruciate_stone.json
文件
{
"parent": "block/cube_all",
"textures": {
"all": "tutorial:block/cruciate_stone"
}
}
- minecraft:block/cube_all(标准模型所有面): 在我的世界里大部分方块的基本结构。这部分方块使用同一个基本模型,以此模型使用它们的纹理,如果是原版模板文件,这个字符串
minecraft:
可以省略不写 - 除了原版外,我们可以自定义自己的模板文件,例如:连接纹理、双层纹理等(先挖个坑)
- textures(材质/纹理): 注意:
textures
单词是复数形式,不是texture
单数形式 - 这个
textures
会指向src/main/resources/assets/tutorial/textures/block/
包下,包括block本身以及block的子包,其他文件夹都无效果,且控制台不会报任何错误,游泳却出现紫黑方块,即使文件路径写对情况也是如此。
方块材质
- 在
src/main/resources/assets/tutorial/textures/block/
包下放置16*16方块像素图 - 通常情况下,大部分都是16*16像素画,而且是 2 n ( n ∈ N ∗ ) 2^n (n \in N^*) 2n(n∈N∗)的倍数关系,如果不是会出现材质错位现象(材质包除外)。
创建并注册物品
开端
- 自从1.21.2以后,物品注册方式改变,需要在
Items.register(RegistryKey<Item>, Function<Settings, Item>, Settings)
方法注册物品,才能使物品模型存储到物品组件上,否则出现空指针异常
java.lang.NullPointerException: Item id not set
创建物品
在main/java/com/crystal/tutorial/item
包下新建DiamondAppleItem
类,继承(extends)方块类(Item),而且每个方块必须要有含一个形式参数(Settings)的构造方法(构造函数)
public class DiamondApple extends Item {
public DiamondApple(Settings settings) {
super(settings);
}
}
- 与方块同理,如果是简单物品,不要新建物品类,直接注册物品即可
注册物品
- 在
main/java/com/crystal/tutorial
包下的TutorialMod
类,在onInitialize()
方法完成物品注册
public void onInitialize() {
RegistryKey<Item> key1 = RegistryKey.of(RegistryKeys.ITEM, TutorialMod.of("diamond_apple"));
Function<Item.Settings, Item> factory1 = new Function<Item.Settings, Item>() {
@Override
public Item apply(Item.Settings settings) {
return new DiamondAppleItem(settings);
}
};
Item.Settings item = new Item.Settings();
Items.register(key1, factory1, item);
}
-
- RegistryKey(注册关键): 与方块类似,这个类型是物品(RegistryKeys.ITEM),生成默认名称为
item.tutorial.diamond_apple
,可以通过lang
包使默认名称覆盖新的(物品)名字。
- RegistryKey(注册关键): 与方块类似,这个类型是物品(RegistryKeys.ITEM),生成默认名称为
物品外观
物品材质
- 在
src/main/resources/assets/tutorial/models/item/
包下编写diamond_apple.json
文件
{
"parent": "item/generated",
"textures": {
"layer0": "tutorial:item/diamond_apple"
}
}
- item/generated(扁平模型): 渲染出玩家手里着扁平化的物品。
- 同理,还有像拿武器形式,应该这样写
item/handheld
物品模型映射
- 在
src/main/resources/assets/tutorial/items
包下编写diamond_apple.json
文件
{
"model": {
"type": "model",
"model": "tutorial:item/diamond_apple"
}
}
运用数据生成器
- 或许你发现,完成物品和方块流程,需要手动编写7次Json文件,当中只要一个单词错误,整个方块物品材质都失效,太麻烦勒,有没有更轻松方式帮助我们编写材质?有我们可以使用数据生成器,来自动生成对应Json文件。
- 注意: 如果在resources包下存在上述Json文件,请必须删除,否则测试游戏崩溃,出现文件重复问题,确保文件是唯一的,独一无二(指的是文件内容,不是文件名)
- 在使用数据生成器之前,需要对我们项目做些改进
- 改进TutorialMod类,分担注册方块和物品责任,缩小体积。分成物品注册类和方块注册类,让TutorialMod类主入口只需绑定(引用)其他子注册类就行,这样才能降低耦合度
- 将方法block和item的局部变量,改为成员常量,这样做的因为能够使用数据生成器上调用它们的class对象,扩大作用范围。
- 在物品注册类和方块注册类类提供通用方法,将多余重复单词合并(封装)成一个方法,只需输入对应材料(类名和字符串)即可。
方块注册类
在java中的../block/
包下新建ModBlocks方法,提供两个方法,一个用来注册方块,一个用来被TutorialMod类调用并注册
public class ModBlocks {
/**
* 如果是简单方块,不用新建方块类,直接采用Block方块注册即可
*/
public static Block register(String name, Settings settings) {
return register(name, Block::new, settings);
}
public static Block register(String name, Function<Settings, Block> factory, Settings settings) {
RegistryKey<Block> key = RegistryKey.of(RegistryKeys.BLOCK, TutorialMod.of(name));
Block block = Blocks.register(key, factory, settings);
Items.register(block);
return block;
}
public static void init() {
}
}
- 然后编写成员常量,变量字母需要大写,每个单词用**_**分割
public static final Block CRUCIATE_STONE_BLOCK = register("cruciate_stone", Block::new, Settings.copy(Blocks.STONE));
物品注册类
在java中的../item/
包下新建ModItems方法,提供两个方法,一个用来注册方块,一个用来被TutorialMod类调用并注册
public class ModItems {
public static final Item DIAMOND_APPLE = register("diamond_apple", new Settings());
/**
* 如果是简单物品,不用新建物品类,直接采用Item物品注册即可
*/
public static Item register(String name, Settings settings) {
return register(name, Item::new, settings);
}
public static Item register(String name, Function<Settings, Item> factory, Settings settings) {
RegistryKey<Item> key = RegistryKey.of(RegistryKeys.ITEM, TutorialMod.of(name));
return Items.register(key, factory, settings);
}
}
- 和方块成员常量同理,只是
new Item.Settings()
,需要new出对应Setting内部class对象
public static final Item DIAMOND_APPLE = register("diamond_apple", new Settings());
数据模型
在client/java/com/crystal/tutorial/datagen/
包下新建一个TutorialModelGenerator(模型构造器/模型生成器),继承Fabric加载器准备好的数据生成器:FabricModelProvider
,并且必须实现一个构成方法,两个无返回值的方法
- 优点:效率快,操作简单,一键生成文件,无需手动添加(Json)文件
- 缺点:灵活性较差,新模型需要手动构建对应的模型,且需要游戏进行前,还需要生成一次
public class TutorialModelGenerator extends FabricModelProvider {
public TutorialModelGenerator(FabricDataOutput output) {
super(output);
}
@Override
public void generateBlockStateModels(BlockStateModelGenerator create) {
// code...
}
@Override
public void generateItemModels(ItemModelGenerator create) {
// code...
}
}
- FabricDataOutput(Fabric 数据输出): 这个类主要目的是将TutorialModelGenerator类打包(封装),发送并分析里面内容,来打印输出对应Json文件
- BlockStateModelGenerator(方块状态模型生成器): 这个类提供大量构造模型方法,其中最简单模型为标准模型(Cube);通过调用
ModBlocks
类中的成员常量即可创建对应模型,并且包括自动生成对应的方块状态、方块模型、方块物品模型映射、自动连接材质等功能 - ItemModelGenerator(物品模型生成器): 和方块状态模型生成器一样作用,自动生成对应的物品模型,物品模型映射。
- 在使用TutorialModelGenerator之前,首先在TutorialModDataGenerator(模组数据构造器/模组数据生成器),调用(连接)模型构造器,这样这个类才能找到这个类来生成对应文件
- 注意:在这个方法完成绑定
public void onInitializeDataGenerator(FabricDataGenerator generator) {
// 获取数据构造器的打包器,进行类名封装处理
FabricDataGenerator.Pack pack = generator.createPack();
// 添加名单
pack.addProvider(TutorialModelGenerator::new);
}
- 然后才能在这里继续编写代码
@Override
public void generateBlockStateModels(BlockStateModelGenerator create) {
create.registerSimpleCubeAll(ModBlocks.CRUCIATE_STONE);
}
@Override
public void generateItemModels(ItemModelGenerator create) {
create.register(ModItems.DIAMOND_APPLE, Models.GENERATED);
}
registerSimpleCubeAll(Block)
(注册简单标准模型所有面):这个方法用来创建,最简单方块(标准模型),所有面都用一个材质,一个模型register(Item, Model)
:用这个方法注册物品,生成item/generated
模型;除了这个模型以外,还可以这样:create.register(ModItems.DIAMOND_APPLE, Models.HANDHELD);
(像手里拿把武器一样)- 最后,先点击Data Generation,生成完成之后,在点击Minecraft Client 即可
总结
- 在实际开发当中,通常情况下,都遵循6步:创建(方块或物品)、注册(方块或物品)、(初始化)、数据生成、加入物品组、配置语言
- 开始创建第一方块和物品时,只需要初始化
init()
一次,然后第二次不用初始化,注册之后,可直接进入下一步“数据生成” - 第一步:在
block
(item
)包下创建新的方块类(物品类),如果是简单类,不需要创建,直接注册即可。 - 第二步:在
ModBlock
(ModItem
)类下,注册方块类(物品类) - 第三步:在
TutorialModelGenerator
类,完成方块模型(物品模型)数据生成。