Skip to content
第02回 ゲームと時間

4 / 4 周期と共通の時間軸 ​

このステップの目的

秒・拍・Tickを換算して、曲を基準にした共通の時間軸を作ります。

剰余 ​

譜面全体の時間は増え続けますが、拍の中の位置は繰り返します。
増え続ける値から、繰り返しの中の位置を求めるときには剰余の計算を使います。

剰余とは、ある数を別の数で割ったときの余りのことです。
例えば、7を3で割ったときの余りは1です。
C#では7 % 3と書きます。

C# の剰余演算子

C#の%は、割り算の余りを求める演算子です。

例えば、4 Tick/拍の指定の曲で曲の進行Tickが582の場合は、582 % 4 = 2となり、拍のTick位置は2となります。

ここで問題になるのが、負の値の扱いです。
負の値に剰余算すると、結果も負になる場合があります。
例えば-1 % 4は−1です。
C#の剰余演算

ここでは周期の位置をあくまで正のTickで求めたいため、負の結果にTickの値を足して正のTick位置に修正しています。

csharp
int tick = (int)currentTick % ticksPerBeat;

if (tick < 0)
    tick += ticksPerBeat;

サンプルのBeatTickViewでは、さらに小数部分を含むTick位置から拍の中の位置を求めています。
浮動小数点数の剰余を使用すれば、区画の番号だけでなく、拍の途中を進むカーソルの位置も計算できます。

リズムゲームの時間軸をそろえる ​

ここまでの時間を、曲の開始を0秒とする時間軸へそろえます。譜面では拍とTickを使って位置を決め、ゲームの処理へ渡すときに秒へ換算します。

text
曲の時刻 = 音声の現在時刻 - 曲の開始時刻
1 Tickの秒数 = 60 / BPM / 1拍のTick数
ノートの判定時刻 = 譜面の開始位置 + 全体のTick番号 × 1 Tickの秒数

練習譜面の開始位置は0秒です。ChartSource.FixedChartにあるTickの配列を、配布済みのChartData.Compileがレーンと判定時刻の配列へ変換します。次回は同じ形のデータをCSVから作ります。

時間の情報と処理の関係
音声の時計と開始時刻
開始時刻との差
曲の時刻
BPM・Tick番号・譜面の開始位置
秒へ換算
ノートの判定時刻
曲の時刻
現在時刻
時刻差の計算
ノートの判定時刻
予定時刻
時刻差の計算
時刻差の計算
発行・位置・受付幅
ノートの表示と入力判定

RhythmGame.Updateは、1フレームに一度だけ曲の時刻を読み、表示の更新と各レーンの入力判定へ渡します。次も読み取り用の抜粋です。

RhythmGame.cscsharp
double songTime = clock.SongTime;
Advance(songTime);
for (int lane = 0; lane < input.LaneCount; lane++)
    if (input.WasLanePressed(lane)) PressLane(lane, songTime);

この作例の入力時刻は、入力を確認したフレームの曲時刻です。複数のレーンにも同じ値を渡します。Input Systemの入力イベントが持つ時刻を直接使う場合は、曲の時計との対応を別に用意する必要があります。この基本課題では、上の配布済みの処理を使います。

開始後にRを押し、曲と表示秒数が最初へ戻ることを確認してください。音源だけを戻し、曲の開始時刻を前のままにしたら、表示と判定がどうなるか説明してください。

チェックリスト
  • 全体のTick番号と、拍の中のTick番号を区別できる。
  • 再生・表示・判定が共有する時計と開始点をコードで確認した。
  • 剰余から拍の中の位置を求め、負の余りを補正する理由を説明できる。