光源方块 26.1 Fabric
本篇教程的视频
本篇教程的源代码
GitHub地址:
本篇教程目标
- 学会用
lightLevel让方块发光 - 学会用
LIT属性做「开关灯」 - 搞清楚哪些属性要写进方块状态文件、哪些不用
- 复习一遍右键交互(
useWithoutItem)
介绍
前面几期的方块,改的都是「形」,朝向、连接,肉眼能看出变化
这一期改的是「光」
发光方块分两种
一种是一直亮着的,火把、荧石、南瓜灯,放下去就亮,没有开关
另一种是能开关的,红石灯是最典型的例子:有信号亮,没信号灭
两种做法其实是一套,后者只是多了一个属性
这期我们做一个能右键开关的壁灯
查看源代码
lightLevel
先看火把,Blocks 里的注册
1 | public static final Block TORCH = register( |
火把能发光就是实现了lightLevel这个方法,参数是一个 ToIntFunction<BlockState>
火把给它一个常数14,无论什么状态,光照等级都是 14
光照等级的范围是0到 15,15 是荧石、海晶灯那一档,火把是14
能开关的
我们可以再来看看红石灯
1 | public static final Block REDSTONE_LAMP = register( |
注意这里传的不是常数,而是 litBlockEmission(15)
我们再来看看这个方法
1 | public static ToIntFunction<BlockState> litBlockEmission(final int lightEmission) { |
显然,LIT为true时就返回 15,否则返回 0
所以「开关灯」的本质就是:方块状态里有个布尔值,光照等级跟着它走
LIT 属性
1 | public static final BooleanProperty LIT = BlockStateProperties.LIT; |
BooleanProperty 就是我们上一篇用的 EnumProperty 的特例(因为它也是继承EnumProperty的),只有 true 和 false 两个值
用到它的原版方块不少:熔炉、烟熏炉、高炉、红石灯、蜡烛,都是这一个属性
动手写
LampBlock
1 | public class LampBlock extends Block { |
Properties是可以链式调用的,我们在构造函数里补一条 lightLevel,效果和写在注册语句里完全一样,你也可以在注册时写这个方法
后面的方块状态初始值,我们默认给 true,也就是放下来就是亮的
属性声明
1 |
|
老规矩,加了属性不声明,启动就崩
碰撞箱
这个壁灯模型只有 4 像素高,挂在墙上,碰撞箱也得跟着缩
1 | public static final VoxelShape SHAPE = Block.box(0, 6, 12, 16, 10, 16); |
当然这里我们就省略了FACING,大家可以按照需要补上
实现右键开关
1 |
|
逻辑很简单,每右键一次就是把当前LIT值取反,然后 setBlock 写回去
state.setValue(LIT, !state.getValue(LIT)) 是取反的写法,比写 if/else 两个分支短
setBlock 的第三个参数是更新标记
光这一块不用我们管,方块状态一变,游戏会自己重算光照
这里有两点要强调
第一,**判断必须是 !level.isClientSide()**,只在服务端改
第二,返回值给 InteractionResult.SUCCESS,手会挥一下,手感正常
返回 PASS 的话,空手右键等于什么都没发生,有些情况下还会把交互让给别的方块
注册
1 | public static final Block LAMP_BLOCK = register("lamp_block", LampBlock::new, |
noOcclusion 照旧,因为它不是满方块
光照那个参数我们在方块类里写了,这里就不用重复
顺手加物品栏
1 | output.accept(ModBlocks.LAMP_BLOCK); |
语言文件
1 | translationBuilder.add(ModBlocks.LAMP_BLOCK, "Lamp"); |
战利品表
1 | dropSelf(ModBlocks.LAMP_BLOCK); |
数据生成
方块状态
这里有个容易搞混的地方:LIT 有这么个属性,方块状态文件里要不要给两个变体?
我们继续用前几期那个生成静态模型的方法
1 | generateCustomBlock(blockModelGenerators, ModBlocks.LAMP_BLOCK); |
生成出来是这样
1 | { |
那个空的键 "" 表示「任何状态都用这个模型」
为什么可以这么写?因为开灯和关灯,模型是同一个
先想清楚一件事:写进方块状态文件的属性,是会让方块「换模型 / 转角度」的属性
比如上一篇的 FACING,不同的值模型转的角度不一样,所以必须列出来
type 也是,四个值对应四份模型
而 LIT 只影响光照等级,模型一动不动,就不用在方块状态里提它
但是红石灯就必须列两个
1 | { |
因为它亮的时候是另一张贴图
如果你也想让灯「亮的时候换个模型」,比如灯罩换成发光的贴图,那就得自己拼MultiVariantGenerator,把LIT的两个值分别select到两个模型上,与我们之前写沙发的方法类似
模型和贴图
模型文件放
1 | assets/tutorial/models/block/lamp_block.json |
Blockbench 导出的那份,记得检查命名空间是不是自己的 modid
测试
跑完数据生成,进游戏进行测试吧
常见问题(大肥鱼总结版)
- 方块不发光:
lightLevel没加,或者加在了BlockBehaviour.Properties.of()之前被覆盖掉了 - 放下来不亮,要右键一下才亮:
registerDefaultState里没设LIT默认值,或者设成了false - 右键没反应:
createBlockStateDefinition里漏了LIT,或者判断写成了level.isClientSide() - 客户端亮了服务端没变,走远回来又灭了:
setBlock写在客户端了,判断方向反了 - 亮度和预期不一样:
lightLevel返回的是0到15,写超过15没意义 - 灯周围有光但方块本身是黑的:贴图问题,和光照无关,检查贴图路径
- 模型显示成紫黑方块:贴图命名空间不对,或者
registerSimpleItemModel漏了 - 启动崩溃,报属性没注册:
createBlockStateDefinition漏了LIT - 想让亮灯换个模型但没生效:方块状态文件还是空的
"",得改成按LIT分两个变体 - 右键灯的时候手上的方块被放下去了:
useItemOn没重写,手上拿东西时会先尝试放置







