User Tools

Site Tools


zh_cn:tutorial:datagen_model

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
zh_cn:tutorial:datagen_model [2025/04/01 14:51] – [先从最简单的开始] solidblockzh_cn:tutorial:datagen_model [2025/04/01 15:47] (current) – [模型生成] solidblock
Line 6: Line 6:
 > :!: **注意:**我们在之前的教程中,在 ''resources'' 文件夹内可能已经手动创建了一些 JSON 文件。当数据生成器生成同名的 JSON 文件时,会与我们已经手动创建的 JSON 冲突,因此在运行数据生成前,请删除它们。 > :!: **注意:**我们在之前的教程中,在 ''resources'' 文件夹内可能已经手动创建了一些 JSON 文件。当数据生成器生成同名的 JSON 文件时,会与我们已经手动创建的 JSON 冲突,因此在运行数据生成前,请删除它们。
  
-===== 先从最简单的开始 =====+> :!: **注意:**从 1.21.4 开始,数据生成会区分客户端和服务器,原版中与模型有关的类都会被加上 ''@Environment(EnvType.CLIENT)'',仅在客户端环境下可用。如果你在实际运行数据生成时,发现是服务器环境从而报错,请参考[[datagen_setup|数据生成入门教程]]的手动数据生成部分,在 ''build.gradle'' 中修改 ''fabricApi'' 块以启用客户端环境,然后重新生成 IDE 的运行配置。 
 +===== 准备 =====
  
 首先,先创建一个类,继承 ''FabricModelProvider'',并在数据生成的入口点注册它: 首先,先创建一个类,继承 ''FabricModelProvider'',并在数据生成的入口点注册它:
Line 40: Line 41:
 </code> </code>
  
-==== 简单的方块模型 ====+===== 简单的方块模型 =====
 我们在 [[blocks]] 的教程中创建过一个示例方块。那么在这里,我们使用短短几行代码,为它创建方块状态映射和方块模型: 我们在 [[blocks]] 的教程中创建过一个示例方块。那么在这里,我们使用短短几行代码,为它创建方块状态映射和方块模型:
 <code java TutorialModelGenerator.java> <code java TutorialModelGenerator.java>
Line 62: Line 63:
 </code> </code>
  
-==== 简单的物品模型(1.21.4 之后) ====+===== 简单的物品模型(1.21.4 之后) =====
 生成物品模型也很简单: 生成物品模型也很简单:
 <code java TutorialModelGenerator.java> <code java TutorialModelGenerator.java>
Line 83: Line 84:
 </code> </code>
  
-=====添加方块模型=====+===== 常见的原版方块模型(以楼梯和台阶为例) =====
  
-''BlockStateModelGenerator'' 类包含了很多创建方块模型的方在本,我们将会创建一个每个面都相同的方块,是 JSON 中的 ''minecraft:block/cube_all''+有时方块模型并不简单,因为涉及多种复杂的方块状态。例楼梯就有多种朝向,可以是东南西北,可以是正放或倒放,而且还可以是角落的。我们自己去为这些方块创建模型和方块状态映射显然会很费神。好在这些常用的方块,原版已经给我们写好了,我们只需要使用可以。这里以楼梯和台阶为例,我们为钻石块创建相应楼梯和台阶首先,我们先快速地创建这些方块:
  
-<code java> +对于 1.21.2 及之后的版本: 
-public static Block SIMPLE_BLOCK Registry.register(Registries.BLOCK, new Identifier("tutorial""simple_block"), new Block(...)); +<code java TutorialBlocks.json
-public static BlockItem SIMPLE_BLOCK_ITEM Registry.register(Registries.ITEM, ..., new BlockItem(SIMPLE_BLOCK, ...)); +  public static final Block DIAMOND_STAIRS = register("diamond_stairs", 
-// ...+      settings -> new StairsBlock(Blocks.DIAMOND_BLOCK.getDefaultState()settings), 
 +      AbstractBlock.Settings.copy(Blocks.DIAMOND_BLOCK)); 
 +  public static final Block DIAMOND_SLAB = register("diamond_slab", 
 +      SlabBlock::new, 
 +      AbstractBlock.Settings.copy(Blocks.DIORITE_SLAB)); 
 +</code>
  
