音乐唱片 26.1 Fabric
本篇教程的视频
本篇教程的源代码
GitHub地址:
本篇教程目标
- 理清唱片的三层结构:声音事件、歌曲、物品
- 学会注册能被歌曲引用的声音事件
- 学会用数据生成写
jukebox_song - 学会注册唱片物品,以及它的模型和贴图
介绍
上一篇我们讲了声音事件,这一篇讲音乐唱片
从1.21开始,唱片就不是「一个物品加一个音效」这么简单了,它分成了三层
第一层是声音事件,也就是SoundEvent,它决定放什么声音
第二层是歌曲,也就是JukeboxSong,它记录这首歌多长、比较器输出多少红石信号、播放的时候显示什么名字
第三层是物品,也就是唱片本身,它通过jukeboxPlayable指向第二层
三层里,只有第一层和第三层是我们熟悉的代码注册,第二层在数据包里
所以在 1.21.1 里,我们注册完物品之后,还得自己手写一个data/<命名空间>/jukebox_song/xxx.json,不然唱片能放进唱片机、也有字幕,但就是没声音
到了 26.1,这一层也能用数据生成写了,不用手写
和 1.21.1 的差别,主要有这几处
第一,注册歌曲用的参数从Registerable<JukeboxSong>变成了BootstrapContext<JukeboxSong>,和上一期职业、交易那套一样
第二,RegistryEntry.Reference<SoundEvent>现在叫Holder.Reference<SoundEvent>,换个名字而已
第三,JukeboxSong里存的声音也从RegistryEntry变成了Holder
第四,jukebox_song的json我们不用手写了,交给FabricDynamicRegistryProvider
第五,物品模型的写法变了,TEMPLATE_MUSIC_DISC现在是ModelTemplates.MUSIC_DISC,而且模型文件拆成了两段
查看源代码
JukeboxSong
先看歌曲这个类,它是个record
1 | public record JukeboxSong( |
四个字段,正好对应唱片机上需要的信息
soundEvent是放哪个声音事件,注意类型是Holder<SoundEvent>,不是SoundEvent本身
description是播放时飘出来的那行描述文本,也就是「正在播放:xxx」
lengthInSeconds是歌曲时长,单位是秒
comparatorOutput是比较器输出的红石信号强度,0到15
时长这里有个细节,lengthInTicks方法会把它换算成tick,换算方法是秒数 × 20向上取整,然后再多留 1 秒的收尾
JukeboxSongs
原版的歌曲都在JukeboxSongs这个接口里
1 | ResourceKey<JukeboxSong> THIRTEEN = of("13"); |
再往下来到bootstrap里的注册
1 | public static void bootstrap(BootstrapContext<JukeboxSong> context) { |
后面的两个数字是时长和比较器信号
再看它内部的注册方法
1 | private static void register( |
Util.makeDescriptionId("jukebox_song", id)会拼出一个jukebox_song.<命名空间>.<路径>的翻译键
我们照抄这个方法就行,键名自动按我们自己的命名空间来
物品那边
最后是唱片物品,原版是这么写的
1 | public static final Item MUSIC_DISC_13 = registerItem( |
三个属性
stacksTo(1)是最大堆叠 1,唱片都是一张一张的
rarity(Rarity.RARE)是稀有度,影响名字颜色,RARE是蓝色
jukeboxPlayable(...)就是告诉游戏「这张唱片对应哪首歌」,参数是歌曲的钥匙
动手写
注册声音事件
唱片的音效和普通音效不太一样,它得是Holder.Reference<SoundEvent>
因为JukeboxSong里存的就是Holder,你直接传SoundEvent是编译不过的
我们在ModSoundEvents里加一个方法
1 | private static Holder.Reference<SoundEvent> registerForHolder(String name) { |
和上一篇那个register几乎一样,只是把Registry.register换成了Registry.registerForHolder
registerForHolder返回的Holder.Reference就是这个条目在注册表里的引用,后面歌曲就能存它
然后注册我们这首歌的声音事件
1 | public static final Holder.Reference<SoundEvent> MUSIC_DISC_A_MOMENT_APART = registerForHolder("music_disc.a_moment_apart"); |
这个字符串后面sounds.json里还要用一次,得对上
创建 ModJukeboxSongs
新建一个接口,和原版一样
1 | public interface ModJukeboxSongs { |
of里的字符串就是歌曲的id,一会儿生成出来的json文件名也是它
接着把原版那个register搬过来
1 | private static void register( |
Util在net.minecraft.util包下,别导错了
最后写bootstrap
1 | static void bootstrap(BootstrapContext<JukeboxSong> context) { |
三个数字里,235是时长、15是比较器信号
比较器信号我给的是满格 15,你想让不同的唱片输出电压不一样,可以自己分配
注册唱片物品
1 | public static final Item A_MOMENT_APART_MUSIC_DISC = registerItem("a_moment_apart_music_disc", |
jukeboxPlayable传的是歌曲的钥匙,这样游戏才知道这张唱片该放哪首歌
别忘了加进物品栏
1 | output.accept(ModItems.A_MOMENT_APART_MUSIC_DISC); |
数据生成
注册歌曲
歌曲是数据包注册表,所以要在buildRegistry里挂上
1 |
|
这一行不加,数据生成不会报错,但data/tutorial/jukebox_song/下面是空的
输出歌曲文件
然后在动态注册表生成器里多写一行
1 |
|
模型
唱片的模型有个专门的模板
1 | itemModelGenerators.generateFlatItem(ModItems.A_MOMENT_APART_MUSIC_DISC, ModelTemplates.MUSIC_DISC); |
1.21.1 那时候叫TEMPLATE_MUSIC_DISC,现在叫ModelTemplates.MUSIC_DISC
数据文件
生成的歌曲文件
跑完数据生成,src/main/generated/data/tutorial/jukebox_song/下面会多出一个a_moment_apart.json
1 | { |
四个字段和我们bootstrap里写的一一对应
sound_event就是我们前面注册的那个声音事件,注意是带点号的那个名字
sounds.json
歌曲指向声音事件,声音事件还得指向音频文件
在上一篇的sounds.json里加一段
1 | { |
和普通音效不同的是,这里不写subtitle
因为唱片播放的时候不显示字幕,显示的是歌曲的description,也就是「正在播放:xxx」
语言文件
两条翻译,一条是物品名,一条是歌曲名
1 | translationBuilder.add(ModItems.A_MOMENT_APART_MUSIC_DISC, "A Moment Apart"); |
中文那份也一样
第二条的键必须和生成的json里description.translate一致,不然放唱片的时候会显示成一串键名
贴图
最后是贴图,放在
1 | assets/tutorial/textures/item/a_moment_apart_music_disc.png |
测试
跑完数据生成后,我们就可以启动游戏进行测试了
常见问题(大肥鱼总结版)
- 唱片能放进唱片机但没声音:
sounds.json里少了music_disc.xxx那一条,或者音频文件没放在sounds/下 - 显示「正在播放:jukebox_song.tutorial.xxx」:语言文件少了
jukebox_song.tutorial.xxx这条翻译 - 物品名显示成
item.tutorial.xxx:物品名没加翻译 - 唱片机不认这张唱片:
jukeboxPlayable的钥匙和bootstrap里注册的钥匙不是同一个 - 唱片机里放不进去:物品少了
jukeboxPlayable,或者jukebox_song的数据没生成出来 - 唱到一半就停:
length_in_seconds写得比音频短,改大一点 - 放完了还在「播放」:那是正常的,游戏会多留 1 秒的收尾时间
- 比较器没信号:
comparator_output写的 0 - 唱片模型是紫黑方块:贴图路径不对,或者
items/下的模型定义没生成 - 数据生成没有
jukebox_song目录:buildRegistry里漏了Registries.JUKEBOX_SONG







