@@ -19,19 +19,19 @@ comments: true
1919## 谱面数据结构定义
2020
2121- 谱面存档文件 {}
22- - BPM 组 [ ] (不影响速度,用于确定音符的判定时间)
23- - Bpm 元素 {}
24- - 开始节拍: [ x,x,x]
25- - bpm: xxx
26- - 变速组 [ ]
27- - 变速元素 {}
28- - 变速类型: (相对|绝对)
29- - 变速数据: [ 见下文]
30- - 音符组 [ ]
31- - 音符元素 {}
32- - 判定节拍: [ x,x,x]
33- - 变速组引用下标: xxx
34- - 变速 offset: xxx
22+ - BPM 组 [ ] (不影响速度,用于确定音符的判定时间)
23+ - Bpm 元素 {}
24+ - 开始节拍: [ x,x,x]
25+ - bpm: xxx
26+ - 变速组 [ ]
27+ - 变速元素 {}
28+ - 变速类型: (相对|绝对)
29+ - 变速数据: [ 见下文]
30+ - 音符组 [ ]
31+ - 音符元素 {}
32+ - 判定节拍: [ x,x,x]
33+ - 变速组引用下标: xxx
34+ - 变速 offset: xxx
3535
3636## BPM 组和判定时间
3737
@@ -42,41 +42,41 @@ BPM 组是线性、依据开始节拍正序排序的,这意味着任意时刻
4242单个 Bpm 元素的构成如下:
4343
4444- Bpm 元素
45- - 开始节拍(通常是一个三元数组,例如 [ 2,3,4] ,代表位于第 2 + (3 / 4) = 2.75 拍。首个 Bpm 元素的节拍经过计算后必须等于 0,末个 Bpm 元素在激活后会持续到谱面结束。)
46- - bpm(通常是一个整数或小数,例如 128,表示激活后直至下个 Bpm 元素激活前音乐的 bpm。)
45+ - 开始节拍(通常是一个三元数组,例如 [ 2,3,4] ,代表位于第 2 + (3 / 4) = 2.75 拍。首个 Bpm 元素的节拍经过计算后必须等于 0,末个 Bpm 元素在激活后会持续到谱面结束。)
46+ - bpm(通常是一个整数或小数,例如 128,表示激活后直至下个 Bpm 元素激活前音乐的 bpm。)
4747
4848在以下示例中,我们定义一个音乐对应的 BPM 组:
4949
5050- BPM 组
51- - Bpm 元素 1
52- - 开始节拍: [ 0,0,1] = 0 // “1” 避免了除 0 异常
53- - bpm: 120
54- - Bpm 元素 2
55- - 开始节拍: [ 2,2,4] = 2.5 // 此时第 2 个元素激活,第 1 个元素自动失效
56- - bpm: 60
57- - Bpm 元素 3
58- - 开始节拍: [ 4,0,1] = 4
59- - bpm: 80
51+ - Bpm 元素 1
52+ - 开始节拍: [ 0,0,1] = 0 // “1” 避免了除 0 异常
53+ - bpm: 120
54+ - Bpm 元素 2
55+ - 开始节拍: [ 2,2,4] = 2.5 // 此时第 2 个元素激活,第 1 个元素自动失效
56+ - bpm: 60
57+ - Bpm 元素 3
58+ - 开始节拍: [ 4,0,1] = 4
59+ - bpm: 80
6060
6161下面示范如何根据 BPM 组和音符判定节拍计算判定毫秒时间:
6262
6363- 假设一个音符位于 [ 0,1,2] = 0.5 拍
64- - 可查到此音符位于第 1 个 Bpm 元素内
65- - 此音符在第 1 个 Bpm 元素内已经经过了 0.5 拍
66- - 按照 120 的 bpm,计算得到每拍的时间 60s / 120 = 0.5s = 500ms
67- - 所以可得此音符的判定时间为 0.5 \* 500ms = 250ms
64+ - 可查到此音符位于第 1 个 Bpm 元素内
65+ - 此音符在第 1 个 Bpm 元素内已经经过了 0.5 拍
66+ - 按照 120 的 bpm,计算得到每拍的时间 60s / 120 = 0.5s = 500ms
67+ - 所以可得此音符的判定时间为 0.5 \* 500ms = 250ms
6868- 假设一个音符位于 [ 2,1,2] = 3.5 拍
69- - 可查到此音符位于第 2 个 Bpm 元素内
70- - 此音符在第 2 个 Bpm 元素内已经经过了 3.5 - 2.5 = 1 拍
71- - 按照 60 的 bpm,计算得到每拍的时间 60s / 60 = 1s = 1000ms
72- - 由于完整经过了第 1 个 Bpm 元素,计算之前的时间为 (2.5 - 0) \* (60 / 120) = 1.25s = 1250ms
73- - 相加所有时间得到此音符的判定时间为 1250ms + 1000ms = 2250ms
69+ - 可查到此音符位于第 2 个 Bpm 元素内
70+ - 此音符在第 2 个 Bpm 元素内已经经过了 3.5 - 2.5 = 1 拍
71+ - 按照 60 的 bpm,计算得到每拍的时间 60s / 60 = 1s = 1000ms
72+ - 由于完整经过了第 1 个 Bpm 元素,计算之前的时间为 (2.5 - 0) \* (60 / 120) = 1.25s = 1250ms
73+ - 相加所有时间得到此音符的判定时间为 1250ms + 1000ms = 2250ms
7474- 假设一个音符位于 [ 5,0,1] = 5 拍
75- - 可查到此音符位于第 3 个 Bpm 元素内
76- - 此音符在第 3 个 Bpm 元素内已经经过了 5 - 4 = 1 拍
77- - 按照 80 的 bpm,计算得到每拍的时间 60s / 80 = 0.75s = 750ms
78- - 由于完整经过了第 1 个和第 2 个 Bpm 元素,计算之前的时间为 [ (2.5 - 0) \* (60 / 120)] + [ (4 - 2.5) \* (60 / 60)] = 1.25s + 1.5s = 2750ms
79- - 相加所有时间得到此音符的判定时间为 2750ms + 750ms = 3500ms
75+ - 可查到此音符位于第 3 个 Bpm 元素内
76+ - 此音符在第 3 个 Bpm 元素内已经经过了 5 - 4 = 1 拍
77+ - 按照 80 的 bpm,计算得到每拍的时间 60s / 80 = 0.75s = 750ms
78+ - 由于完整经过了第 1 个和第 2 个 Bpm 元素,计算之前的时间为 [ (2.5 - 0) \* (60 / 120)] + [ (4 - 2.5) \* (60 / 60)] = 1.25s + 1.5s = 2750ms
79+ - 相加所有时间得到此音符的判定时间为 2750ms + 750ms = 3500ms
8080
8181## 变速音符速度-时间关系计算
8282
@@ -85,26 +85,26 @@ BPM 组是线性、依据开始节拍正序排序的,这意味着任意时刻
8585这里以 CyanStars 的设计为例,通过贝塞尔曲线可以实现可视化编辑器和减小谱面文件体积占用。
8686
8787- 变速数据 [ ]
88- - 三次贝塞尔曲线元素 1 [ ]
89- - 贝塞尔控制点 1 {}
90- - x: xxx(相对于判定时间的毫秒时间偏移)
91- - y: xxx(流速)
92- - 贝塞尔控制点 2 {}
93- - ...
94- - 贝塞尔控制点 3 {}
95- - ...
96- - 贝塞尔控制点 4 {}
97- - ...
98- - 三次贝塞尔曲线元素 2 [ ]
99- - ...
88+ - 三次贝塞尔曲线元素 1 [ ]
89+ - 贝塞尔控制点 1 {}
90+ - x: xxx(相对于判定时间的毫秒时间偏移)
91+ - y: xxx(流速)
92+ - 贝塞尔控制点 2 {}
93+ - ...
94+ - 贝塞尔控制点 3 {}
95+ - ...
96+ - 贝塞尔控制点 4 {}
97+ - ...
98+ - 三次贝塞尔曲线元素 2 [ ]
99+ - ...
100100
101101值得注意的是,这里的变速数据和三次贝塞尔曲线元素是有一定要求的:
102102
103103- 在判定时刻必须有明确流速,即首个曲线元素必须存在,且其控制点 1 的 x 值必须等于 0
104104- 曲线元素之间必须是“首尾相连”(连续)的,即后一个曲线元素的控制点 1 必须完全等于上一个曲线元素的控制点 4
105105- 每个曲线元素必须是“不折返”(可导,任意 x 值有且仅有一个对应的 y 值)的,这里有两种验证方案:
106- - 验证控制点 2、控制点 3 的 x 值在控制点 1、控制点 4 区间内,这是确保曲线完全不折返的** 充分非必要** 条件,阻止了一些合法的曲线
107- - 利用贝塞尔曲线特性,计算曲线边界在控制点 1、控制点 4 区间内,这是** 充分必要条件** ,但计算难度大
106+ - 验证控制点 2、控制点 3 的 x 值在控制点 1、控制点 4 区间内,这是确保曲线完全不折返的** 充分非必要** 条件,阻止了一些合法的曲线
107+ - 利用贝塞尔曲线特性,计算曲线边界在控制点 1、控制点 4 区间内,这是** 充分必要条件** ,但计算难度大
108108- 所有控制点 1、控制点 4 的 x 值必须是单调递增或单调递减的(取决于程序定义判定偏移为正值还是负值),且不能相同
109109
110110在定义流速时,我们从判定偏移 0 时开始,倒推在判定前音符的流速;在游戏时,我们从最后一个曲线元素读取音符的流速,直到进入第一个曲线元素。这两个操作的方向是相反的。
@@ -136,8 +136,8 @@ BPM 组是线性、依据开始节拍正序排序的,这意味着任意时刻
136136- 创建一个变速组,并编写变速曲线
137137- 让所有音符引用此变速组
138138- 为每个音符设定不同的变速 offset,具体如下:
139- - 这一组音符的最后一个音符的 offset 为 0,代表完全应用整个变速效果
140- - 向前计算每个音符与最后一个音符的时间差,并作为 offset 填入,以将此音符的变速效果与最后一个音符同步
139+ - 这一组音符的最后一个音符的 offset 为 0,代表完全应用整个变速效果
140+ - 向前计算每个音符与最后一个音符的时间差,并作为 offset 填入,以将此音符的变速效果与最后一个音符同步
141141
142142## 总结
143143
0 commit comments