您实际上并不需要(或想要)多个计时器;仅驱动窗口刷新的单个计时器(以 30fps 或其他速度)就足够了。
当您加载歌曲文件时,对于歌曲中的每个音符,您应该存储歌曲开始播放和播放特定音符之间的毫秒数,例如(伪代码):
int millisCounter = 0;
int note, noteLengthMillis;
while(ReadNextNoteFromSongFile(note, noteLengthMillis))
{
songNoteRecordsVector.push_back(NoteRecord(millisCounter, note));
millisCounter += noteLengthMillis;
}
然后,当您开始游戏关卡时,在歌曲开始播放的那一刻,以毫秒为单位记录当前时间。只要歌曲继续播放,您就将使用此值作为时间零参考。
现在,在每个视频帧(或实际上在任何时间),您可以计算相对于当前系统时钟时间的毫秒数,直到播放给定的音符:
int NoteRecord :: GetMillisecondsUntilNoteIsPlayed(int songStartTimeMillis, int currentTimeMillis) const
{
return this->myNoteOffsetMillis - (currentTimeMillis - songStartTimeMillis);
}
请注意,如果音符的播放时间已经过去,则返回的值将为负数。
一旦你有了它,只需将每个音符的当前毫秒 - 直到音符播放结果转换为相应的屏幕位置,你就知道在哪里为当前帧绘制音符圆圈:
int millisUntilNotePlayTime = note.GetMillisecondsUntilNoteIsPlayed(songStartTimeMillis, currentTimeMillis);
int circleY = someFixedOffsetY + (millisUntilNotePlayTime/(1000/pixelsScrolledPerSecond));
DrawCircleAt(circleX, circleY);
...如果用户按下一个键,您可以使用相同的函数计算用户与给定音符的正确时间相差多远,例如:
int errorMillis = note.GetMillisecondsUntilNoteIsPlayed(songStartTimeMillis, currentTimeMillis);
if (errorMillis < -50)
{
printf("You're too slow!\n");
}
else if (errorMillis > 50)
{
printf("You jumped the gun!\n");
}
else
{
printf("Good job!\n");
}