作物 1.20.1 Forge 长线教程计划
本篇教程的视频
本篇教程的源代码
GitHub地址:TutorialMod-1.20.1-Forge-Crop
查看CropBlock类
这一期教程我们来写一个单方块的作物,类似于原版的那些小麦,而下一期教程我们来写一个多方块的作物
所以在此之前,我们不妨先看看源代码是怎么写的(为了减少阅读量,这里截取的代码已简化,建议自行结合源代码来查看)
CropBlock类是所有种植在耕地上的作物的基类,小麦是直接使用这个方块类的,而马铃薯、甜菜根、胡萝卜等作物也都是继承这个类的
一些参数
1 | public static final int MAX_AGE = 7; |
MAX_AGE:作物最大生长阶段AGE:作物生长阶段的方块状态属性SHAPE_BY_AGE:作物各个生长阶段的碰撞箱(外轮廓线)
构造函数
这里取构造函数中的一条语句
1 | this.registerDefaultState(this.stateDefinition.any().setValue(this.getAgeProperty(), Integer.valueOf(0))); |
这个就是设置方块最初的方块状态,将AGE属性设置为0
这个方法我们将在之后的方块状态小系列中频繁使用
getShape 方法
1 | public VoxelShape getShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, CollisionContext pContext) { |
这是根据方块的AGE属性来返回对应的碰撞箱
mayPlaceOn 方法
1 | protected boolean mayPlaceOn(BlockState pState, BlockGetter pLevel, BlockPos pPos) { |
这个方法用于判断方块是否可以放置在指定位置上
那么耕地作物一般是种植在耕地上的,所以这里返回pState.is(Blocks.FARMLAND)
isRandomlyTicking & randomTick 方法
1 | public boolean isRandomlyTicking(BlockState pState) { |
isRandomlyTicking是判断方块是否需要随机刻,因为作物这种方块的生长依赖于随机刻,而当作物成熟后,就不需要随机刻了
randomTick是具体的随机刻逻辑,当然,这里的部分逻辑已经经过了Forge的特化处理(可对比Fabric那边的源代码)
注意这里的getGrowthSpeed方法,具体的代码我就不放了,这是一个用于获取作物生长速度的函数,其运算机制可参见作物机制 - 中文 Minecraft Wiki,这里我就不解释了
growCrops 方法
1 | public void growCrops(Level pLevel, BlockPos pPos, BlockState pState) { |
这是骨粉催熟作物时执行的逻辑
这里我们再说一点,我们可以观察到CropBlock实现了BonemealableBlock这个接口,而实现了这个接口的方块,就可以被骨粉催熟(或者执行一些其他的逻辑,比如骨粉右键草方块可以生成一堆花花草草)
而getBonemealAgeIncrease方法就是获取骨粉催熟作物时,作物的Age属性增加量
canSurvive 方法
1 | public boolean canSurvive(BlockState pState, LevelReader pLevel, BlockPos pPos) { |
这个方法是判断方块是否可以存活的,当光照等级不够时,方块将无法存活
entityInside 方法
1 | public void entityInside(BlockState pState, Level pLevel, BlockPos pPos, Entity pEntity) { |
这个方法是当实体进入作物方块的范围内时,会执行的逻辑
参照原版的一些逻辑,比如劫掠兽经过是会破坏作物,甜浆果丛会使实体减速或受伤
getBaseSeedId 方法
1 | protected ItemLike getBaseSeedId() { |
这个方法用于获取作物的种子,那么CropBlock默认返回是就是小麦种子
getCloneItemStack 方法
1 | public ItemStack getCloneItemStack(BlockGetter pLevel, BlockPos pPos, BlockState pState) { |
这是当我们游戏的十字准星对准方块并点击鼠标中键时,会执行的逻辑
那么对于作物方块来说,就是返回作物的种子
isValidBonemealTarget & isBonemealSuccess & performBonemeal 方法
1 | public boolean isValidBonemealTarget(LevelReader pLevel, BlockPos pPos, BlockState pState, boolean pIsClient) { |
这三者是实现BonemealableBlock接口时需要重写的方法
分别对应的是是否可以进行骨粉催熟,是否成功催熟,以及执行骨粉催熟的逻辑
createBlockStateDefinition 方法
1 | protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> pBuilder) { |
这个方法是为方块添加方块状态属性,这个方法我们在未来的方块状态小系列中,也会频繁使用
作物方块类
那么到这里为止,我们已基本了解了作物方块有哪些逻辑,接下来我们救来实现自己的作物方块
这里我们新建一个StrawberryCrop类,并让它继承CropBlock类
1 | public class StrawberryCrop extends CropBlock { |
同时我们重写一些参数,我们将最大的生长阶段定义为5,并定义一个属性为AGE,并添加到方块状态中
这里的种子我们还没注册,等会去注册
最后我们还重写了mayPlaceOn方法,让它不仅可以种在耕地上,还可以直接种在草方块、泥土方块上
注册方块
在注册作物的种子之前,我们先注册方块
1 | public static final RegistryObject<StrawberryCrop> STRAWBERRY_CROP = |
这里我们注册了一个名为strawberry_crop的作物方块,并继承了原版小麦方块的属性
注意我们这里直接使用BLOCKS.register方法注册,没有使用我们之前封装的一起注册方块和方块物品的方法,因为方块物品需要单独注册
注册种子物品
1 | public static final RegistryObject<Item> STRAWBERRY_SEEDS = ITEMS.register("strawberry_seeds", |
这里的种子物品我们使用ItemNameBlockItem类,并传入作物方块和物品属性
不要忘了将其添加到物品栏
1 | pOutput.accept(ModItems.STRAWBERRY_SEEDS.get()); |
数据文件
同样我们还是使用数据生成来写数据文件
战利品列表
1 | LootItemCondition.Builder builder1 = LootItemBlockStatePropertyCondition.hasBlockStateProperties(ModBlocks.STRAWBERRY_CROP.get()) |
作物的战利品列表有些特殊,当作物没有成熟就被破坏了,只会掉落种子;只有完全成熟的作物才会掉落种子和果实
所以这里我们需要使用LootItemBlockStatePropertyCondition类来判断作物是否已经完全成熟
而后使用createCropDrops方法来生成作物的战利品
方块模型文件
在写方块模型的生成语句之前,我们再创建两个方法
1 | public void crop(CropBlock block, String name, IntegerProperty property) { |
这两个方法本质就是作物方块模型文件的生成语句,因为Forge这边没有提供给我们已经封装好的方法,所以得自己写
最后也附上了renderType的设置,因为作物方块一般是有透明通道的,总不是一个纯粹的面片
然后我们就可以调用这个方法了
1 | crop(ModBlocks.STRAWBERRY_CROP.get(), "strawberry_crop_stage", StrawberryCrop.AGE); |
这里传入的参数为作物方块,作物名称,作物的生长阶段属性
语言文件
1 | add(ModItems.STRAWBERRY_SEEDS.get(), "Strawberry Seeds"); |
物品模型文件
1 | basicItem(ModItems.STRAWBERRY_SEEDS.get()); |
种子的模型还是一般的物品
贴图文件
作物的方块贴图也有一些特殊,你需要准备作物各个生长阶段的贴图
比如我们这里写的作物,你需要准备6张贴图,strawberry_crop_stage_0.png为未成熟作物,strawberry_crop_stage_1.png为第一阶段,以此类推
物品的种子贴图也记得放到对应的文件夹中
之后我们就可以启动游戏进行测试了









