-1

我正在使用 C++ 和 SDL 构建一个类似于摇滚乐队的程序,并且希望能够对事件进行计时,以便我可以在程序中编排一首歌曲。这是我到目前为止所取得的成就:

  1. 从窗口顶部到中间的4个圆圈进入4个指定的击球点。

  2. 圆圈以随机间隔下降(不使用时间,随机数生成器确定它们开始下降距离窗口顶部的距离)

  3. 我能够确定何时击中音符,并在右上角显示分数

  4. 在标记周围应用简单的火花,让您知道音符被击中

  5. 我可以打开一个文件并从中读取文本

现在我希望能够使用该文件编写歌曲供程序读取和执行。我在想一些类似“1g、2g、4y、3r 等等等等”的东西。数字是毫秒,等待下一个音符和指定哪种颜色应该下降的字母。

4

1 回答 1

1

您实际上并不需要(或想要)多个计时器;仅驱动窗口刷新的单个计时器(以 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");
}
于 2013-11-07T20:19:38.880 回答