-@Override + 
-public void generateBlockStateModels(BlockStateModelGenerator blockStateModelGenerator{ +对于 1.21.2 之前的版本: 
- blockStateModelGenerator.registerSimpleCubeAll(SIMPLE_BLOCK); +<code java TutorialBlocks.json> 
-}+  public static final Block DIAMOND_STAIRS = register("diamond_stairs", 
 +      new StairsBlock(Blocks.DIAMOND_BLOCK.getDefaultState(), 
 +      AbstractBlock.Settings.copy(Blocks.DIAMOND_BLOCK))); 
 +  public static final Block DIAMOND_SLAB = register("diamond_slab", 
 +      new SlabBlock(AbstractBlock.Settings.copy(Blocks.DIORITE_SLAB)));
 </code> </code>
  
-因为 ''SIMPLE_BLOCK'' 的 ''BlockItem'' 已经存在并且已经注册所以会自动生成一个物品模型,该物品模型直接继承方块模型。这可以在 ''generateItemModels'' 方法中重写+然后我们先分别创建常规楼梯模型、内角落楼梯模型、外角落楼梯模型、底台阶模型、顶台阶模型。双台阶模型我们直接使用原版的钻石块模型。创建些模型时,会返回模型的 id,然后使用 ''BlockStateModelGenerator'' 中的各个实用方法去创建方块状态映射,会使用到之前返回的模型 id。我们还需要使用常规楼梯模型和底台阶模型作为物品模型
  
-====严格验证=====+<code java TutorialModelGenerator.json> 
 +    final TextureMap diamondTexture TextureMap.all(Identifier.ofVanilla("block/diamond_block")); 
 +     
 +    final Identifier stairsModelId Models.STAIRS.upload(TutorialBlocks.DIAMOND_STAIRS, diamondTexture, blockStateModelGenerator.modelCollector); 
 +    final Identifier innerStairsModelId Models.INNER_STAIRS.upload(TutorialBlocks.DIAMOND_STAIRS, diamondTexture, blockStateModelGenerator.modelCollector); 
 +    final Identifier outerStairsModelId Models.OUTER_STAIRS.upload(TutorialBlocks.DIAMOND_STAIRS, diamondTexture, blockStateModelGenerator.modelCollector); 
 +    blockStateModelGenerator.blockStateCollector.accept( 
 +        BlockStateModelGenerator.createStairsBlockState(TutorialBlocks.DIAMOND_STAIRS, 
 +            BlockStateModelGenerator.createWeightedVariant(innerStairsModelId), 
 +            BlockStateModelGenerator.createWeightedVariant(stairsModelId), 
 +            BlockStateModelGenerator.createWeightedVariant(outerStairsModelId))); 
 +    blockStateModelGenerator.registerParentedItemModel(TutorialBlocks.DIAMOND_STAIRS, stairsModelId);
  
-默认情况下,如果运行过程中,没有为所有属于被处理的模组的方块生成方块模型,数据生成将报错。Fabric API 允许禁用该功能。为此,请编辑你的 ''build.gradle'' 文件,删除 ''loom {}'' 块中的 VM 参数 ''-Dfabric-api.datagen.strict-validation'' +    final Identifier slabBottomModelId = Models.SLAB.upload(TutorialBlocks.DIAMOND_SLAB, diamondTexture, blockStateModelGenerator.modelCollector); 
-=====添加物品模型=====+    final Identifier slabTopModelId Models.SLAB_TOP.upload(TutorialBlocks.DIAMOND_SLAB, diamondTexture, blockStateModelGenerator.modelCollector); 
 +    blockStateModelGenerator.blockStateCollector.accept( 
 +        BlockStateModelGenerator.createSlabBlockState(TutorialBlocks.DIAMOND_SLAB, 
 +            BlockStateModelGenerator.createWeightedVariant(slabBottomModelId), 
 +            BlockStateModelGenerator.createWeightedVariant(slabTopModelId), 
 +            BlockStateModelGenerator.createWeightedVariant(Identifier.ofVanilla("block/diamond_block"))) 
 +    ); 
 +    blockStateModelGenerator.registerParentedItemModel(TutorialBlocks.DIAMOND_SLAB, slabBottomModelId); 
 +</code>
  
-''ItemModelGenerator'' 包含了其他的多种创建物品模型的方法。+> 在 1.21.4 及之前的版本中,不需要调用 ''BlockStateModelGenerator.createWeightedVariant'' 方法中,直接将 id 传作参数即可
  
-个例子中我们重写上一个例子中的 ''SIMPLE_BLOCK'' 方块模型生成的物品模型。我们将从 ''SIMPLE_BLOCK_ITEM'' 生成一个物品模型+大功告成!我们就仅仅通过么几行代码,成功地添加了楼梯和台阶所需要一切模型、方块状态映射和物品模型映射!
  
-<code java> +===== 带有朝向的方块(以竖直台阶为例) =====
-public static Block SIMPLE_BLOCK Registry.register(Registries.BLOCK, new Identifier("tutorial", "simple_block"), new Block(...)); +
-public static BlockItem SIMPLE_BLOCK_ITEM Registry.register(Registries.ITEM, ..., new BlockItem(SIMPLE_BLOCK, ...)); +
-// ...+
  
-@Override +带有朝向的方块通常仅使用一个方块模型,但是在方块模型映射中,会映射到不同的模型变种,例如不同的 x 旋转、y 旋转以及 uvlock。(这些词是不是在之前手动写方块状态映射的 JSON 时有些熟悉?) 
-public void generateItemModels(ItemModelGenerator itemModelGenerator{ + 
- itemModelGenerator.register(SIMPLE_BLOCK_ITEMModels.GENERATED);+我们以 [[directionalblock]] 中创建的竖直台阶为例,通过数据生成器为其生成模型和方块状态映射。 
 + 
 +==== 自定义模型 ==== 
 + 
 +我们之前创建过一个 ''tutorial:block/vertical_slab'' 的模型,作为竖直方块模型的模板。这种模板模型通常不需要使用数据生成器生成。然后我们为竖直的磨制安山岩台阶方块创建了名为 ''tutorial:block/polished_andesite_vertical_slab'',继承前面提到的模板模型,并提供纹理变量。 
 + 
 +为了在数据生成器中继承此模板模型,我们需要为此模板模型创建 ''Model'' 对象。该模型的 JSON 中会使用 ''bottom''、''top'' 和 ''side'' 这三个纹理变量,因此我们在这里的代码中也依赖这三个纹理变量,数据生成器将在生成模型时为这几个变量赋值。 
 +<code java TutorialModelGenerator.java> 
 +public class TutorialModelGenerator extends FabricModelProvider { 
 +  public static final Model VERTICAL_SLAB = new Model( 
 +      Optional.of(Identifier.of("tutorial", "block/vertical_slab")), 
 +      Optional.empty(), 
 +      TextureKey.BOTTOM, TextureKey.TOP, TextureKey.SIDE); 
 +   
 +  // ...
 } }
 </code> </code>
  
-FIXME //**正在施工中**//+然后再调用 ''upload'' 方法即可。该方法将生成方块模型,并返回该模型的 id。在这里,我们一并把物品模型映射做好,让物品也直接使用此方块模型。 
 +<code java TutorialModelGenerator.java> 
 +  @Override 
 +  public void generateBlockStateModels(BlockStateModelGenerator blockStateModelGenerator) { 
 +    // ... 
 +     
 +    final Identifier verticalSlabModelId = VERTICAL_SLAB.upload(TutorialBlocks.POLISHED_ANDESITE_VERTICAL_SLAB, TextureMap.all(Identifier.ofVanilla("block/polished_andesite")), blockStateModelGenerator.modelCollector); 
 +    blockStateModelGenerator.registerParentedItemModel(TutorialBlocks.POLISHED_ANDESITE_VERTICAL_SLAB, verticalSlabModelId); 
 +  } 
 +</code>
  
-==== 为带有朝向的方块添加数据生成 ====+==== 自定义方块状态映射(1.21.5 之后) ==== 
 +重头戏来了——现在我们要为竖直台阶方块创建方块状态映射。其实没那么复杂,因为我们知道模型都是一样的,只需要有不同的旋转而已。
  
-**警告**:这将非常复杂!!!+在 1.21.5 中,方块状态的对象是 ''BlockModelDefinitionCreator'',分为两种: 
 +  ''VariantsBlockModelDefinitionCreator'':根据方块状态分配一个方块模型变种。简单的方块都是用的这种。我们这里所讨论的竖直台阶也是这种。 
 +  ''MultipartBlockModelDefinitionCreator''由多个部分组成的方块,每个部分都是一个方块模型变种,每个部分会根据其方块状态来决定是否显示。原版的红石线就是种。
  
-在本例中,我们将为 ''MACHINE_BLOCK'' 生成带朝向的方块模型。+对于 ''VariantsBlockModelDefinitionCreator'',我们需要指定方块状态与方块模型变种的关系,也就是要说清楚:什么方块状态使用什么模型变种。多个方块状态可以使用同一模型变种(例如含水和不含水的方块,模型都是一样的),同一方块状态也可以使用多个变种(例如泥土、沙子等方块的随机旋转)——但必须不重不漏:不一定每个方块状态属性都要考虑到,但每个可能的方块状态都要被包括,且不能重复,否则就会错误。在 1.21.5 中,有两种方法指定方块状态与模型变种关系: 
 +  * **方法一**:先指定一个模型变种,然后再根据方块状态修改其变种,如 x 旋转、y 旋转和 uvlock,各方块状态都使用同一模型 id,只是变种可能不同。 
 +  * **方法二**:直接为不同的方块状态分配模型变种,然后仍可继续根据方块状态修改变种。这种情况下,各方块状态可以使用不同的模型 id
  
-首先,添加方块自身并且注册它+我们的竖直台阶有两个方块状态属性:''facing'' 和 ''waterlogged''。''waterlogged'' 不影响其模型,所以我们只看 ''facing''。因为模型 id 不受朝向影响,所以这里我们使用方法一 
 +<code java TutorialModelGenerator.java> 
 +  @Override 
 +  public void generateBlockStateModels(BlockStateModelGenerator blockStateModelGenerator) { 
 +    // ... 
 +     
 +    blockStateModelGenerator.blockStateCollector.accept( 
 +        VariantsBlockModelDefinitionCreator.of(TutorialBlocks.POLISHED_ANDESITE_VERTICAL_SLAB, 
 +                BlockStateModelGenerator.createWeightedVariant(verticalSlabModelId)) 
 +            .apply(BlockStateModelGenerator.UV_LOCK) 
 +            .coordinate(BlockStateVariantMap.operations(VerticalSlabBlock.FACING) 
 +                .register(Direction.NORTH, BlockStateModelGenerator.NO_OP) 
 +                .register(Direction.EAST, BlockStateModelGenerator.ROTATE_Y_90) 
 +                .register(Direction.SOUTH, BlockStateModelGenerator.ROTATE_Y_180) 
 +                .register(Direction.WEST, BlockStateModelGenerator.ROTATE_Y_270))); 
 +  } 
 +</code>
  
-<code java> +这里的 ''apply'' 方法会将所有方块状态都应用同一操作例如全部添加 uvlock),而 ''coordinate'' 方法则会根据不同方块状态应用不同操作(例如设置不同的 y 旋转值,''BlockStateVariantMap.operations'' 只管变种,不管模型 id。
-// 在 Tutorial 类或者你自己模组初始化类中 +
-public static final Block MACHINE_BLOCK = new Block(FabricBlockSettings.copy(Blocks.BLAST_FURNACE));+
  
-@Override +> 可以使用方法二吗?当然可以,代码如下: 
-public void onInitialize() { +> <code java TutorialModelGenerator.java> 
-    Registry.register(Registries.BLOCKnew Identifier("tutorial""machine"), MACHINE_BLOCK); +    blockStateModelGenerator.blockStateCollector.accept( 
-}+        VariantsBlockModelDefinitionCreator.of( 
 +            TutorialBlocks.POLISHED_ANDESITE_VERTICAL_SLAB
 +            .with(BlockStateVariantMap.models(VerticalSlabBlock.FACING) 
 +                .register(Direction.NORTHBlockStateModelGenerator.createWeightedVariant(verticalSlabModelId)) 
 +                .register(Direction.EASTBlockStateModelGenerator.createWeightedVariant(verticalSlabModelId).apply(BlockStateModelGenerator.ROTATE_Y_90)) 
 +                .register(Direction.SOUTHBlockStateModelGenerator.createWeightedVariant(verticalSlabModelId).apply(BlockStateModelGenerator.ROTATE_Y_180)
 +                .register(Direction.WEST, BlockStateModelGenerator.createWeightedVariant(verticalSlabModelId).apply(BlockStateModelGenerator.ROTATE_X_270)) 
 +            ) 
 +            .apply(BlockStateModelGenerator.UV_LOCK) 
 +    );
 </code> </code>
 +> 我们发现,在方法二中,调用 ''VariantsBlockModelDefinitionCreator.of'' 时没有将 ''WeightedVariant'' 作为第二个参数,而是在后面每个方块状态提供一次,我们也注意到方法一中调用的是 ''BlockStateVariantMap.operations'',每次注册的也仅是对模型变种的操作,而在方法二中,调用的是 ''BlockStateVariantMap.models'',每次注册的都是个完整的模型变种。通过调用 ''with'' 分配模型变种之后,就可以继续像方法一那样使用 ''apply'' 或 ''coordinate'' 方法修改变种。
  
-我们成功地注册好了让我们向好的方向发展!+上面的方法一和方法二中,除了通过 ''register'' 方法逐个注册模型操作或模型变种之外,也可以使用 ''generate'' 通过 lambda 的方式指定模型操作或者模型变种,这个 lambda 是一个函数,接收对应的属性值作为参数,返回的是模型操作或模型变种。
  
-<code java> +如果方块状态有多个属性,多个属性都会影响到模型变种,那么可以直接在 ''BlockStateVariantMap.operations'' 或 ''BlockStateVariantMap.models'' 中提供多个属性,然后在后面调用 ''register'' 或 ''generate'' 时同时指定或使用多个值,也可以调用两次 ''coordinate'',让每个属性各自决定对方块状态的修改。
-private static class MyModelGenerator extends FabricModelProvider { +
- private MyModelGenerator(FabricDataGenerator generator) { +
- super(generator); +
-+
-  +
- @Override +
- public void generateBlockStateModels(BlockStateModelGenerator blockStateModelGenerator) { +
- // ..+
-                blockStateModelGenerator.blockStateCollector.accept(MultipartBlockStateSupplier.create(Tutorial.MACHINE_BLOCK) +
-                          .with(When.create().set(Properties.HORIZONTAL_FACING, Direction.NORTH), +
-                          BlockStateVariant.create().put(VariantSettings.X, VariantSettings.Rotation.X))); +
-+
-  +
- @Override +
- public void generateItemModels(ItemModelGenerator itemModelGenerator) { +
- // ... +
-+
-+
-</code>+
zh_cn/tutorial/datagen_model.1743519083.txt.gz · Last modified: 2025/04/01 14:51 by solidblock