Forge模组开发-方块类详解

引言:
首先要了解方块分为这样几种:
注册方块:例如Blocks.Wood,一般写在方块注册类,注册后生成一种类似【方块类型】的变量,用以对比、调用。
实例方块生成类:例如RotatedPillarBlock类,用于注册方块提供构造器引用的类,使用形式一般为调用DeferredRegister<Block>的register方法时,作为第二个形参将构造器以Supplier的形式传入被用。
实例方块:使用实例方块生成类写入并生成BlockState实例,该实例则是存在于世界的真正的方块,一切操作以该实例为主,而实例方块生成类更像一个工具类,被调用,这也就是为什么很多Block类的方法都会传入方块坐标这个形参。
所以,需要先解析实例方块生成类,包括:AbstractBlock类、Block类、IForgeBlock类以及其他小接口。方法会在讲完构造器后详解,构造器涉及的成员变量初始化会提及一些方法,但仍然会下下面详解。
讲的方法是在游戏主线程中,泛化调用的方法,你覆写以后,执行到你的方块后,有效果的方法,例如玩家掉落在你方块上没有伤害,就是覆写了你的实例方块生成类的onFallenUpon方法,显然你没有调用这个方法换入摔伤检测里,只是他将你的方块抽象为Block父类调用了作为子类的你的对应方法。
如果本文提及方块,一般指的是方块实例。
AbstractBlock类
构造器:public AbstractBlock(AbstractBlock.Properties properties)
其中,传入的Properties 决定了实例方块的基本属性,使用AbstractBlock.Properties.create()静态方法创建它,该方法形参需要传入一个Material实例。
Properties 类成员变量包括但不止:
Material material 方块基本类别,预设了一些属性,写入了AbstractBlock类的material成员变量,并据此设置了Properties 类的方块颜色(一种更宽泛感性的类别,并不是真的颜色),该参数影响BlockTags的贴附,后面有重复会讲;
boolean blocksMovement 是否有碰撞箱,写入了AbstractBlock类的boolean canCollide成员变量,被AbstractBlock类默认方法中的getCollisionShape方法使用,默认方法作用为如果不可碰撞,则返回0体积碰撞箱,否则返回标准16*16*16标准方块(完整方块)碰撞箱;
ResourceLocation lootTable 掉落表位置,写入了AbstractBlock类的ResourceLocation lootTable成员变量,被AbstractBlock类默认方法中的getDrops方法使用,根据资源路径的战利品表内容生成一个物品堆列表供调用;其次还有一个默认getter方法getLootTable,返回该资源目录详见我的世界wiki内loottable,与数据包制作类教程,默认为方块注册名对应的loottable。
float resistance 抗爆性,写入了AbstractBlock类的float blastResistance成员变量,顾名思义,方块对爆炸的抗性,在爆炸计算时会使用,自定义爆炸时,为了模组兼容性,建议以此判别。
float hardness构造器并没有,一般是与爆炸抗性同设的,但是可以单独设置,自行查看方法。
boolean ticksRandomly 该方块是否参与随机刻的执行,此处提醒,方块虽然有tick方法,意思是每个tick该方块均会执行一次tick方法的功能,但实际上该方法多数情况下并不奏效,不过随机刻还是奏效的,该参数写入了AbstractBlock类的boolean ticksRandomly成员变量,用于默认方法ticksRandomly方法调用判断是否能执行随机刻方法。
SoundType soundType,基本声音使用设置,包括放置、摧毁等,一般与Material 参数对应设置,例如设置SoundType.WOOD,写入了AbstractBlock类的SoundType soundType成员变量,被默认方法getSoundType调用,在一些预设播放声音的代码中调用作为形参使用,例如playsound方法。
float slipperiness 滑溜溜,写入了AbstractBlock类的float slipperiness成员变量,同爆炸抗性顾名思义,例如冰。
float speedFactor 速度影响(速率系数),写入了AbstractBlock类的float speedFactor成员变量,同爆炸抗性顾名思义,例如灵魂沙。
float jumpFactor 跳跃影响,写入了AbstractBlock类的float jumpFactor成员变量,同爆炸抗性顾名思义,例如粘液块。
boolean variableOpacity,参数不建议在这设置,表示贴图透明度。
方法:
updateDiagonalNeighbors(BlockState state, IWorld worldIn, BlockPos pos, int flags, int recursionLeft)
该方法默认是给红石线路使用的,用以让红石线路上下对角线路台阶式连接传递信号使用,其中state为本方块实例,worldin为实例所在世界,pos为实例所在位置,剩下两个参数红石相关,不予细讲,这是一个多数少用的方法,科技类模组可以参考。
boolean allowsMovement(BlockState state, IBlockReader worldIn, BlockPos pos, PathType type)
该方法在实体将要穿过方块时调用,判断是否能穿过方块,返回true表示可以,false表示不行,state是本方块实例,worldin是本方块世界,pos是本方块位置,type是该方块的可穿基本类型,例如水是该枚举类的WATER,一般方块为空,此处如果重写为true,即使有碰撞箱也可以被穿过。
BlockState updatePostPlacement(BlockState stateIn, Direction facing, BlockState facingState, IWorld worldIn, BlockPos currentPos, BlockPos facingPos)
该方法在该方块实例statein其空间一格十字内放置/摧毁方块后调用,返回的方块实例会被把该方块实例替换(更新掉),facing为状态改变的方块实例facingstate在该方块的哪个朝向,worldin为方块实例所在世界,两个pos一下分别是本方块和更新方块(facing)的位置,例如导线连接,需要详细重写该方法以调整连接状态与朝向,原版案例则是火把。
@OnlyIn(Dist.CLIENT)
public boolean isSideInvisible(BlockState state, BlockState adjacentBlockState, Direction side)
该方法被仅客户端修饰,是一个显示方法,表示是否能透过方块看到,与后面要讲的,处理作物贴图透明部分纯黑不同,该方法一般是用于液体透过观察与史莱姆块透过,而且倾向于看到另一个方块实例adjacentBlockState,side则是朝向,指看不看得到另一边的那一边不理解可以自行测试。
neighborChanged(BlockState state, World worldIn, BlockPos pos, Block blockIn, BlockPos fromPos, boolean isMoving)
在本方块位置改变时调用,除非内容写了改变自身的代码,否则一般是作用一些其他功能,例如TNT充能检测,海绵吸水检测,漏斗重新绑定容器,state为本方块实例,worldin为本方块世界,pos为当前位置(移动稳定后),frompos为移动前位置,blockin是本方块注册方块。
onBlockAdded(BlockState state, World worldIn, BlockPos pos, BlockState oldState, boolean isMoving)
在本方块被完全加入到世界后执行,用于设置方块的一些参数,例如漏斗绑定容器,火焰燃烧计时开始等,state为确定下的方块实例,worldin为该方块世界,pos为该方块位置,oldstate是原位置的方块,一般是空气方块实例,ismoving表示是不是移动来的,一般不用这个参数。
onReplaced(BlockState state, World worldIn, BlockPos pos, BlockState newState, boolean isMoving)
在方块实例被替换时调用,据上个方法可知,其实是不存在方块破坏这个概念的,而是替换为了空气方块实例,这个就是在本方块实例被销毁前调用的方法,一般是交代后事,比如挖爆箱子掉落箱子内物品,state为本方块实例,worldin为本方块世界,pos为本方块位置,newstate为要替换上的方块实例,ismoving表示是不是移动来的,一般不用这个参数。
ActionResultType onBlockActivated(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, Hand handIn, BlockRayTraceResult hit)
该方法用以决定玩家右键点击的动作,返回SUCCESS表示动作应当执行完毕,CONSUME表示动作仍要展示,一般是连续长按动作使用,返回PASS是白给,Fail未知功能,不建议未理解透彻使用。按道理,是允许在本方法添加一些右击方块的内容,mojang也是这么做的,但是这在物品类中有所不同,state为该方块实例,worldin为该方块世界,pos为该方块位置,player为传入的玩家实体,hit为记录了稍微详细交互信息的类,自行研究。
boolean eventReceived(BlockState state, World worldIn, BlockPos pos, int id, int param)
该方法如果返回true,那么服务端和客户端都会执行该事件,否则仅服务端执行,该方法在服务端事件执行完后才会执行判断,state为该方块实例,worldin为该方块世界,pos为该方块位置,id与param为事件的鉴别信息,具体清空不同,无法讲明。
BlockRenderType getRenderType(BlockState state)
该方法决定了方块的渲染形式,一般情况返回MODEL,箱子类则返回ENTITYBLOCK_ANIMATED,箱子实质上渲染的是实体模型而不是方块模型,因此有所不同,INVISIBLE则是不可见,state为该方块实例。
boolean isTransparent(BlockState state)
该方法决定了方块渲染形式,上述讲过,非完整方块模型空缺部分会填充为黑色,该方法返回true,则不会出错,正常显示透明,这个错误也可以在FMLClientSetupEvent中解决,RenderTypeLookup.setRenderLayer(注册方块, RenderType.getCutout());
其中rendertype如果是cutout和transeparent,等效于该方法返回true,功能被弃用,近身辨别使用,state为该方块实例。
boolean canProvidePower(BlockState state)
本方法决定了该方块实例是否能向外输出红石信号,true表示可以,state为该方块实例,forgedoc建议就方块实例来检测是否能输出,而不是该注册方块生成类的方法。
PushReaction getPushReaction(BlockState state)
该方法决定了该方块实例的推动形式,例如床返回DESTROY,那么会塞推动时就会破坏该床,参数来源是Material,翻译即可知道枚举返回值的用法,state为该方块实例。
FluidState getFluidState(BlockState state)
该方法目的是获取方块容纳的流体的流体实例,一般含水方块调用不为空,state为本方块实例。
boolean hasComparatorInputOverride(BlockState state)
该方法表决定该方块实例state可以被红石比较器比较输出,返回true则可以,例如箱子。
AbstractBlock.OffsetType getOffsetType()
该方法决定该方块实例是否执行随机偏移原位的渲染,有常用的不偏移NONE,有花用的xz也就是水平随机偏移,这也是为何花的位置不在中心,还有xyz偏移,返回值是什么,就怎么偏。
BlockState rotate(BlockState state, Rotation rot)
一般有朝向的方块必要重写该方法,为传入的state根据rot进行with方块属性。
BlockState mirror(BlockState state, Mirror mirrorIn)
一般有朝向的方块必要重写该方法,为传入的state根据mirrorIn进行with方块属性。
boolean isReplaceable(BlockState state, BlockItemUseContext useContext)
该方法决定方块实例state能否被替换,或者说是覆盖,例如空气就可以,他默认从Material里
获取参数用以判断,而useContext则是一种信息类你可以使用其内容来判断到底返回什么,里面包含内容足够使用了,而同类型的boolean isReplaceable(BlockState state, Fluid fluid)方法一般不建议覆写,原版涉及也很少。
List<ItemStack> getDrops(BlockState state, LootContext.Builder builder)
该方法在上述将了生成来源,决定了方块掉落,state是本方块,builder是生成器,它会根据你对生成器的设置来生成掉落物,例如生成器传入Random实例和不传入会有区别。
VoxelShape getRenderShape(BlockState state, IBlockReader worldIn, BlockPos pos)
该方法决定了渲染范围,栅栏、头颅、讲台有所使用,意义不明,默认调用总shape方法返回,state为该方块实例,worldin是一种阉割版世界,用于快速读取方块实例,pos是方块实例位置。
VoxelShape getCollisionShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context)
该方法决定了碰撞箱体积,默认调用总shape方法返回,形参同上,该方法一般配合方块实例属性食用,其中VoxelShape getCollisionShape(BlockState state, IBlockReader reader, BlockPos pos)应当是特例重写用方法,专供树叶、雪等。
VoxelShape getRaytraceShape(BlockState state, IBlockReader worldIn, BlockPos pos)
该方法决定光追渲染,一般为空,可以自定义,影子等光追效果依此生成,形参同上,其中VoxelShape getRayTraceShape(BlockState state, IBlockReader reader, BlockPos pos, ISelectionContext context) 同上为专供方法,提供雪、草、栅栏等,均需自行鉴别。
int getOpacity(BlockState state, IBlockReader worldIn, BlockPos pos)
该方法仅树叶覆写,恒定返回1,基本上是结合光照等级写的,材质透明度,形参同上。
INamedContainerProvider getContainer(BlockState state, World worldIn, BlockPos pos)
该方法举个例子会用在漏斗检测,建议有容器的,根据形参传入的位置和世界,获取到容器实例返回,用以让漏斗判别容器类型,形参差不多同上。
boolean isValidPosition(BlockState state, IWorldReader worldIn, BlockPos pos)
该方法会在放置前检查当前要放置的位置是否合理,返回false则无法放下,例如作物,注意位置是要放下的位置,不是右键的方块的位置,一般在右键的方块的一面的这格子。
@OnlyIn(Dist.CLIENT)
float getAmbientOcclusionLightValue(BlockState state, IBlockReader worldIn, BlockPos pos)
客户端方法,用于计算环境遮光。
int getComparatorInputOverride(BlockState blockState, World worldIn, BlockPos pos)
在允许比较器比较方法后,比较器调用的方法。
VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context)
总shape方法,返回的是一个体积大小,默认会被各种需求的体积getter调用,例如碰撞箱等,默认值为完整方块,context为信息实例。
randomTick(BlockState state, ServerWorld worldIn, BlockPos pos, Random random)
随机刻方法,当在构造器中允许时,该方法参与随机刻,如同作物一样的随机刻,详见我的世界wiki随机刻。
tick(BlockState state, ServerWorld worldIn, BlockPos pos, Random rand)
脆弱的tick方法,不建议在方块的tick方法中写过多内容让他执行,而且在方块实体中写。
float getPlayerRelativeBlockHardness(BlockState state, PlayerEntity player, IBlockReader worldIn, BlockPos pos)
一般该方法用于对构造器中设定的方块硬度的二次计算给定,AbstractBlock构造器并无硬度,所以该方法默认是从state获取的,一般用于计算工具加成等,被扣进了forge事件执行。
spawnAdditionalDrops(BlockState state, ServerWorld worldIn, BlockPos pos, ItemStack stack)
该方法决定了方块实例是否额外掉落一些东西,例如蠹虫就是在这生成的,state为该方块实例,worldin是世界,pos为方块位置,stack为采集工具。
onBlockClicked(BlockState state, World worldIn, BlockPos pos, PlayerEntity player)
与onBlockActivated方法不同,该方法是左键点击长按则是连续点击,state为该方块实例,worldin为该方块世界,pos为该方块位置,player是左键进行点击的玩家。
int getWeakPower(BlockState blockState, IBlockReader blockAccess, BlockPos pos, Direction side)
该方法一般是红石信号源覆写,例如红石火把,在红石火把点亮状态且输出面不为向上时返回15,否则返回0,形参side为脉冲输出面。
onEntityCollision(BlockState state, World worldIn, BlockPos pos, Entity entityIn)
该方法会在实体已经进入了方块碰撞形状内部而调用,而非即将进入,state为本方块实例,worldin为该方块世界,pos为该方块位置,entityin为该实体。
int getStrongPower(BlockState blockState, IBlockReader blockAccess, BlockPos pos, Direction side)
红石火把在打开状态会对其下方返回信号15,否则0;总之这俩方法是用来输出朝向最强信号的。
onProjectileCollision(World worldIn, BlockState state, BlockRayTraceResult hit, ProjectileEntity projectile)
该方法会在方块实例state被飞行道具射中插入后调用,例如标靶等,projectile为飞行道具例如箭,hit有一些信息可供使用。
======至此,抽象方块类可覆写方法结束======
由于IForgeBlock接口被Block类实现,所以一块将Block类,同时,Block类作为AbstractBlock类的子类,重复的方法不再细讲。由于Blocks继承了AbstractBlock类,如果在本节有方法名与其父类重复的,以覆写本节方法为主,否则并不会如期生效。
Block类
构造器:public Block(AbstractBlock.Properties properties)
其中大部分的方块基本属性已经通过super调用AbstractBlock类的构造器解决了,剩下是在注册blockstate额外属性容器(例如朝向、生长状态等),额外的基本属性:
Int harvestLevel 采集等级,写入了Blocks的Int harvestLevel成员变量中;
ToolType harvestTool 采集工具类型,写入了Blocks的ToolType harvestTooll成员变量中;
然后还初始化了默认方块实例创建器StateContainer<Block, BlockState> stateContainer,并用这个创建器创建了默认方块实例存入了该类(实例方块创建类)的成员变量BlockState defaultState中,等待getDefaultState方法调用。
方法:
Block getBlockFromItem(@Nullable Item itemIn)
静态方法,返回该物品类型对应的方块,覆写无用,游戏内原版调用的是Block类的静态方法而不是你覆写的方法,你仍可以使用此方法,或者在自己方块父类中进行一些覆写操作。
BlockState nudgeEntitiesWithNewState(BlockState oldState, BlockState newState, World worldIn, BlockPos pos)
此方法太过抽象,举例就是农田作为非完整方块,在被踩坏时变为完整方块泥土,如果不执行本方法,那么玩家在下一tick会自然下落,穿过泥土站在泥土下面方块上,而不是被轻推上去站在泥土上,用法特点同上。
boolean ticksRandomly(BlockState state)
该方法决定了是否允许执行随机刻,返回是则允许,state为该方块实例。
boolean propagatesSkylightDown(BlockState state, IBlockReader reader, BlockPos pos)
该方法决定了自然光线是否能从上而下传播,state为该方块实例,reader是一种类似阉割版世界的方块实例获取器,pos为方块位置。
@OnlyIn(Dist.CLIENT)
animateTick(BlockState stateIn, World worldIn, BlockPos pos, Random rand)
客户端方法,以tick在客户端执行一些方块动画显示,例如熔炉的火,其实是一张长图uv切割按tick展示形成的动画,rand是调用者传入的随机实例,频繁创建随机实例会有极高的消耗,所以一般要用的时候都是传递继承的。
onPlayerDestroy(IWorld worldIn, BlockPos pos, BlockState state)
该方法在完全破坏此方块实例后调用,注意是破坏后,state已经非有效形参了,建议使用pos和worldin来进行自己的功能。
dropXpOnBlockBreak(ServerWorld worldIn, BlockPos pos, int amount)
该方法在方块被采集后调用,且只有玩家采集后调用,worldin为该方块世界,pos为该方块位置,amount为要产生的数量,默认他会把数量分成几份,以生成经验实体的形式掉落。
float getExplosionResistance()
该方法在爆炸炸到该方块时调用,返回的就是在AbstractBlock构造器初始化的爆炸抗性参数。
onExplosionDestroy(World worldIn, BlockPos pos, Explosion explosionIn)
该方法在方块被炸毁后调用,explosionIn是爆炸参数,包括不限于是否破坏,爆炸范围,是否有火等详细爆炸信息。
onEntityWalk(World worldIn, BlockPos pos, Entity entityIn)
该方法在实体在方块上走时调用,不建议写过多内容,放点声音是一种他的用法,entityIn是该走路的实体,注意是否是活的生物;
BlockState getStateForPlacement(BlockItemUseContext context)
在方块放置前调用,调用次数未知,context内有详细放置信息供使用,一般是放之前预处理,例如设置朝向等。
harvestBlock(World worldIn, PlayerEntity player, BlockPos pos, BlockState state, @Nullable TileEntity te, ItemStack stack)
方块被破坏能掉落物品,就是执行的本方法,覆写后可以例如实现随机掉落,worldin为该方块实例世界,player为采集的玩家,pos为该方块实例位置,state为该方块实例(暂时存在),te为方块实体,如果这个方块实例是容器类方块,原版一般这个方块实体就是容器本身,可获取它里面的物品,stack是采集工具,原版用法是精准采集检测。
onBlockPlacedBy(World worldIn, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack)
在方块被完全放下后执行,可以进行后处理或者写一些类似战吼的功能,stack是方块物品堆,就是手持的方块物品。
boolean canSpawnInBlock()
该方法在生物生成前调用判断,如果返回为false,生物·不会生成在其内部,但不代表生物不会生成,生物会继续检测附近允许位置,生成在他附近。
@OnlyIn(Dist.CLIENT)
IFormattableTextComponent getTranslatedName()
客户端方法,返回本地化代码,配合语言文件,用以改变客户端该方块名称本地文字显示。
String getTranslationKey()
与上面类似,只不过这个返回字符串。
onFallenUpon(World worldIn, BlockPos pos, Entity entityIn, float fallDistance)
生物掉落在该方块实例上方时调用,例如农田,史莱姆块都有重写,如非必要,super调用,不然完全重写实体么得掉落伤害了。
onLanded(IBlockReader worldIn, Entity entityIn)
实体躺在该方块时调用,实际上实体不会躺,是该方块让实体躺了,例如玩家睡觉,玩家只是状态在睡觉,是这个方法让玩家做了一系列传送动作。
ItemStack getItem(IBlockReader worldIn, BlockPos pos, BlockState state)
该方法用以获取该方块实例对应的物品类型,实际上是该方块实例的Item asItem()方法的包装,没什么用应该,forge好像也不推荐。
public float getSlipperiness() {return this.slipperiness;}
public float getSpeedFactor() {return this.speedFactor;}
public float getJumpFactor() {return this.jumpFactor;}
三个getter方法返回了一些基本属性,想改就改,比如倍率返回。
onBlockHarvested(World worldIn, BlockPos pos, BlockState state, PlayerEntity player)
该方法在玩家将方块采集并破坏成空气前调用,其默认内容判断了state的BlockTags是否有猪灵看守着的标签,会发怒。
fillWithRain(World worldIn, BlockPos pos)
在下雨天且执行到本方块随机刻时调用,一般用在坩埚接水。
boolean canDropFromExplosion(Explosion explosionIn)
在爆炸并破坏本方块后执行的检查,返回true则可以如常规破坏一样掉落物品,explosionIn为爆炸的详细信息。
fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
写方块必然要覆写的方法,决定了方块额外属性是否能正常注册应用,一般格式是builder.add(属性键...)。
@OnlyIn(Dist.CLIENT)
Public void addInformation(ItemStack stack, @Nullable IBlockReader worldIn, List<ITextComponent> tooltip, ITooltipFlag flagIn)
客户端方法,用以为其加入描述,方块的话位置未知,如果是物品,那么会加在物品的lore上,请注意,不要写一些检测物品有这个方法加入的lore而执行功能的代码,因为这个方法在服务端是不存在的,而客户端向服务端同步数据是危险行为。
有关方块注册
第一种方法 新建一个方块注册类,其内写一静态成员:
public static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, "modid");
然后可以使用:
public static final RegistryObject<Block> 方块类型名称 = BLOCKS.register("类型键名", 实例方块生成类::new);
或者
public static final RegistryObject<Block> 方块类型名称 = BLOCKS.register("类型键名", () -> new Block(AbstractBlock.Properties.create(Material.ROCK).setRequiresTool().hardnessAndResistance(3.0F, 3.0F).notSolid().setLightLevel((light) -> 1)));
这样,将属性等内容在这写。
注册几个方块都可以,最后是注册这个注册类,在你的模组主类构造器中写
方块注册类.BLOCKS.register(FMLJavaModLoadingContext.get().getModEventBus());即可
第二种方法 也可以监听RegistryEvent.Register<Block>,在这里通过事件获取方块注册器,将你的方块注册进去。
============
到此,有关方块的覆写有效方法已经解析完毕,有时候我会把可调用方法解析也补充上去。
补充:版本为1.16.5请注意甄别。