MCBBS Wiki欢迎您共同参与编辑!在参与编辑之前请先阅读Wiki方针

如果在编辑的过程中遇到了什么问题,可以去讨论板提问。

为了您能够无阻碍地参与编辑 未验证/绑定过邮箱的用户,请尽快绑定/验证

MCBBS Wiki GitHub群组已上线!

您可以在回声洞中发表吐槽!

服务器状态监控。点击进入

本站由MCBBS用户自行搭建,与MCBBS及东银河系漫游指南(北京)科技有限公司没有从属关系。点此了解 MCBBS Wiki 不是什么>>

用户:MashKJo/1.12.2模组开发教程/9.第一个方块:修订间差异

来自MCBBS Wiki
跳到导航 跳到搜索
(创建页面,内容为“在开始制作我们的第一个方块前,笔者先区分一下两个概念:方块(Block)和方块的物品形式(ItemBlock)——平时,我们说“方块”,是同时涵盖了这两个概念的:广义上的方块,被放置在世界中时,即为Block;而在物品栏中时,它则是一种特殊的物品,也即ItemBlock。 好,区分了这两个概念后,我们就可以开始了。 == 添加一个新方块的流程 == 所有方…”)
 
→‎添加一个新方块的流程:​ // Edit via Wikiplus
 
(未显示同一用户的5个中间版本)
第13行: 第13行:


想必读者肯定猜到了,Block类也有很多getter和setter可以用。与Item的类似,Block类的getter牵扯到BlockState这一概念,因此后面再讲,现在只讲setter:
想必读者肯定猜到了,Block类也有很多getter和setter可以用。与Item的类似,Block类的getter牵扯到BlockState这一概念,因此后面再讲,现在只讲setter:
* setLightOpacity:传入一个int量,用于设定方块的透光度,传入的数值越大透光度越低。完全不透光的方块,该值为255;完全透光则为0;部分透光则在两者之间,如树叶的lightOpacity为1。该值默认由一个三目运算决定,具体为:如果该方块为渲染意义上的完整方块,lightOpacity为255,否则为0
* setLightLevel:传入一个float量,设置方块的光照等级,注意实际的光照值要把这个传入的参数乘以15。具体设定为多少,读者可以去查阅原版中会发光的方块的源代码,这块是怎么设定的
* setResistance:传入一个float量,用于设置方块的爆炸抗性,实际抗性值为传入参数乘以3的结果
* setHardness:传入一个float量,用于设置方块的挖掘硬度,注意这个setter也有可能影响爆炸抗性。特别地,还有一个该setter的特化版:setBlockUnbreakable,会把hardness设定为-1.0F,即让该方块变得像基岩一样不可破坏
* setUnlocalizedName、setCreativeTab:作用类似于Item类中的同名方法。注意:这里如果给setUnlocalizedName传入<code>"examplename"</code>,那么最终的本地化键名不是<code>item.examplename.name</code>,也不是<code>block.examplename.name</code>,而是<code>tile.examplename.name</code>
* setHarvestLevel:形参列表为<code>(String toolClass, int level)</code>,用于设定该方块的挖掘等级和应被什么种类的工具挖掘。实际上这个方法是Forge塞进去的——没错,Minecraft原版甚至没有挖掘等级这一概念,原版中某些方块需要特定等级的工具才能挖掘的例子纯属硬编码的结果。toolClass这个String代表工具类型,如<code>"pickaxe"</code>、<code>"axe"</code>等等。而level的规则是:木制/金制工具的等级为0,石制为1,铁制为2,钻石工具则为3,-1则代表空手挖掘。另外注意:该方法的返回值类型为void,即不符合链式调用规则

例如,笔者新增了一个方块:红宝石块:

<code>src/main/java/net/tutorial_mod/block/BlockRuby.java:</code>

package net.tutorial_mod.block;
import net.minecraft.block.Block;
import net.minecraft.block.material.MapColor;
import net.minecraft.block.material.Material;
import net.tutorial_mod.TutorialMod;
public class BlockRuby extends Block{
public BlockRuby(){
super(Material.ROCK, MapColor.RED);
this.setRegistryName(TutorialMod.MODID, "ruby_block");
this.setUnlocalizedName("ruby_block");
this.setCreativeTab(TutorialMod.TutorialModTab);
this.setHarvestLevel("pickaxe", 2);
}
}

