05. 音符显示
在本章中,我们将实现音符显示组件:
音符显示组件
让我们先设置一个音符显示组件:
export const noteDisplay = {}
export const noteDisplay = {}
// ...
const components = [initialization, stage, noteDisplay] as const
// ...
// ...
const components = [initialization, stage, noteDisplay]
// ...
声明
声明音符的皮肤精灵:
export const skin = defineSkin({
sprites: {
// ...
note: SkinSpriteName.NoteHeadCyan,
},
})
export const skin = defineSkin({
sprites: {
// ...
note: SkinSpriteName.NoteHeadCyan,
},
})
模式
音符显示组件将负责在多个片段中绘制音符,因此让我们声明一个模式枚举(enum)来代表它:
enum Mode {
None,
Overlay,
Fall,
Frozen,
}
const Mode = {
None: 0,
Overlay: 1,
Fall: 2,
Frozen: 3,
}
为了存储当前模式,我们可以使用 Tutorial Memory 区块中的一个值:
let mode = tutorialMemory(DataType<Mode>)
let mode = tutorialMemory(Number)
最后,组件可以暴露一系列方法来改变当前模式:
// ...
export const noteDisplay = {
showOverlay() {
mode = Mode.Overlay
},
showFall() {
mode = Mode.Fall
},
showFrozen() {
mode = Mode.Frozen
},
clear() {
mode = Mode.None
},
}
// ...
export const noteDisplay = {
showOverlay() {
mode = Mode.Overlay
},
showFall() {
mode = Mode.Fall
},
showFrozen() {
mode = Mode.Frozen
},
clear() {
mode = Mode.None
},
}
绘制
让我们实现音符显示组件的绘制功能。
当我们在 None 模式时,这里没有需要绘制的东西,我们就可以简单退出即可:
// ...
export const noteDisplay = {
update() {
if (!mode) return
},
// ...
}
// ...
export const noteDisplay = {
update() {
if (!mode) return
},
// ...
}
当我们在需要被 Intro 片段使用的 Overlay 模式时,我们应该在屏幕中心旁绘制一个放大的音符。
注意到 Intro 片段会持续 1
秒,因此我们可以让音符从 0.75
秒开始消失。
// ...
export const noteDisplay = {
update() {
// ...
if (mode === Mode.Overlay) {
const a = Math.unlerpClamped(1, 0.75, segment.time)
const layout = Rect.one
.mul(2 * note.radius)
.scale(1, -1)
.translate(0, 0.5)
skin.sprites.note.draw(layout, 1000, a)
}
},
// ...
}
// ...
export const noteDisplay = {
update() {
// ...
if (mode === Mode.Overlay) {
const a = Math.unlerpClamped(1, 0.75, segment.time)
const layout = Rect.one
.mul(2 * note.radius)
.scale(1, -1)
.translate(0, 0.5)
skin.sprites.note.draw(layout, 1000, a)
}
},
// ...
}
当我们在会被 Fall 片段使用的 Fall 模式时,我们应该绘制一个从顶部到判定线下落的音符。
注意到 Fall 片段会持续 2
秒,因此我们应该据此计算 y
坐标。
当我们在会被 Frozen 片段使用的 Frozen 模式时,我们应该在判定线绘制一个冻住的音符。我们可以通过将 y
设置为 1
来重复使用 Fall 模式的代码。
// ...
export const noteDisplay = {
update() {
// ...
if (mode === Mode.Overlay) {
// ...
} else {
const y = mode === Mode.Fall ? Math.unlerp(0, 2, segment.time) : 1
const layout = Rect.one.mul(note.radius).scale(1, -1).translate(0, y)
skin.sprites.note.draw(layout, 1000, 1)
}
},
// ...
}
// ...
export const noteDisplay = {
update() {
// ...
if (mode === Mode.Overlay) {
// ...
} else {
const y = mode === Mode.Fall ? Math.unlerp(0, 2, segment.time) : 1
const layout = Rect.one.mul(note.radius).scale(1, -1).translate(0, y)
skin.sprites.note.draw(layout, 1000, 1)
}
},
// ...
}