音符绘制
在本章中,您将会学习到:
- 可视时间的计算
- 音符精灵的声明
- 音符精灵的绘制
- 如何解决 Z 轴堆叠冲突
可视时间
让我们计算一个音符的可视时间,包含最小与最大可视时间,并重构生成时间以使用最小可视时间:
class Note: public Archetype {
// ...
Variable<EntityMemoryId> minVisualTime;
Variable<EntityMemoryId> maxVisualTime;
// ...
SonolusApi preprocess() {
FUNCBEGIN
// ...
maxVisualTime = targetTime;
minVisualTime = maxVisualTime - 1;
spawnTime = minVisualTime;
return VOID;
}
// ...
};
声明
就像 Stage
原型一样,为了简便,我们为我们的音符使用标准精灵:
class Sprites {
// ...
int note = 1;
}Sprites;
auto skins = defineSkins<class Sprites>({
// ...
{ SkinSpriteName.NoteHeadCyan, Sprites.note }
});
绘制
有了最小和最大可视时间以及当前时间,我们可以计算音符的 y
坐标。它的计算也很简单:
class Note: public Archetype {
// ...
SonolusApi updateParallel() {
FUNCBEGIN
let y = screen.top - (screen.top - judgeLine.y) * (times.now - minVisualTime) / (maxVisualTime - minVisualTime);
return VOID;
}
};
计算了 y
坐标后,我们可以绘制音符:
class Note: public Archetype {
// ...
SonolusApi updateParallel() {
FUNCBEGIN
// ...
let l = -1 * note.radius, r = note.radius;
let t = y + note.radius, b = y - note.radius;
Draw(Sprites.note, l, b, l, t, r, t, r, b, 0, 1);
return VOID;
}
};
Z 轴堆叠冲突
尽管我们的引擎已经开始工作的,但仍然还有我们尚未解决的潜在问题: Z 轴堆叠冲突。
这是指当有许多对象在同一 z
坐标上渲染时,在帧与帧之间它们的顺序可能并不稳定,这会导致如果它们重叠时会发生闪烁。
为了解决这个问题,让我们将 z
坐标设置为 1000
减去目标时间,这样更早的音符将一直会在后面音符的上方。
这也是一个音符的不变属性,因此我们可以计算一次并将其存储在 EntityMemory
区块以重复使用。然而这个时间应该在 initialize
回调函数中计算而不是 preprocess
回调函数,因为它并不会被生成逻辑使用,因此我们可以减少计算来优化关卡加载时间:
class Note: public Archetype {
// ...
Variable<EntityMemoryId> z;
// ...
SonolusApi initialize() {
FUNCBEGIN
z = 1000 - targetTime;
return VOID;
}
SonolusApi updateParallel() {
FUNCBEGIN
// ...
Draw(Sprites.note, l, b, l, t, r, t, r, b, z, 1);
return VOID;
}
};