最后,先实例化,监听RegistryEvent.Register<Block>事件,把我们的新方块注册进游戏。


== 添加方块对应的物品形式 ==
== 添加方块对应的物品形式 ==
进入游戏后,读者可能会感到奇怪:创造模式物品栏里,并没有我们的新方块,用/give指令也得不到,只有用/setblock指令才能把该方块放置在世界中。那是因为我们还没有注册该方块的物品形式。

方块的物品形式首先是物品,实际上是ItemBlock——这是Item的一个子类,构造器接受一个Block参数的传入,这种设计很易于理解。我们直接实例化ItemBlock类,再通过监听RegistryEvent.Register<Item>事件即可把它注册进游戏。注意:我们不需要给方块的物品形式的实例调用一系列setter方法,但必须调用setRegistryName——且它的registryName和对应方块的registryName必须相同。

可能会有读者对前面Block类的setUnlocalizedName和setCreativeTab这两个setter感到奇怪——因为其实这两个方法只对物品有意义——毕竟放在世界中的方块需要什么对应的创造模式物品栏?是这样,这两个方法是为ItemBlock服务的。我们用我们的Block对象构造出一个ItemBlock后,该ItemBlock对象中的对应的创造模式物品栏、unlocalizedName这些信息会自动等于对应的Block对象中的对应信息的,这也是为什么我们不需要给ItemBlock调用一大堆的setter。

ItemBlock类中覆写了Item类中的一些方法,导致了ItemBlock对象的通用逻辑为:玩家在世界中对着非空气方块右键时,如果坐标合法(如Y坐标在0~255这个范围内),就会在该处放置一个对应的方块,同时手上的ItemStack的数量这一字段减一。除非你真的有特殊需求,否则你只需要直接实例化ItemBlock类得到方块的物品形式就好了,不用去先继承ItemBlock。

好了,现在再运行游戏,应该可以在创造模式物品栏中看到我们的方块的物品形式了。


== 为方块和ItemBlock提供模型和材质文件 ==
== 为方块和ItemBlock提供模型和材质文件 ==
和物品不同,方块的模型文件,并不是通过代码来指定的,而是通过<code>assets/[modid]/blockstates</code>路径下的与方块注册名同名的.json文件来指定的,例如,对于我们给前文添加的红宝石块:

<code>src/main/resources/assets/tutorial_mod/blockstates/ruby_block.json:</code>

{
"variants":{
"normal":{
"model":"tutorial_mod:ruby_block"
}
}
}

这个文件的各部分内容都是什么意思,会在后面讲到,不过有一处是显而易见的:该文件把我们的方块的模型文件映射到了<code>assets/[modid]/models/block/ruby_block.json</code>这个文件。那么,再来看看模型文件的内容:

<code>src/main/resources/assets/tutorial_mod/models/block/ruby_block.json:</code>

{
"parent": "block/cube_all",
"textures":{
"all": "tutorial_mod:blocks/ruby_block"
}
}

由于我们要添加的红宝石块是类似于原版的铁块、金块等方块的——六个面的材质都一样,所以我们直接指定该模型文件的父模型为<code>assets/minecraft/models/block/cube_all.json</code>,然后我们就可以指定所有(all)面的材质文件了。我们可以看出,"parent"用于指定模型的父模型文件。之前讲过的物品模型文件,其父模型文件实际上是<code>assets/minecraft/models/item/generated.json</code>,这个模型文件主要设定了物品材质的默认几何变换。

读者翻阅原版的模型文件时,会发现这些模型的最终父模型是"builtin/generated",这实际上不指代一个具体的模型文件,它的意思是“交给相关的渲染代码处理”。

如果我想新增一个六个面材质不是全相同的方块呢?你可以继承原版其他的模型文件,如:<code>block/cube_column</code>、<code>block/cube_directional</code>等等。或者你就可以直接继承<code>block/cube</code>,然后你手动指定方块的六个面的材质和粒子效果材质的文件路径,也可以,但是一般没必要。

把绘制好的材质文件放到对应路径中,进入游戏,就可以看到世界中的方块的材质已经被正确渲染了。

但是,方块的物品形式仍然是紫黑色的,我们还需要给它声明模型。首先用<code>ModelLoader#setCustomModelResourceLocation</code>将它和一个ModelResourceLocation绑定,再:

<code>src/main/resources/assets/tutorial_mod/models/item/ruby_block.json:</code>

