Skip to content

Commit d2a1cda

Browse files
authored
Update variable-speed-design-scheme.md
1 parent 472a314 commit d2a1cda

File tree

1 file changed

+54
-54
lines changed

1 file changed

+54
-54
lines changed

docs/essay/variable-speed-design-scheme.md

Lines changed: 54 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)