書籍資訊
- 書名:《Unityで作るリズムゲーム》
- 作者:長崎大学マルチメディア研究会
這一篇在講什麼
第一篇把畫面與軌道結構先定下來之後,下一步就是回答一個更核心的問題:
「畫面上的 Note 位置,到底怎麼和譜面時間對上?」
這一篇主要處理三件事:
- Single Note 的
y座標到底代表什麼 - 秒數與拍子怎麼互相換算
- 如果曲子有變速,這套換算要怎麼擴充
Single Note 的 y 座標其實是在表示時間差
對 Single Note 來說,x 座標代表它屬於哪一條軌道,這一點比較直觀。真正重要的是 y。
這裡的 y 並不是單純在表示畫面高度,而是在表示:
Note 應該到達判定線的時間 - 曲目目前已經經過的時間
也就是說,y 本質上是「它離判定點還差多少」的視覺化結果。
如果某個 Note 會在曲目開始後 3 秒打到判定線,那麼:
- 曲目剛開始時,它的
y會是正值 - 到第
3秒時,它的y會等於0 - 超過第
3秒之後,它的y會變成負值
如果把:
- Note 判定時間記成
j - 曲目目前時間記成
p
那麼這顆 Note 的 y 可以寫成:
j - p
例如:
j = 3, p = 0,所以y = 3j = 3, p = 3,所以y = 0j = 3, p = 4,所以y = -1

為什麼一定要談拍子和秒數
Unity 執行遊戲時,最容易直接取得的是「秒數」;但 BMS 這類譜面格式,真正記錄的通常是「拍子上的位置」。
所以系統一定要能在這兩個量之間互相轉換:
- 譜面資料習慣用拍子
- 遊戲播放與音訊同步習慣用秒數
這也是節奏遊戲實作裡很常見的一個分工:
- 譜面層保存拍點資訊
- 播放層負責把當前時間換成對應拍點
固定 BPM 下的換算
先看最單純的情況。
1 beat可以先理解成一個四分音符BPM表示每分鐘有幾拍
因此每秒有幾拍,其實就是:
BPM / 60
於是:
beat = sec * (BPM / 60)sec = beat / (BPM / 60)
例子
如果 BPM = 60:
- 每秒
1拍 3秒 =3拍3拍 =3秒
如果 BPM = 120:
- 每秒
2拍 3秒 =6拍3拍 =1.5秒

變速曲的關鍵在於分段累加
真正有難度的是曲中變速。
假設一首歌在不同拍段有不同 BPM:
0 - 2拍與4 - 6拍是60 BPM2 - 4拍與6 - 8拍是120 BPM

這時候就不能再用一條公式一次算到底,而是要先判斷目標位置落在哪一個速度段裡,再把前面所有段落累加起來。
拍子轉秒數
例如要算某一拍發生在第幾秒:
- 先把它前面完整經過的 BPM 段逐段換成秒數。
- 再補上它在當前段內所經過的那一小段。
像前面的例子:
- 到第
2拍為止共經過2 sec - 到第
4拍為止共經過3 sec - 到第
7拍為止共經過5.5 sec
秒數轉拍子
反過來要從秒數求拍子時,思路也一樣:
- 先累積完整經過的速度段。
- 找到目前秒數落在哪一段裡。
- 再用該段 BPM 去補出最後那一小段拍數。
對譜面資料來說,最後需要保存什麼
回到節奏遊戲本體,這些換算最後會回到 Note 資料結構上。
系統真正需要知道的資訊,其實很集中:
- 這顆 Note 在哪一條軌道
- 這顆 Note 應該在第幾拍被打到
- 如果是 Long Note,它從哪一拍開始、哪一拍結束
換句話說:
- 單點 Note 至少要知道判定拍點與軌道
- 長押 Note 還要再多保存結束拍點
這些就是後面設計 NoteProperty、BeatMap 之類類別時的基礎。
我自己的整理
節奏遊戲常讓人一開始就想著「怎麼讓 Note 掉下來」,但其實更重要的是先想清楚:
畫面位置到底是由什麼數學關係推回來的。
這一篇的核心,就是把 y 座標重新理解成「時間差」而不是「高度」。一旦這個觀念成立,很多問題都會自然簡化:
- 為什麼 Note 會往下走
- 為什麼它會在某個時刻剛好碰到判定線
- 為什麼 BPM 一變,整體流動節奏就必須重算
先記住的重點
- Note 的
y座標,本質上是在表示它和判定時間之間的差距。 - 譜面通常記拍子,遊戲執行通常拿秒數,所以一定要做雙向換算。
- 固定 BPM 可以直接換算,變速曲則必須分段累加。
- Long Note 需要同時保存起點與終點時間資訊。