{
"parent": "tutorial_mod:block/ruby_block"
}

没错,我们把它的父模型指定为之前的方块模型即可,MC的相关代码会自动把该物品的样子渲染成方块斜着放的样子的。

现在进入游戏测试,应该真的没什么问题了。

2024年12月16日 (一) 18:36的最新版本

在开始制作我们的第一个方块前,笔者先区分一下两个概念:方块(Block)和方块的物品形式(ItemBlock)——平时,我们说“方块”,是同时涵盖了这两个概念的:广义上的方块,被放置在世界中时,即为Block;而在物品栏中时,它则是一种特殊的物品,也即ItemBlock。

好,区分了这两个概念后,我们就可以开始了。

添加一个新方块的流程

所有方块都是net.minecraft.block.Block类的实例,该类和Item类不同,它没有零参构造器,我们在构造Block实例时,有2个构造器可供选择:(Material materialIn)(Material materialIn, MapColor mapColorIn)

MapColor用于指示地图上显示该方块所用的颜色,这个类的构造方法是private的,且它要求构造MapColor对象时指定该对象对应的序数,该类有许多静态MapColor字段供你使用,Mojang在这个类里把硬编码体现得淋漓尽致(捂脸)。

Material用于描述方块的“质地”,这个类纯粹用于封装许多和方块有关的信息,它能影响方块的许多属性,这个类的构造方法倒是public的,其中要求传入一个MapColor。不过我们也没必要新建Material,直接复用原版的即可。

上述的第一个构造方法,实际效果为this(materialIn, materialIn.getMaterialMapColor());。读者根据自己的需求自行选择合适的构造器,填入合适的参数就好。

想必读者肯定猜到了,Block类也有很多getter和setter可以用。与Item的类似,Block类的getter牵扯到BlockState这一概念,因此后面再讲,现在只讲setter:

  • setLightOpacity:传入一个int量,用于设定方块的透光度,传入的数值越大透光度越低。完全不透光的方块,该值为255;完全透光则为0;部分透光则在两者之间,如树叶的lightOpacity为1。该值默认由一个三目运算决定,具体为:如果该方块为渲染意义上的完整方块,lightOpacity为255,否则为0
  • setLightLevel:传入一个float量,设置方块的光照等级,注意实际的光照值要把这个传入的参数乘以15。具体设定为多少,读者可以去查阅原版中会发光的方块的源代码,这块是怎么设定的
  • setResistance:传入一个float量,用于设置方块的爆炸抗性,实际抗性值为传入参数乘以3的结果
  • setHardness:传入一个float量,用于设置方块的挖掘硬度,注意这个setter也有可能影响爆炸抗性。特别地,还有一个该setter的特化版:setBlockUnbreakable,会把hardness设定为-1.0F,即让该方块变得像基岩一样不可破坏
  • setUnlocalizedName、setCreativeTab:作用类似于Item类中的同名方法。注意:这里如果给setUnlocalizedName传入"examplename",那么最终的本地化键名不是item.examplename.name,也不是block.examplename.name,而是tile.examplename.name
  • setHarvestLevel:形参列表为(String toolClass, int level),用于设定该方块的挖掘等级和应被什么种类的工具挖掘。实际上这个方法是Forge塞进去的——没错,Minecraft原版甚至没有挖掘等级这一概念,原版中某些方块需要特定等级的工具才能挖掘的例子纯属硬编码的结果。toolClass这个String代表工具类型,如"pickaxe""axe"等等。而level的规则是:木制/金制工具的等级为0,石制为1,铁制为2,钻石工具则为3,-1则代表空手挖掘。另外注意:该方法的返回值类型为void,即不符合链式调用规则

例如,笔者新增了一个方块:红宝石块:

src/main/java/net/tutorial_mod/block/BlockRuby.java:

package net.tutorial_mod.block;

import net.minecraft.block.Block;
import net.minecraft.block.material.MapColor;
import net.minecraft.block.material.Material;
import net.tutorial_mod.TutorialMod;

public class BlockRuby extends Block{
   public BlockRuby(){
       super(Material.ROCK, MapColor.RED);
       this.setRegistryName(TutorialMod.MODID, "ruby_block");
       this.setUnlocalizedName("ruby_block");
       this.setCreativeTab(TutorialMod.TutorialModTab);
       this.setHarvestLevel("pickaxe", 2);
   }
}

