Sonolus Wiki

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 },
    })

    // ...
}

请注意,我们不必为节拍使用特殊的数据名称,但我们这样做只是为了保持一致性。