自定义农作物方块
自定义农作物方块支持多阶段生长,可以实现类似小麦、胡萝卜的种植机制。中国版提供两种实现方式:纯 JSON 配置(netease:transform)和脚本控制(netease:random_tick + netease:block_entity)。你可以根据需求灵活选择。
前置条件
本功能不需要开启实验性玩法。请先阅读 自定义基础方块 了解方块注册流程和 JSON 组件基础。方式二(脚本控制)还需要了解 Python 脚本开发 的基本用法。
概述
自定义农作物的核心思路是:将每个生长阶段定义为独立的方块,通过条件触发方块之间的转换来模拟生长。一套完整的农作物通常包括:
- 种子物品 — 玩家手持种子右键耕地时种下
- 多个阶段方块 — 从幼苗到成熟,每个阶段是一个独立的自定义方块
- 收获掉落物 — 成熟阶段方块被破坏时掉落果实和种子
两种实现方式对比:
| 特性 | 方式一:纯 JSON | 方式二:脚本控制 |
|---|---|---|
| base_block | custom_crop_block | 不需要 |
| 生长驱动 | netease:transform 组件 | BlockRandomTickServerEvent 事件 |
| 转换条件 | 光照、随机刻次数、周围方块 | 脚本中自由判断任意条件 |
| 灵活度 | 适合简单线性生长 | 适合复杂逻辑(天气、季节、施肥等) |
| 脚本依赖 | 不需要 Python 脚本 | 需要 Python 脚本 |
方式一:纯 JSON 配置(netease:transform)
base_block 设置
每个生长阶段方块的 description 中,base_block 需要设为 custom_crop_block。这使引擎将方块识别为农作物类型,并启用 netease:transform 驱动的方块转换功能。
netease:transform 组件
| 键 | 类型 | 默认值 | 说明 |
|---|---|---|---|
conditions | dict | — | 必填,方块转换条件,所有子条件同时满足时触发转换 |
result | string | — | 必填,转换后的目标方块标识符 |
conditions 子条件:
| 条件键 | 类型 | 说明 |
|---|---|---|
brightness | dict | 光照条件。min/max 定义光照等级范围(0~15),方块位置光照在此范围内时满足 |
random_tick_count | dict | 随机刻次数。value 表示在第 N+1 次随机刻时触发转化,值必须 > 0 |
surrouding | dict | 周围方块条件。value 为方块标识符,radius 为检测半径,检测范围内存在该方块时满足 |
拼写注意
surrouding 是引擎内部的固定拼写(少了一个 n),请勿写成 surrounding,否则条件不会生效。
种子物品定义
种子使用 minecraft:seed 组件,定义在行为包的 netease_items_beh/ 目录中:
{
"format_version": "1.10",
"minecraft:item": {
"description": {
"identifier": "mymod:tomato_seeds",
"category": "Nature",
"register_to_create_menu": true
},
"components": {
"minecraft:seed": {
"crop_result": "mymod:tomato_stage0",
"plant_at": "minecraft:farmland"
}
}
}
}| 键 | 类型 | 说明 |
|---|---|---|
crop_result | string | 种子种下后生成的方块(第一阶段) |
plant_at | string | 种子只能种植在该方块上 |
种植后,所有生长阶段的下方方块也必须为 plant_at 指定的方块,否则农作物会被自动破坏(类似原版小麦在耕地被踩坏后掉落)。
完整 JSON 示例(方式一)
以下示例创建一套三阶段番茄农作物:种子 → 阶段0(幼苗)→ 阶段1(生长中)→ 阶段2(成熟)。
种子物品
文件路径:行为包/netease_items_beh/tomato_seeds.json
{
"format_version": "1.10",
"minecraft:item": {
"description": {
"identifier": "mymod:tomato_seeds",
"category": "Nature",
"register_to_create_menu": true
},
"components": {
"minecraft:seed": {
"crop_result": "mymod:tomato_stage0",
"plant_at": "minecraft:farmland"
}
}
}
}阶段 0 — 幼苗
文件路径:行为包/netease_blocks/tomato_stage0.json
{
"format_version": "1.10.0",
"minecraft:block": {
"description": {
"identifier": "mymod:tomato_stage0",
"base_block": "custom_crop_block",
"register_to_create_menu": false
},
"components": {
"minecraft:block_light_absorption": { "value": 0 },
"netease:render_layer": { "value": "alpha" },
"netease:aabb": {
"collision": {
"min": [0.0, 0.0, 0.0],
"max": [1.0, 0.3, 1.0]
},
"clip": {
"min": [0.0, 0.0, 0.0],
"max": [1.0, 0.3, 1.0]
}
},
"netease:transform": {
"conditions": {
"brightness": { "min": 9, "max": 15 },
"random_tick_count": { "value": 1 },
"surrouding": { "value": "minecraft:farmland", "radius": 1 }
},
"result": "mymod:tomato_stage1"
}
}
}
}阶段 1 — 生长中
文件路径:行为包/netease_blocks/tomato_stage1.json
{
"format_version": "1.10.0",
"minecraft:block": {
"description": {
"identifier": "mymod:tomato_stage1",
"base_block": "custom_crop_block",
"register_to_create_menu": false
},
"components": {
"minecraft:block_light_absorption": { "value": 0 },
"netease:render_layer": { "value": "alpha" },
"netease:aabb": {
"collision": {
"min": [0.0, 0.0, 0.0],
"max": [1.0, 0.6, 1.0]
},
"clip": {
"min": [0.0, 0.0, 0.0],
"max": [1.0, 0.6, 1.0]
}
},
"netease:transform": {
"conditions": {
"brightness": { "min": 9, "max": 15 },
"random_tick_count": { "value": 2 },
"surrouding": { "value": "minecraft:farmland", "radius": 1 }
},
"result": "mymod:tomato_stage2"
}
}
}
}阶段 2 — 成熟
文件路径:行为包/netease_blocks/tomato_stage2.json
{
"format_version": "1.10.0",
"minecraft:block": {
"description": {
"identifier": "mymod:tomato_stage2",
"base_block": "custom_crop_block",
"register_to_create_menu": false
},
"components": {
"minecraft:block_light_absorption": { "value": 0 },
"netease:render_layer": { "value": "alpha" },
"netease:aabb": {
"collision": {
"min": [0.0, 0.0, 0.0],
"max": [1.0, 1.0, 1.0]
},
"clip": {
"min": [0.0, 0.0, 0.0],
"max": [1.0, 1.0, 1.0]
}
},
"minecraft:loot": {
"value": "loot_tables/blocks/tomato_harvest.json"
}
}
}
}掉落表
文件路径:行为包/loot_tables/blocks/tomato_harvest.json
{
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "item",
"name": "mymod:tomato_seeds",
"weight": 1,
"functions": [
{ "function": "set_count", "count": { "min": 1, "max": 2 } }
]
}
]
}
]
}方式二:脚本控制(netease:random_tick + netease:block_entity)
方式二不需要设置 base_block 为 custom_crop_block,生长逻辑完全由 Python 脚本控制。这种方式灵活度更高,可以自定义任意生长条件(天气、时间、施肥、附近水源等)。
方块 JSON 定义
每个阶段方块需要配置 netease:random_tick 和 netease:block_entity 组件:
文件路径:行为包/netease_blocks/script_crop_stage0.json
{
"format_version": "1.10.0",
"minecraft:block": {
"description": {
"identifier": "mymod:script_crop_stage0",
"register_to_create_menu": false
},
"components": {
"minecraft:block_light_absorption": { "value": 0 },
"netease:render_layer": { "value": "alpha" },
"netease:random_tick": {
"enable": true,
"tick_to_script": true
},
"netease:block_entity": {
"tick": false,
"movable": true
},
"netease:neighborchanged_sendto_script": { "value": true },
"netease:redstone_property": { "value": "break_on_push" }
}
}
}Python 脚本示例
import server.extraServerApi as serverApi
ServerSystem = serverApi.GetServerSystemCls()
class CropServer(ServerSystem):
def __init__(self, namespace, systemName):
super(CropServer, self).__init__(namespace, systemName)
engineNs = serverApi.GetEngineNamespace()
engineSys = serverApi.GetEngineSystemName()
self.ListenForEvent(engineNs, engineSys,
'BlockRandomTickServerEvent', self, self.OnRandomTick)
def OnRandomTick(self, args):
blockName = args["blockName"]
pos = (args["posX"], args["posY"], args["posZ"])
dimensionId = args["dimensionId"]
if blockName != "mymod:script_crop_stage0":
return
# 获取方块实体数据,记录生长进度
comp = serverApi.GetEngineCompFactory().CreateBlockEntityData(self.levelId)
blockEntityData = comp.GetBlockEntityData(dimensionId, pos)
if not blockEntityData:
return
growth = blockEntityData.get("growth", 0)
growth += 1
blockEntityData["growth"] = growth
# 判断光照条件
blockInfoComp = serverApi.GetEngineCompFactory().CreateBlockInfo(self.levelId)
# 自定义条件:生长计数达到 3 时切换到下一阶段
if growth >= 3:
blockDict = {
'name': 'mymod:script_crop_stage1',
'aux': 0
}
blockInfoComp.SetBlockNew(pos, blockDict, dimensionId=dimensionId)方式二的优势
脚本控制方式可以实现更复杂的生长逻辑,例如:根据时间段决定是否生长、根据附近水源加速生长、实现施肥功能等。代价是需要编写和维护 Python 脚本。
农作物外观
农作物方块可以使用自定义方块模型配置外观。建议所有阶段共用同一个模型工程,仅更换贴图纹理。也可以使用交叉贴图方块的方式配置十字交叉贴图外观,类似原版小麦。
常见问题
农作物不生长
排查步骤(方式一):
- 确认
base_block设为custom_crop_block - 确认
netease:transform的conditions中各子条件拼写正确(特别是surrouding) - 确认方块位置的光照等级在
brightness范围内 - 确认方块下方存在
surrouding指定的方块 - 随机刻触发是概率性的,可能需要等待较长时间
排查步骤(方式二):
- 确认
netease:random_tick的enable和tick_to_script均为true - 确认 Python 脚本正确监听了
BlockRandomTickServerEvent - 确认脚本中的方块名匹配正确
种子无法种植
原因: minecraft:seed 组件的 plant_at 字段指定了种植基底方块。确认玩家右键的方块与 plant_at 一致(通常为 minecraft:farmland)。
农作物在生长过程中自动掉落
原因: 农作物下方的基底方块被破坏或改变。使用方式一时,所有阶段下方必须始终存在 plant_at 指定的方块。