最后,先实例化,监听RegistryEvent.Register<Block>事件,把我们的新方块注册进游戏。

添加方块对应的物品形式

进入游戏后,读者可能会感到奇怪:创造模式物品栏里,并没有我们的新方块,用/give指令也得不到,只有用/setblock指令才能把该方块放置在世界中。那是因为我们还没有注册该方块的物品形式。

方块的物品形式首先是物品,实际上是ItemBlock——这是Item的一个子类,构造器接受一个Block参数的传入,这种设计很易于理解。我们直接实例化ItemBlock类,再通过监听RegistryEvent.Register<Item>事件即可把它注册进游戏。注意:我们不需要给方块的物品形式的实例调用一系列setter方法,但必须调用setRegistryName——且它的registryName和对应方块的registryName必须相同。

可能会有读者对前面Block类的setUnlocalizedName和setCreativeTab这两个setter感到奇怪——因为其实这两个方法只对物品有意义——毕竟放在世界中的方块需要什么对应的创造模式物品栏?是这样,这两个方法是为ItemBlock服务的。我们用我们的Block对象构造出一个ItemBlock后,该ItemBlock对象中的对应的创造模式物品栏、unlocalizedName这些信息会自动等于对应的Block对象中的对应信息的,这也是为什么我们不需要给ItemBlock调用一大堆的setter。

ItemBlock类中覆写了Item类中的一些方法,导致了ItemBlock对象的通用逻辑为:玩家在世界中对着非空气方块右键时,如果坐标合法(如Y坐标在0~255这个范围内),就会在该处放置一个对应的方块,同时手上的ItemStack的数量这一字段减一。除非你真的有特殊需求,否则你只需要直接实例化ItemBlock类得到方块的物品形式就好了,不用去先继承ItemBlock。

好了,现在再运行游戏,应该可以在创造模式物品栏中看到我们的方块的物品形式了。

为方块和ItemBlock提供模型和材质文件

和物品不同,方块的模型文件,并不是通过代码来指定的,而是通过assets/[modid]/blockstates路径下的与方块注册名同名的.json文件来指定的,例如,对于我们给前文添加的红宝石块:

src/main/resources/assets/tutorial_mod/blockstates/ruby_block.json:

{
   "variants":{
       "normal":{
           "model":"tutorial_mod:ruby_block"
       }
   }
}

这个文件的各部分内容都是什么意思,会在后面讲到,不过有一处是显而易见的:该文件把我们的方块的模型文件映射到了assets/[modid]/models/block/ruby_block.json这个文件。那么,再来看看模型文件的内容:

src/main/resources/assets/tutorial_mod/models/block/ruby_block.json:

{
   "parent": "block/cube_all",
   "textures":{
       "all": "tutorial_mod:blocks/ruby_block"
   }
}

由于我们要添加的红宝石块是类似于原版的铁块、金块等方块的——六个面的材质都一样,所以我们直接指定该模型文件的父模型为assets/minecraft/models/block/cube_all.json,然后我们就可以指定所有(all)面的材质文件了。我们可以看出,"parent"用于指定模型的父模型文件。之前讲过的物品模型文件,其父模型文件实际上是assets/minecraft/models/item/generated.json,这个模型文件主要设定了物品材质的默认几何变换。

读者翻阅原版的模型文件时,会发现这些模型的最终父模型是"builtin/generated",这实际上不指代一个具体的模型文件,它的意思是“交给相关的渲染代码处理”。

如果我想新增一个六个面材质不是全相同的方块呢?你可以继承原版其他的模型文件,如:block/cube_columnblock/cube_directional等等。或者你就可以直接继承block/cube,然后你手动指定方块的六个面的材质和粒子效果材质的文件路径,也可以,但是一般没必要。

把绘制好的材质文件放到对应路径中,进入游戏,就可以看到世界中的方块的材质已经被正确渲染了。

但是,方块的物品形式仍然是紫黑色的,我们还需要给它声明模型。首先用ModelLoader#setCustomModelResourceLocation将它和一个ModelResourceLocation绑定,再:

src/main/resources/assets/tutorial_mod/models/item/ruby_block.json:

{
   "parent": "tutorial_mod:block/ruby_block"
}

没错,我们把它的父模型指定为之前的方块模型即可,MC的相关代码会自动把该物品的样子渲染成方块斜着放的样子的。

现在进入游戏测试,应该真的没什么问题了。