08. BPM 和节拍
在本章中,我们将重构音符原型,不再使用时间,而是 BPM 和节拍。
BPM 和节拍
目前音符使用以秒为单位的时间,但这不是节奏游戏谱面的通常做法。一般做法是,每个音符都有一个节拍编号,其时间是使用节拍编号和 BGM 的 BPM(每分钟节拍数)列表计算的。
虽然引擎可以存储 BPM 并自行执行转换,但 Sonolus 提供了方便的功能来执行此操作。让我们看看如何把这个特性集成到我们的引擎中来使用。
BPM 变化
我们首先需要将所有 BPM 的变化告知 Sonolus。
这是通过把使用一个用特殊的数据名称存储 BPM 和节拍的特殊原型的实体列表,提供给关卡数据来完成的:
export const data: LevelData = {
// ...
entities: [
// ...
{
archetype: EngineArchetypeName.BpmChange,
data: [
{
name: EngineArchetypeDataName.Beat,
value: 0,
},
{
name: EngineArchetypeDataName.Bpm,
value: 120,
},
],
},
// ...
],
}
export const data = {
// ...
entities: [
// ...
{
archetype: EngineArchetypeName.BpmChange,
data: [
{
name: EngineArchetypeDataName.Beat,
value: 0,
},
{
name: EngineArchetypeDataName.Bpm,
value: 120,
},
],
},
// ...
],
}
该实体告诉 Sonolus,在节拍0
处,BPM 现在变为120
。
请注意,我们不需要实现 BPM 变化(BPM change)原型。通常,任何使用了未实现原型的实体都将被直接忽略,因此我们可以安全地使用它们在关卡中存储元数据。
重构
通过 BPM 集合,我们就可以重构 Note 原型,在数据中使用节拍而不是时间:
export class Note extends Archetype {
import = this.defineImport({
beat: { name: EngineArchetypeDataName.Beat, type: Number },
})
// ...
}
export class Note extends Archetype {
import = this.defineImport({
beat: { name: EngineArchetypeDataName.Beat, type: Number },
})
// ...
}
请注意,我们不必为节拍使用特殊的数据名称,但我们这样做只是为了保持一致性。