Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa8dbd7d27 | ||
|
|
14975bc31c | ||
|
|
c8d61ba5b7 | ||
|
|
e7646d59b5 | ||
|
|
4463ba1b87 | ||
|
|
1115099a29 | ||
|
|
449d800bae | ||
|
|
ec2ab12898 | ||
|
|
f2b0c3a33e | ||
|
|
46fbe848d3 | ||
|
|
5555e612b3 | ||
|
|
e23b6000c4 | ||
|
|
c8bc990412 | ||
|
|
aedf303e89 | ||
|
|
6a433657f6 | ||
|
|
64fe405d2e | ||
|
|
f18d0974ac | ||
|
|
51207f501f | ||
|
|
4d7e2961f8 | ||
|
|
a26095bea2 | ||
|
|
a6711f859f | ||
|
|
d6e5ff39e3 | ||
|
|
f315aa9f2e | ||
|
|
92a39d05ad | ||
|
|
2792202ac5 | ||
|
|
1d8825da77 | ||
|
|
f4dee6f3c6 | ||
|
|
7300fbfa16 | ||
|
|
8486bd807e | ||
|
|
41ada2cf46 | ||
|
|
8ee381909f | ||
|
|
ae9c93deb2 | ||
|
|
d922a6fae6 | ||
|
|
006e2f2ff1 | ||
|
|
c14a85c464 | ||
|
|
5aedbbfbe0 | ||
|
|
a14b4436f5 | ||
|
|
4e1e57b08e | ||
|
|
51e8b1f449 | ||
|
|
11846eb83e | ||
|
|
2b3e6404a4 | ||
|
|
f5e8b6d6f4 | ||
|
|
758645041b | ||
|
|
fb2e0e124b | ||
|
|
006a5fe4f5 | ||
|
|
1e4e94cce7 | ||
|
|
1024b31790 | ||
|
|
55c53278ea | ||
|
|
089f4c551d | ||
|
|
9a060eb6ff | ||
|
|
6b0da44786 |
@@ -1,12 +1,32 @@
|
|||||||
# 节奏医生中文编辑器教程网站
|
# 节奏医生中文编辑器教程网站
|
||||||
|
|
||||||
## 预览和编辑
|
## 预览
|
||||||
|
|
||||||
### [Visual Studio Code](https://code.visualstudio.com/)
|
### [Visual Studio Code](https://code.visualstudio.com/)
|
||||||
|
|
||||||
此项目基于 [docsify](https://docsify.js.org/#/) 编辑,并使用 [RDView](https://github.com/OLDRedstone/RDView) 显示编辑器可交互模块。
|
此项目基于 [docsify](https://docsify.js.org/#/) 编辑,并使用 [RDView](https://github.com/OLDRedstone/RDView) 和 [RhythmBase.View(RDTKView)](https://github.com/OLDRedstone/RhythmBase.View) 显示编辑器可交互模块。
|
||||||
|
可以参考以下多种方案预览编辑效果。
|
||||||
|
|
||||||
- 确保 [安装 markdown-rdview-support](https://github.com/OLDRedstone/markdown-rdview-support) 即可预览 RDView 内容。
|
- [安装 docsify](https://docsify.js.org/#/quickstart) 以实时预览页面。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm i docsify-cli -g
|
||||||
|
```
|
||||||
|
|
||||||
|
- 运行以下命令以启动本地服务器实时预览。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docsify s
|
||||||
|
```
|
||||||
|
|
||||||
|
- 在 Visual Studio Code 插件商店[安装 Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer) 以预览。
|
||||||
|
|
||||||
|
### RDView
|
||||||
|
|
||||||
|
> ### 此条目即将废弃。
|
||||||
|
> 请优先使用 RhythmBase.View 格式。
|
||||||
|
|
||||||
|
- [安装 markdown-rdview-support](https://github.com/OLDRedstone/markdown-rdview-support) 以预览 RDView 内容。
|
||||||
如果成功安装,那么以下内容的 markdown 预览应当是一个事件栏。
|
如果成功安装,那么以下内容的 markdown 预览应当是一个事件栏。
|
||||||
|
|
||||||
```rdview 7 5 1
|
```rdview 7 5 1
|
||||||
@@ -15,16 +35,18 @@ es.SetCrotchetsPerBar_4 0 1[];
|
|||||||
eb.AddClassicBeat 0 2[tick=6];
|
eb.AddClassicBeat 0 2[tick=6];
|
||||||
```
|
```
|
||||||
|
|
||||||
- 确保 [安装 docsify](https://docsify.js.org/#/quickstart) 以实时预览页面。
|
### RhythmBase.View
|
||||||
|
|
||||||
```bash
|
RDView 的替代。
|
||||||
npm i docsify-cli -g
|
可以直接使用关卡文件内格式进行展示。
|
||||||
```
|
暂无计划支持 Visual Studio Code 内预览。(~~感觉没必要支持~~)
|
||||||
|
可以访问[此处](https://view.obugs.cn/)以预览效果和获得额外属性说明。
|
||||||
|
|
||||||
- 运行以下命令以启动本地服务器实时预览。
|
## 编辑
|
||||||
|
|
||||||
```bash
|
请尽量使用 [RhythmBase.View(RDTKView)](https://github.com/OLDRedstone/RhythmBase.View) 格式展示事件。
|
||||||
docsify s
|
本教程后期将会逐步迁移模块指此格式。
|
||||||
```
|
|
||||||
|
## 其他
|
||||||
|
|
||||||
Game assets courtesy of [7th Beat Games](https://7thbe.at/). Game assets are excluded from this site's license.
|
Game assets courtesy of [7th Beat Games](https://7thbe.at/). Game assets are excluded from this site's license.
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
- [<img src="/indexicon/Intro.png"/><p>序</p>](/pages/intro.md)
|
- [<img src="/indexicon/Intro.png"/><p>序</p>](/pages/intro.md)
|
||||||
- [<img src="/indexicon/changelog.png"/><p>更新信息</p>](/pages/changelog.md)
|
- [<img src="/indexicon/changelog.png"/><p>更新信息</p>](/pages/changelog.md)
|
||||||
- 基础:熟悉编辑器
|
- 基础:熟悉编辑器
|
||||||
- [<img src="/indexicon/changegameversion.png"/><p>修改游戏版本</p>](/pages/changegameversion.md)
|
- [<img src="/indexicon/changegameversion.png"/><p>修改游戏配置</p>](/pages/changegameversion.md)
|
||||||
- [<img src="/indexicon/editorinterface.png"/><p>编辑器界面与快捷键</p>](/pages/editorinterface.md)
|
- [<img src="/indexicon/editorinterface.png"/><p>编辑器界面与快捷键</p>](/pages/editorinterface.md)
|
||||||
- [<img src="/indexicon/importandexport.png"/><p>导入与导出</p>](/pages/importandexport.md)
|
- [<img src="/indexicon/importandexport.png"/><p>导入与导出</p>](/pages/importandexport.md)
|
||||||
- 基础:游戏机制
|
- 基础:游戏机制
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
- [<img src="/indexicon/vfxpro.png"/><p>视觉干扰</p>](/pages/vfxpro.md)
|
- [<img src="/indexicon/vfxpro.png"/><p>视觉干扰</p>](/pages/vfxpro.md)
|
||||||
- [<img src="/indexicon/camerafx.png"/><p>摄像机视效</p>](/pages/camerafx.md)
|
- [<img src="/indexicon/camerafx.png"/><p>摄像机视效</p>](/pages/camerafx.md)
|
||||||
- [<img src="/indexicon/hallofmirror.png"/><p>镜厅</p>](/pages/hallofmirror.md)
|
- [<img src="/indexicon/hallofmirror.png"/><p>镜厅</p>](/pages/hallofmirror.md)
|
||||||
- [<img src="/indexicon/windowmove.png"/><p>窗口移动</p>](/pages/windowmove.md)
|
- [<img src="/indexicon/windowmove.png"/><p>窗口相关的动画</p>](/pages/WindowDance.md)
|
||||||
- [<img src="/indexicon/hand.png"/><p>手</p>](/pages/hand.md)
|
- [<img src="/indexicon/hand.png"/><p>手</p>](/pages/hand.md)
|
||||||
- [<img src="/indexicon/character.png"/><p>自制角色与精灵资源</p>](/pages/spritesheet.md)
|
- [<img src="/indexicon/character.png"/><p>自制角色与精灵资源</p>](/pages/spritesheet.md)
|
||||||
- [<img src="/indexicon/decoration.png"/><p>使用精灵</p>](/pages/decoration.md)
|
- [<img src="/indexicon/decoration.png"/><p>使用精灵</p>](/pages/decoration.md)
|
||||||
|
|||||||
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 701 KiB |
|
After Width: | Height: | Size: 639 KiB |
|
After Width: | Height: | Size: 609 KiB |
|
After Width: | Height: | Size: 555 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 244 KiB |
|
After Width: | Height: | Size: 14 KiB |
@@ -35,12 +35,8 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import "/script/rdview.js";
|
import "./script/rdview.js";
|
||||||
import {
|
import {render} from "./script/rdtkview_render.js";
|
||||||
Extensions,
|
|
||||||
IconStyle,
|
|
||||||
Point,
|
|
||||||
} from "/script/Extensions.js";
|
|
||||||
|
|
||||||
function escapeHtml(unsafe) {
|
function escapeHtml(unsafe) {
|
||||||
return unsafe
|
return unsafe
|
||||||
@@ -53,52 +49,32 @@
|
|||||||
loadRDView();
|
loadRDView();
|
||||||
const originalCodeRenderer = $docsify.markdown
|
const originalCodeRenderer = $docsify.markdown
|
||||||
?.renderer?.code;
|
?.renderer?.code;
|
||||||
|
function codeblockRender(code, lang) {
|
||||||
|
if (lang.trim().startsWith("rdview")) {
|
||||||
|
return RDViewRender(lang, code);
|
||||||
|
}
|
||||||
|
// else if (lang.trim().startsWith("rdtkview")) {
|
||||||
|
// const args = lang.slice(8).trim().split(" ");
|
||||||
|
// const width = parseInt(args[0]) || 8;
|
||||||
|
// const height = parseInt(args[1]) || 4;
|
||||||
|
// const scale = parseFloat(args[2]) || 2.0;
|
||||||
|
// const result = render(code, width, height, scale);
|
||||||
|
// result.toString = () => result;
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
return originalCodeRenderer
|
||||||
|
? originalCodeRenderer(code, lang)
|
||||||
|
: `<pre data-lang="${lang}"><code>${
|
||||||
|
escapeHtml(code)
|
||||||
|
}</code></pre>`;
|
||||||
|
}
|
||||||
$docsify = {
|
$docsify = {
|
||||||
...$docsify,
|
...$docsify,
|
||||||
...{
|
...{
|
||||||
markdown: {
|
markdown: {
|
||||||
renderer: {
|
renderer: {
|
||||||
code: function (code, lang) {
|
code: (code, lang) => {
|
||||||
if (
|
return codeblockRender(code, lang);
|
||||||
lang.trim().startsWith(
|
|
||||||
"rdview2",
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
const langParts = lang
|
|
||||||
.trim().split(" ");
|
|
||||||
const width =
|
|
||||||
parseInt(
|
|
||||||
langParts[1],
|
|
||||||
) || 8;
|
|
||||||
const height =
|
|
||||||
parseInt(
|
|
||||||
langParts[2],
|
|
||||||
) || 4;
|
|
||||||
const scale =
|
|
||||||
(parseFloat(
|
|
||||||
langParts[3],
|
|
||||||
) || 1) * 2;
|
|
||||||
return `<div class="rdview2-container"><canvas class="rdview2-canvas" image-rendering: pixelated;" data-width="${width}" data-height="${height}" data-scale="${scale}"" data='${
|
|
||||||
JSON.stringify(code)
|
|
||||||
}'></canvas></div>`;
|
|
||||||
} else if (
|
|
||||||
lang.trim().startsWith(
|
|
||||||
"rdview",
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return RDViewRender(
|
|
||||||
lang,
|
|
||||||
code,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return originalCodeRenderer
|
|
||||||
? originalCodeRenderer(
|
|
||||||
code,
|
|
||||||
lang,
|
|
||||||
)
|
|
||||||
: `<pre data-lang="${lang}"><code>${
|
|
||||||
escapeHtml(code)
|
|
||||||
}</code></pre>`;
|
|
||||||
},
|
},
|
||||||
image: function (src, title, alt) {
|
image: function (src, title, alt) {
|
||||||
return `<div class="image-tip"><img class="medium-zoom-image" src="${src}" alt="${alt}">${
|
return `<div class="image-tip"><img class="medium-zoom-image" src="${src}" alt="${alt}">${
|
||||||
@@ -126,18 +102,17 @@
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<script src="/script/docsify.min.js"></script>
|
<script type="module" src="./script/special.js"></script>
|
||||||
|
<!-- <script src="/script/docsify.min.js"></script> -->
|
||||||
<script src="/script/zoom-image.min.js"></script>
|
<script src="/script/zoom-image.min.js"></script>
|
||||||
<script src="/script/search.min.js"></script>
|
<script src="/script/search.min.js"></script>
|
||||||
<script src="/script/emoji.min.js"></script>
|
<script src="/script/emoji.min.js"></script>
|
||||||
<script src="/script/docsify-copy-code.min.js"></script>
|
<!-- <script src="/script/docsify-copy-code.min.js"></script> -->
|
||||||
<script src="/script/switchLightDarkMode.js"></script>
|
<script src="/script/switchLightDarkMode.js"></script>
|
||||||
<script src="/script/otto.js"></script>
|
<script src="/script/otto.js"></script>
|
||||||
<script src="/script/custommethod-search.js"></script>
|
<script src="/script/custommethod-search.js"></script>
|
||||||
<script
|
<!-- <script
|
||||||
src="https://cdn.jsdelivr.net/npm/prismjs@1/components/prism-yaml.js"
|
src="https://cdn.jsdelivr.net/npm/prismjs@1/components/prism-yaml.js"
|
||||||
></script>
|
></script> -->
|
||||||
<script type="module" src="/script/special.js"></script>
|
|
||||||
<script type="module" src="/script/rdview2.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
# 窗口相关的动画
|
||||||
|
|
||||||
|
在1.0正式版更新后,窗口被单独提取成了一个独立的板块:**窗口板块**。为了避免剧透,这个板块默认是隐藏的,要让这个板块显示出来,我们需要在动作板块先放一个窗口动效事件,或者在编辑器设置中的显示窗口标签栏设置为**总是报告**,这个板块才会显示出来。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
我们可以看到,窗口事件比以前多了很多。而且4个窗口都像房间板块一样每个窗口都占了一行。所有事件都只会对其所在行对应的窗口生效。接下来将会介绍这些事件的作用和使用方法。
|
||||||
|
|
||||||
|
## 隐藏窗口
|
||||||
|
|
||||||
|
窗口的显示和隐藏都在这个事件中,窗口默认只有窗口1是显示,其他窗口默认都是隐藏的,所以如果你需要操作其他窗口,需要把窗口显示出来再进行操作。
|
||||||
|
|
||||||
|
窗口板块中隐藏状态的窗口会显示灰色,可以通过这个判断你的窗口有没有忘记显示出来。
|
||||||
|

|
||||||
|
|
||||||
|
## 窗口动效
|
||||||
|
|
||||||
|
(谁会,来补充一下,我只用过移动2333)
|
||||||
|
|
||||||
|
## 窗口大小重置
|
||||||
|
|
||||||
|
想要让窗口大小发生变化,或调整窗口的轴点,都需要借助**窗口大小重置**事件。这个事件的设置项目是这样的:
|
||||||
|

|
||||||
|
|
||||||
|
**大小:** 窗口的目标大小,最大可以设置为`200%, 200%`,如果`x`和`y`同时被设置为了0,则窗口会立刻被重置为初始的大小(即`100%, 100%`)。
|
||||||
|
|
||||||
|
**缩放模式:** 窗口大小发生变化时内容的缩放模式,具体如下:
|
||||||
|
<ul>
|
||||||
|
<li><strong>填充:</strong>将画面中的内容尽可能的填满窗口</li>
|
||||||
|
<li><strong>适应:</strong>将画面中的内容尽可能放进窗口</li>
|
||||||
|
<li><strong>无:</strong>保持窗口中原来内容的大小不变</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
> 如果你的窗口在大小发生变化前后长宽比不变,那么**填充**和**适应**在效果上没有区别。
|
||||||
|
|
||||||
|
**轴点模式:** 有**默认**和**锚点边缘**两种模式,选择**默认**即和房间移动的轴点没有区别,选择**锚点边缘**则表示选择一条边缘固定不动的情况下进行大小的缩放。
|
||||||
|
|
||||||
|
> 实际上选择锚点边缘的情况下就是由系统计算轴点的位置,因此在锚点边缘选择左或右时不要把`x`设置为`0%`,在锚点边缘选择上或下时不要把`y`设置为`0%`,否则会出现bug,表现为对应窗口会消失且**无法被任何方式**移动回来,即使通过事件恢复了窗口的大小和轴点。
|
||||||
|
|
||||||
|
**轴点:** 设置位置和缩放的基准点,类似于精灵图和房间的轴点,此处不再赘述。
|
||||||
|
|
||||||
|
**动画时长和缓速:** 见过多次了,不再赘述。
|
||||||
|
|
||||||
|
> 如果你想直观地看看各种移动方式的速度区别,可以[看看这里](https://easings.net/zh-cn)。
|
||||||
|
|
||||||
|
### 轴点的使用技巧
|
||||||
|
我们知道,在窗口大小发生变化时,调整轴点可以改变其缩放的中心。但是实际上,即使窗口不需要缩放,我们也可以通过调整轴点来改变窗口的位置,那么这种操作从效果上看和直接使用窗口移动看似没有什么区别,实际上在一些特殊的需求中,这个方法可以被很巧妙地使用。
|
||||||
|
|
||||||
|
窗口在移动时,使用的坐标是相对整个屏幕的,而由于不同设备的屏幕分辨率不同,导致有些效果如果用窗口移动来做会在不同分辨率下有不同效果,尤其是当多个窗口之间的位置有配合的时候(如两个窗口紧贴的效果)。
|
||||||
|
如在16:9的分辨率下制作的两个窗口紧挨着的效果:
|
||||||
|

|
||||||
|
在其他分辨率下,即使不改变窗口的位置,也可能会有不同的效果(如在22:9分辨率下):
|
||||||
|

|
||||||
|
如果想要做出在类似`2-XN`中两个窗口相撞的效果,窗口中间有距离会导致表演效果大打折扣。这时我们可能会想,能不能不以屏幕为基准定位,而是以窗口的大小为基准定位,是不是就可以做到了呢?事实上,**轴点**就解决了这样的问题。
|
||||||
|
试想,如果两个窗口在同一个位置,而一个窗口的轴点在左边缘,另一个窗口在右边缘,就可以保证无论在什么样的分辨率下,这两个窗口都一定是紧挨着的。
|
||||||
|

|
||||||
|

|
||||||
|
所以当你的动画需要多个窗口之间的位置配合时,不妨试试用轴点的移动来代替直接移动窗口。
|
||||||
|
|
||||||
|
## 设置窗口数量
|
||||||
|
|
||||||
|
首先需要纠正一个翻译问题,这个事件的正确翻译应为**设置窗口内容**(英文原文: Set Window Content)。真正可以设置窗口数量的事件是前面提到的**隐藏窗口**事件(显示或者隐藏窗口),请大家不要搞错了。
|
||||||
|
|
||||||
|
这个事件的设置项目是这样的:
|
||||||
|

|
||||||
|
**内容模式:** 可以选择**一个房间**或**全部房间**,在选择**一个房间**时,下面会出现**房间目录**选项,可以从4个房间里面选择一个,随后这个窗口中只会显示这个房间的内容。在选择全部房间时,这个窗口将会像平常一样显示全部4个房间的内容。
|
||||||
|
|
||||||
|
> 注意,在选择**一个房间**时,这个房间中的所有房间事件都不会在这个窗口中生效,包括房间移动,淡入淡出房间,房间遮罩等。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**位置,缩放和角度:** 类似于摄像头移动的对应参数。在内容模式为**一个房间**时,位置是对应房间区域的对应坐标(左下角是`(0%, 0%)`,右上角是`(100%, 100%)`),在选择**全部房间**时,位置是在一个**长宽均为一个房间默认长宽的2倍的大区域**中的位置(如果需要定位到一个处于默认位置和默认大小的房间的左下角,位置是`(25%, 25%)`,右上角则是`(75%,75%)`,正中心依然是`(50%, 50%)`),但是由于窗口默认大小和一个窗口的默认大小是相同的,因此如果不改变窗口的大小,依然只能显示一个房间大小的区域。
|
||||||
|

|
||||||
|
无论选择哪种内容模式,超出区域范围的内容都会被裁剪。
|
||||||
|
|
||||||
|
**时长和缓速:** 略
|
||||||
|
|
||||||
|
## 重新排列窗口
|
||||||
|
类似于房间板块的**重新排列房间**,可以设置窗口的上下关系。
|
||||||
|
|
||||||
|
## 设置主窗口
|
||||||
|
将对应的窗口设置为主窗口,对话,顶部护士灯光,状态牌等内容将在主窗口显示。
|
||||||
|
|
||||||
|
## 设置窗口名称
|
||||||
|
修改窗口的标题。在动作选择**设置**的时候,会直接设置窗口的标题;在动作选择**增补**的时候,会在原来标题的后面增加内容;在动作选择**重置**的时候,会将标题重置为`Rhythm Doctor`。
|
||||||
|
|
||||||
|
## 设置桌面颜色
|
||||||
|
修改模拟移动时桌面的颜色,如果不打开起始颜色,则表示从当前颜色渐变为结束颜色,如果打开起始颜色,效果将类似于**自定义闪烁**。
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
| sndPoliticianHurryUp3 | 政客说:Make a move already, would you? |
|
| sndPoliticianHurryUp3 | 政客说:Make a move already, would you? |
|
||||||
| sndPoliticianHurryUp4 | 政客说:Oh, bloody hell. Hurry up already! |
|
| sndPoliticianHurryUp4 | 政客说:Oh, bloody hell. Hurry up already! |
|
||||||
|
|
||||||
### 在EA版本中出现的音效
|
### 在正式版本中使用的音效
|
||||||
|
|
||||||
| 音效名 | 内容 |
|
| 音效名 | 内容 |
|
||||||
| :--- | :--- |
|
| :--- | :--- |
|
||||||
@@ -160,10 +160,20 @@
|
|||||||
| sndWhistle5 | 口哨声第5拍 |
|
| sndWhistle5 | 口哨声第5拍 |
|
||||||
| sndWhistle7 | 口哨声第7拍 |
|
| sndWhistle7 | 口哨声第7拍 |
|
||||||
| sndApplause | 鼓掌声 |
|
| sndApplause | 鼓掌声 |
|
||||||
|
| sndRowShrink | 轨道缩短声 |
|
||||||
|
| sndSkate1 | <code>MD-3</code>内布若拍子用到的提示音(准备) |
|
||||||
|
| sndSkate2 | <code>MD-3</code>内布若拍子用到的提示音(释放) |
|
||||||
|
| sndSquareshot | 哐啷声 |
|
||||||
|
| sndTriangleshot1 | 三角拍子声(后面的数字可改为1~10) |
|
||||||
|
| sndSleighHi | 圣诞树节拍器 - 高 |
|
||||||
|
| sndSleighLo | 圣诞树节拍器 - 低 |
|
||||||
| sndCalibrationBeep | 校准界面哔声 |
|
| sndCalibrationBeep | 校准界面哔声 |
|
||||||
| sndMelonMorse | Melon 摩尔斯电码 |
|
| sndMelonMorse | Melon 摩尔斯电码 |
|
||||||
|
| sndVividStasisResultsRiser | <code>X-FTS</code>恋恋群星S+评价前摇 |
|
||||||
|
| sndVividStasisScratch | <code>X-FTS</code>恋恋群星V评价出现 |
|
||||||
|
| sndWingFlap | 鸟儿扇动翅膀 |
|
||||||
|
|
||||||
### 未使用的音效
|
### 未在正式版本中使用的音效
|
||||||
|
|
||||||
| 音效名 | 内容 |
|
| 音效名 | 内容 |
|
||||||
| :--- | :--- |
|
| :--- | :--- |
|
||||||
@@ -245,7 +255,7 @@
|
|||||||
| sndLowHealthCSharpA | 低血量警告音 CA# |
|
| sndLowHealthCSharpA | 低血量警告音 CA# |
|
||||||
| sndLowHealthE | 低血量警告音 E |
|
| sndLowHealthE | 低血量警告音 E |
|
||||||
|
|
||||||
### 在EA版本中出现的音乐
|
### 在正式版本中出现的音乐
|
||||||
|
|
||||||
| 音效名 | 内容 | 曲速 |
|
| 音效名 | 内容 | 曲速 |
|
||||||
| :--- | :--- | :--- |
|
| :--- | :--- | :--- |
|
||||||
@@ -286,6 +296,7 @@
|
|||||||
| sndOrientalTechnodown | <code>1-1N</code>东方回响贝斯的开头部分 | |
|
| sndOrientalTechnodown | <code>1-1N</code>东方回响贝斯的开头部分 | |
|
||||||
| sndOrientalDubstep | <code>1-1N</code>东方回响贝斯 | 130 |
|
| sndOrientalDubstep | <code>1-1N</code>东方回响贝斯 | 130 |
|
||||||
| sndIntimate | <code>1-2</code>亲密 | 90 |
|
| sndIntimate | <code>1-2</code>亲密 | 90 |
|
||||||
|
| sndIntimate2P | 在双人模式下的<code>1-2N</code>亲密 | 90 |
|
||||||
| sndOrientalInsomniac | <code>1-X</code>东方不眠夜 | 180 |
|
| sndOrientalInsomniac | <code>1-X</code>东方不眠夜 | 180 |
|
||||||
| sndOrientalInsomniac1 | <code>1-X</code> 东方不眠夜 第一部分 | |
|
| sndOrientalInsomniac1 | <code>1-X</code> 东方不眠夜 第一部分 | |
|
||||||
| sndOrientalInsomniac2 | <code>1-X</code> 东方不眠夜 第二部分 | |
|
| sndOrientalInsomniac2 | <code>1-X</code> 东方不眠夜 第二部分 | |
|
||||||
@@ -294,8 +305,8 @@
|
|||||||
| sndGongXi | <code>1-CNY</code>春节贺岁片 | 145 |
|
| sndGongXi | <code>1-CNY</code>春节贺岁片 | 145 |
|
||||||
| sndHalloween | <code>1-BOO</code>恐怖鸟主题 | 100 |
|
| sndHalloween | <code>1-BOO</code>恐怖鸟主题 | 100 |
|
||||||
| sndLofi | <code>2-1</code>Lo-fi Hip-Hop Beats To Treat Patients To | 75 |
|
| sndLofi | <code>2-1</code>Lo-fi Hip-Hop Beats To Treat Patients To | 75 |
|
||||||
| sndCareless | <code>2-1N</code>wish i could care less | 75~81 |
|
| sndCareless | <code>2-1N</code>wish i could care less | 75->81 |
|
||||||
| sndCarelessCN | <code>2-1N</code>wish i could care less中文版 | 75~81 |
|
| sndCarelessCN | <code>2-1N</code>wish i could care less中文版 | 75->81 |
|
||||||
| sndSupraventricular88 | <code>2-2</code>室上性心动过速 | 88 |
|
| sndSupraventricular88 | <code>2-2</code>室上性心动过速 | 88 |
|
||||||
| sndUnreachable | <code>2-2N</code>Unreachable | 180 |
|
| sndUnreachable | <code>2-2N</code>Unreachable | 180 |
|
||||||
| sndPuffPiece | <code>2-3</code>缭绕 | 127 |
|
| sndPuffPiece | <code>2-3</code>缭绕 | 127 |
|
||||||
@@ -303,8 +314,8 @@
|
|||||||
| song_of_the_sea | <code>2-4</code>与<code>2-4N</code>的背景音乐 | 60 |
|
| song_of_the_sea | <code>2-4</code>与<code>2-4N</code>的背景音乐 | 60 |
|
||||||
| sndAllTheTimes | <code>2-X</code>All The Times 咖啡之歌 | 145 |
|
| sndAllTheTimes | <code>2-X</code>All The Times 咖啡之歌 | 145 |
|
||||||
| sndAllTheTimesCN | <code>2-X</code>All The Times 咖啡之歌中文版 | 145 |
|
| sndAllTheTimesCN | <code>2-X</code>All The Times 咖啡之歌中文版 | 145 |
|
||||||
| sndBitterness | <code>2-XN</code>Bitter Times 爱情是苦和甜的交错前半段 | 112~195 |
|
| sndBitterness | <code>2-XN</code>Bitter Times 爱情是苦和甜的交错前半段 | 112->196-->231->255-->196 |
|
||||||
| sndBitternessCutscene | <code>2-XN</code>Bitter Times 爱情是苦和甜的交错后半段 | 112->195->264->195 |
|
| sndBitternessCutscene | <code>2-XN</code>Bitter Times 爱情是苦和甜的交错后半段 | |
|
||||||
| sndTutorialBeans | <code>2-B1</code>武士豆子跳 | 110 |
|
| sndTutorialBeans | <code>2-B1</code>武士豆子跳 | 110 |
|
||||||
| sndSleepyGarden | <code>3-1</code>沉睡花园 | 158 |
|
| sndSleepyGarden | <code>3-1</code>沉睡花园 | 158 |
|
||||||
| sndLounge | <code>3-1N</code>休息室 | 75 |
|
| sndLounge | <code>3-1N</code>休息室 | 75 |
|
||||||
@@ -313,26 +324,27 @@
|
|||||||
| sndDistantDuetNight | <code>3-3</code>分离二重唱·夜 | 194 |
|
| sndDistantDuetNight | <code>3-3</code>分离二重唱·夜 | 194 |
|
||||||
| sndLesMisBar1 | <code>3-X</code>轮班之歌 | 100 |
|
| sndLesMisBar1 | <code>3-X</code>轮班之歌 | 100 |
|
||||||
| sndLesMisBar39 | <code>3-X</code>轮班之歌 从第39小节开始 | |
|
| sndLesMisBar39 | <code>3-X</code>轮班之歌 从第39小节开始 | |
|
||||||
| sndMinersong | <code>4-1</code>实习医生培训演练表现评估 | 128->225.9->140~110? |
|
| sndLesmisDog | 节奏狗生 | 100 |
|
||||||
|
| sndMinersong | <code>4-1</code>实习医生培训演练表现评估 | 128 |
|
||||||
| sndRollerdisco | <code>4-1N</code>滑板迪斯科震颤 | 125.5 |
|
| sndRollerdisco | <code>4-1N</code>滑板迪斯科震颤 | 125.5 |
|
||||||
| sndInvisible | <code>4-2</code>和<code>4-2N</code> | 190 |
|
| sndInvisible | <code>4-2</code>和<code>4-2N</code> | 190 |
|
||||||
| sndSteinway | <code>4-3</code>斯坦威 | 80 |
|
| sndSteinway | <code>4-3</code>斯坦威 | 80 |
|
||||||
| sndKnowYou | <code>4-4</code>懂你 | 157 |
|
| sndKnowYou | <code>4-4</code>懂你 | 157 |
|
||||||
| sndMurmurs | <code>4-4N</code>喃喃细语 | 65 |
|
| sndMurmurs | <code>4-4N</code>喃喃细语 | 65 |
|
||||||
| sndLuckyBreak | <code>5-1</code>路吉摔了一跤 | 156 |
|
| sndLuckyBreak | <code>5-1</code>路吉摔了一跤 | 156 |
|
||||||
| sndOSTL | <code>5-1N</code>One Slip Too Late 创伤之歌 | 175~150 |
|
| sndOSTL | <code>5-1N</code>One Slip Too Late 创伤之歌 | 175->150->175 |
|
||||||
| sndOSTLCN | <code>5-1N</code>One Slip Too Late 创伤之歌中文版 | 175~150 |
|
| sndOSTLCN | <code>5-1N</code>One Slip Too Late 创伤之歌中文版 | 175->150->175 |
|
||||||
| sndFreezeshot | <code>5-2</code>Lo-fi Beats For Patients To Chill To | 140 |
|
| sndFreezeshot | <code>5-2</code>Lo-fi Beats For Patients To Chill To | 140 |
|
||||||
| sndFreezeshotH | <code>5-2N</code>无以为继的伤心欲绝 | 176->100->80 |
|
| sndFreezeshotH | <code>5-2N</code>无以为继的伤心欲绝 | 176->100->80 |
|
||||||
| sndTherapy1 | <code>5-3</code>七局伸展操第一部分 | 128 |
|
| sndTherapy1 | <code>5-3</code>七局伸展操第一部分 | 128 |
|
||||||
| sndTherapy2 | <code>5-3</code>七局伸展操第二部分 | 124 |
|
| sndTherapy2 | <code>5-3</code>七局伸展操第二部分 | 124 |
|
||||||
| sndTherapy3 | <code>5-3</code>七局伸展操第三部分 | 136 |
|
| sndTherapy3 | <code>5-3</code>七局伸展操第三部分 | 136 |
|
||||||
| sndTherapyEnding | <code>5-3</code>七局伸展操结尾 | 136 |
|
| sndTherapyEnding | <code>5-3</code>七局伸展操结尾 | 136 |
|
||||||
| sndCorazonesViejos | <code>5-3N</code>偕老 | 249? |
|
| sndCorazonesViejos | <code>5-3N</code>偕老 | 248 |
|
||||||
| sndDDS | <code>5-X</code>梦想永不歇 | 95->62.6->102.5 |
|
| sndDDS | <code>5-X</code>梦想永不歇 | 95->62.6->102.5 |
|
||||||
| sndDDSCN | <code>5-X</code>梦想永不歇中文版 | 95->62.6->102.5 |
|
| sndDDSCN | <code>5-X</code>梦想永不歇中文版 | 95->62.6->102.5 |
|
||||||
| sndHaileyDuet | <code>6-1</code>想把心事和你说 | 94.5->190->220->181->206? |
|
| sndHaileyDuet | <code>6-1</code>想把心事和你说 | 94.5-->190-->200->210-->181->198? |
|
||||||
| sndHaileyDuetCN | <code>6-1</code>想把心事和你说中文版 | 94.5->190->220->181->206? |
|
| sndHaileyDuetCN | <code>6-1</code>想把心事和你说中文版 | 94.5-->190-->200->210-->181->198? |
|
||||||
| sndEdegaRave | <code>6-2</code>欢迎回来 | 200->85->200 |
|
| sndEdegaRave | <code>6-2</code>欢迎回来 | 200->85->200 |
|
||||||
| sndBossStage_Main | <code>6-X</code>Boss战(包括埃德加批评佩奇的一段) | 150~255 |
|
| sndBossStage_Main | <code>6-X</code>Boss战(包括埃德加批评佩奇的一段) | 150~255 |
|
||||||
| sndBossStage_NoCutscene | <code>6-X</code>Boss战(不包括埃德加批评佩奇的一段) | 150~255 |
|
| sndBossStage_NoCutscene | <code>6-X</code>Boss战(不包括埃德加批评佩奇的一段) | 150~255 |
|
||||||
@@ -353,10 +365,21 @@
|
|||||||
| sndWornOutTapes | <code>X-WOT</code>Worn Out Tapes | 149 |
|
| sndWornOutTapes | <code>X-WOT</code>Worn Out Tapes | 149 |
|
||||||
| sndSparkLine | <code>X-KOB</code>气球王国 | 124 |
|
| sndSparkLine | <code>X-KOB</code>气球王国 | 124 |
|
||||||
| sndVividStasis | <code>X-FTS</code>恋恋群星 | 150 |
|
| sndVividStasis | <code>X-FTS</code>恋恋群星 | 150 |
|
||||||
|
| sndVividStasisResults | <code>X-FTS</code>恋恋群星V评价结算音乐 | 150 |
|
||||||
| sndOsmBluesRadio | 夜间的病房内过剧情 | 98? |
|
| sndOsmBluesRadio | 夜间的病房内过剧情 | 98? |
|
||||||
|
| sndOsmBluesNormal | 袖管涂鸦背景音乐 | 98? |
|
||||||
|
| sndInsomniacCutscene | 1-X后过剧情 | 140 |
|
||||||
| SoftPianoIntimate | 第一章末尾的过剧情 | 88 |
|
| SoftPianoIntimate | 第一章末尾的过剧情 | 88 |
|
||||||
|
| sndDistantDuetCutsceneLoop | 3-3后过剧情 | 165 |
|
||||||
|
| sndDDSPianoCutsceneLoop | 5-X后过剧情第一部分 | 130 |
|
||||||
|
| sndDDSEdegaCutscene | 5-X后过剧情第一部分(埃德加来) | 140 |
|
||||||
|
| sndDDSVirusTalkCutsceneLoop | 5-X后过剧情(伊恩来) | 105 |
|
||||||
|
| sndMarksTheme | 病历间音乐 | 110 |
|
||||||
|
| sndBlurredCutscene | 7-1后过剧情 | 63? |
|
||||||
|
| sndPaigeReturnsCutscene | 佩奇回来 | 157? |
|
||||||
|
| sndEdegaBedsidePaigeCutscene | 7-X2后埃德加住院 | 58? |
|
||||||
|
|
||||||
### 未使用的音乐
|
### 未在正式版本中出现的音乐
|
||||||
|
|
||||||
| 音效名 | 内容 | 曲速 |
|
| 音效名 | 内容 | 曲速 |
|
||||||
| :--- | :--- | :--- |
|
| :--- | :--- | :--- |
|
||||||
@@ -368,4 +391,5 @@
|
|||||||
| sndChopin | Op.10 No.4 | |
|
| sndChopin | Op.10 No.4 | |
|
||||||
| sndOrientalInsomniacHardRepOld | 超东方不眠夜(旧版) | |
|
| sndOrientalInsomniacHardRepOld | 超东方不眠夜(旧版) | |
|
||||||
| sndGreenlightInsomniac | 超东方不眠夜(预告片版) | |
|
| sndGreenlightInsomniac | 超东方不眠夜(预告片版) | |
|
||||||
| sndOpeningCredits | |
|
| sndLanterns | | 150 |
|
||||||
|
| sndOpeningCredits | | 187.9 |
|
||||||
|
|||||||
@@ -98,6 +98,12 @@
|
|||||||
|
|
||||||
在`5-2N 无以为继的伤心欲绝`中被首次引入。
|
在`5-2N 无以为继的伤心欲绝`中被首次引入。
|
||||||
|
|
||||||
|
### 长按单发拍子
|
||||||
|
|
||||||
|
当听到咻噗的声音时,说明下一次击拍需要长按到下一个出拍点。
|
||||||
|
|
||||||
|
在`7-1 模糊`中被首次引入。
|
||||||
|
|
||||||
### 冲击拍子
|
### 冲击拍子
|
||||||
|
|
||||||
单发拍子根据哨声提示在脉冲和按拍时全都按拍。
|
单发拍子根据哨声提示在脉冲和按拍时全都按拍。
|
||||||
|
|||||||
@@ -20,4 +20,74 @@ g 1 1 5 4 #0ff{
|
|||||||
};
|
};
|
||||||
</textarea></div></div>
|
</textarea></div></div>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
完整功能支持请前往[此处](https://view.obugs.cn)。
|
||||||
|
|
||||||
|
<div class="rdview2"><div class="canvas"></div><div class="editor"><div class="number"></div><textarea class="area" spellcheck="false" placeholder="Enter code here">
|
||||||
|
{ "bar": 1, "beat": 1, "y": 0, "type": "MoveRoom", "if": "1d0", "roomPosition": [50, 50], "scale": [70, 100], "angle": 20, "pivot": [0, 0], "duration": 1, "ease": "Linear" },
|
||||||
|
{ "bar": 1, "beat": 1, "y": 6, "type": "AddOneshotBeat", "pulseType": "Triangle", "tick": 2, "subdivisions": 2, "subdivSound": true },
|
||||||
|
{ "bar": 1, "beat": 2, "y": 0, "type": "AddClassicBeat", "tick": 0.5, "swing": 0, "hold": 0 },
|
||||||
|
{ "bar": 1, "beat": 2, "y": 1, "type": "ReorderRooms", "order": [1, 3, 2, 0] },
|
||||||
|
{ "bar": 1, "beat": 2, "y": 5, "type": "SetSpeed", "speed": 1, "duration": 0, "ease": "Linear" },
|
||||||
|
{ "bar": 1, "beat": 3, "y": 3, "type": "AddOneshotBeat", "pulseType": "Wave", "tick": 2 },
|
||||||
|
{ "bar": 1, "beat": 4, "y": 4, "type": "AddOneshotBeat", "pulseType": "Wave", "freezeBurnMode": "Burnshot", "interval": 2, "tick": 1 },
|
||||||
|
{ "bar": 1, "beat": 5, "y": 1, "type": "SetBeatsPerMinute", "if": "~3d0", "beatsPerMinute": 100 },
|
||||||
|
{ "bar": 1, "beat": 5, "y": 2, "type": "SetBackgroundColor", "rooms": [0], "backgroundType": "Color", "color": "FFFFFFFF", "filter": "NearestNeighbor" },
|
||||||
|
{ "bar": 1, "beat": 5, "y": 5, "type": "SetGameSound", "soundType": "SmallMistake", "filename": "" },
|
||||||
|
{ "bar": 1, "beat": 5, "y": 6, "type": "SetHeartExplodeInterval", "intervalType": "GatherAndCeil", "interval": 1 },
|
||||||
|
{ "bar": 1, "beat": 7, "y": 0, "type": "AddClassicBeat", "tag": "1", "tick": 0.5, "hold": 1, "swing": 0.5, "setXs": "NoChange" },
|
||||||
|
{ "bar": 1, "beat": 7, "y": 6, "type": "AddOneshotBeat", "pulseType": "Wave", "freezeBurnMode": "Freezeshot", "interval": 2, "tick": 1, "delay": 0.5, "skipshot": true },
|
||||||
|
{ "bar": 1, "beat": 8, "y": 1, "type": "SetVisible", "visible": true },
|
||||||
|
{ "bar": 1, "beat": 8, "y": 2, "type": "ShowStatusSign", "if": "1d0", "text": "RhythmBase.View!", "duration": 5, "useBeats": true, "narrate": true },
|
||||||
|
{ "bar": 1, "beat": 8, "y": 3, "type": "Tint", "border": "Glow", "borderColor": "FF0000FF", "tint": true, "tintColor": "001BFFFF", "opacity": 100, "duration": 3, "ease": "Linear" },
|
||||||
|
{ "bar": 1, "beat": 8, "y": 4, "type": "SetCountingSound", "voiceSource": "JyiCount", "enabled": true, "volume": 100, "subdivOffset": 0.5 },
|
||||||
|
{ "bar": 1, "beat": 8, "y": 5, "type": "TextExplosion", "rooms": [0], "text": "Explosion", "color": "000000", "mode": "OneColor", "direction": "Left", "speed": 100, "ease": "Linear" },
|
||||||
|
{ "bar": 2, "beat": 2, "y": 5, "type": "SayReadyGetSetGo", "tag": "1", "rooms": [4], "phraseToSay": "JustSayRea", "voiceSource": "Nurse", "tick": 1, "volume": 100 },
|
||||||
|
{ "bar": 2, "beat": 3, "y": 1, "type": "Move", "position": [50, 50], "duration": 1, "ease": "Linear" },
|
||||||
|
{ "bar": 2, "beat": 3, "y": 2, "type": "NewWindowDance", "tab": "Windows", "preset": "Move", "position": [50, 50], "reference": "Center", "easingDuration": 1, "ease": "Linear" },
|
||||||
|
{ "bar": 2, "beat": 3, "y": 3, "type": "PlayAnimation", "expression": "neutral" },
|
||||||
|
{ "bar": 2, "beat": 3, "y": 4, "type": "SetOneshotWave", "waveType": "BoomAndRush", "height": 100, "width": 100 },
|
||||||
|
{ "bar": 2, "beat": 5, "y": 0, "type": "BassDrop", "if": "1&~2d0", "rooms": [0], "strength": "High" },
|
||||||
|
{ "bar": 2, "beat": 5, "y": 1, "type": "ShowDialogue", "rooms": [4], "text": "Samurai_Happy: SAMURAI!", "speed": 1, "panelSide": "Bottom", "portraitSide": "Left", "playTextSounds": true },
|
||||||
|
{ "bar": 2, "beat": 5, "y": 2, "type": "FloatingText", "rooms": [0], "id": 0, "text": "等/呀/等/得/好/心/慌……", "times": "", "color": "00F4FFFF", "outlineColor": "0011FFFF", "textPosition": [50, 50], "font": "Default", "size": 8, "angle": 0, "showChildren": true, "fadeOutRate": 3, "mode": "FadeOut", "anchor": "MiddleCenter", "narrate": true, "narrationCategory": "Subtitles" },
|
||||||
|
{ "bar": 2, "beat": 6, "y": 3, "type": "SetRoomPerspective", "cornerPositions": [[0, 0], [100, 0], [0, 100], [100, 100]], "duration": 1, "ease": "Linear" },
|
||||||
|
{ "bar": 2, "beat": 6, "y": 4, "type": "SetPlayStyle", "PlayStyle": "Normal", "NextBar": 1, "Relative": true },
|
||||||
|
{ "bar": 2, "beat": 7, "y": 5, "type": "SetCrotchetsPerBar", "crotchetsPerBar": 7, "visualBeatMultiplier": 1 },
|
||||||
|
{ "bar": 2, "beat": 7, "y": 6, "type": "SetHandOwner", "rooms": [0], "hand": "Right", "character": "Player" },
|
||||||
|
{ "bar": 2, "beat": 8, "y": 3, "type": "SetBeatSound", "row": 0, "sound": {"filename": "Shaker"} },
|
||||||
|
{ "bar": 2, "beat": 8, "y": 4, "type": "TintRows", "if" : "1&~2d1", "rooms": [0], "border": "Outline", "borderColor": "2BFF00FF", "tint": true, "tintColor": "007E0EFF", "opacity": 100, "duration": 0, "ease": "Linear", "effect": "None" },
|
||||||
|
{ "bar": 3, "beat": 1, "y": 0, "type": "MoveCamera", "rooms": [0], "cameraPosition": [50, 50], "duration": 1, "ease": "Linear" },
|
||||||
|
{ "bar": 3, "beat": 1, "y": 1, "type": "ShowHands", "rooms": [0], "hand": "Right", "action": "Show", "extent": "Full", "forceRaise": true, "align": false, "instant": false },
|
||||||
|
{ "bar": 3, "beat": 1, "y": 2, "type": "PulseCamera", "rooms": [0], "strength": 1, "count": 1, "frequency": 1 },
|
||||||
|
{ "bar": 3, "beat": 3, "y": 2, "type": "SayReadyGetSetGo", "if": "1d0", "rooms": [4], "phraseToSay": "SayGetSetOne", "voiceSource": "Nurse", "tick": 1, "volume": 100 },
|
||||||
|
{ "bar": 3, "beat": 3, "y": 6, "type": "FlipScreen", "rooms": [0], "flipX": false, "flipY": true },
|
||||||
|
{ "bar": 3, "beat": 4, "y": 6, "type": "InvertColors", "rooms": [0], "enable": true },
|
||||||
|
{ "bar": 3, "beat": 5, "y": 3, "type": "DesktopColor", "endColor": "00ffff", "duration": 2, "ease": "Linear" },
|
||||||
|
{ "bar": 3, "beat": 6, "y": 6, "type": "SayReadyGetSetGo", "rooms": [4], "phraseToSay": "JustSayAndStop", "voiceSource": "Nurse", "tick": 1, "volume": 100 },
|
||||||
|
{ "bar": 3, "beat": 5, "y": -1, "type": "AdvanceText", "id": 0, "text": "text 字段不存在于原事件中,但是 RDView 提供了支持。" },
|
||||||
|
{ "bar": 3, "beat": 5.5, "y": -1, "type": "PulseFreeTimeBeat", "row": 0, "action": "Increment", "hold": 0 },
|
||||||
|
{ "bar": 3, "beat": 8, "y": 3, "type": "HideWindow", "show": false },
|
||||||
|
{ "bar": 3, "beat": 8, "y": 4, "type": "NarrateRowInfo", "row": 0, "infoType": "Connect", "soundOnly": false, "narrateSkipBeats": "On", "skipsUnstable": false, "customPlayer": "AutoDetect" },
|
||||||
|
{ "bar": 3, "beat": 8, "y": 5, "type": "Comment", "tab": "Actions", "text": "五彩斑斓的黑", "color": "8C00FF", "show": false },
|
||||||
|
{ "bar": 4, "beat": 1, "y": 2, "type": "PaintHands", "rooms": [0], "hands": "Right", "border": "Outline", "borderColor": "AFFF00FF", "tint": true, "tintColor": "4FAF00FF", "opacity": 100, "duration": 0 },
|
||||||
|
{ "bar": 4, "beat": 1, "y": 4, "type": "CallCustomMethod", "methodName": "", "executionTime": "OnBar", "sortOffset": 0 },
|
||||||
|
{ "bar": 4, "beat": 1, "y": 6, "type": "SetWindowContent", "contentMode": "OnTop" },
|
||||||
|
{ "bar": 4, "beat": 2, "y": 2, "type": "FadeRoom", "opacity": 100, "duration": 2, "ease": "Linear" },
|
||||||
|
{ "bar": 4, "beat": 2, "y": 4, "type": "PlaySong", "song": {"filename": "sndOrientalTechno"}, "bpm": 100, "loop": false },
|
||||||
|
{ "bar": 4, "beat": 2, "y": 6, "type": "SetRowXs", "pattern": "-x--x-", "syncoBeat": 3, "syncoSwing": 0, "syncoVolume": 70, "syncoPitch": 100, "syncoStyle": "Chirp", "syncoPlayModifierSound": true, "syncoPlayModifierOffSound": true },
|
||||||
|
{ "bar": 4, "beat": 3, "y": 3, "type": "ChangeCharacter", "row": 0, "character": "Samurai", "transition": "Instant" },
|
||||||
|
{ "bar": 4, "beat": 5, "y": 2, "type": "ShowRooms", "rooms": [1, 3], "heights": [0, 0, 0, 0], "transitionTime": 1, "ease": "InOutSine" },
|
||||||
|
{ "bar": 4, "beat": 6, "y": 6, "type": "RenameWindow", "action": "Set", "text": "Minecraft 1.21.1 NeoForge" },
|
||||||
|
{ "bar": 4, "beat": 7, "y": 2, "type": "SetVFXPreset", "rooms": [0], "preset": "Vignette", "enable": true },
|
||||||
|
{ "bar": 4, "beat": 7, "y": 3, "type": "PlaySound", "sound": {"filename": "Shaker"}, "customSoundType": "CueSound" },
|
||||||
|
{ "bar": 4, "beat": 7, "y": 4, "type": "SetTheme", "rooms": [0], "preset": "None", "skipPaintEffects": false },
|
||||||
|
{ "bar": 4, "beat": 7, "y": 5, "type": "SetRoomContentMode", "mode": "Center" },
|
||||||
|
{ "bar": 4, "beat": 8, "y": 6, "type": "TagAction", "Action": "Run", "Tag": "Nothing" },
|
||||||
|
{ "bar": 5, "beat": 1, "y": 2, "type": "PulseCamera", "rooms": [0], "strength": 1, "count": 1, "frequency": 1 },
|
||||||
|
{ "bar": 5, "beat": 1, "y": 3, "type": "SetClapSounds", "rowType": "Classic", "p1Sound": {"filename": "ClapHit"} },
|
||||||
|
{ "bar": 5, "beat": 1, "y": 4, "type": "ChangePlayersRows", "playerMode": "OneOrTwoPlayers", "players": ["NoChange", "NoChange", "NoChange", "NoChange", "NoChange", "NoChange", "NoChange", "NoChange", "NoChange", "NoChange", "NoChange", "NoChange", "NoChange", "NoChange", "NoChange", "NoChange"], "cpuMarkers": ["Otto", "Otto", "Otto", "Otto", "Otto", "Otto", "Otto", "Otto", "Otto", "Otto", "Otto", "Otto", "Otto", "Otto", "Otto", "Otto"] },
|
||||||
|
{ "bar": 5, "beat": 1, "y": 5, "type": "ReadNarration", "text": "一名身穿红色盔甲的武士跟着音乐节拍点头。", "category": "Description" }
|
||||||
|
</textarea></div></div>
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# 修改游戏版本
|
# 修改游戏配置
|
||||||
|
|
||||||
## 说明
|
## 说明
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
最后说明的是,不论你使用的是哪个版本,由于近期官方对编辑器的更新极为频繁,请务必将游戏更新设置为**始终保持此游戏为最新**,否则真的是……脑壳痛。
|
最后说明的是,不论你使用的是哪个版本,由于近期官方对编辑器的更新极为频繁,请务必将游戏更新设置为**始终保持此游戏为最新**,否则真的是……脑壳痛。
|
||||||
|
|
||||||
## 进入方式
|
## 修改游戏版本
|
||||||
|
|
||||||
接下来介绍进入Beta版本的方法:
|
接下来介绍进入Beta版本的方法:
|
||||||
|
|
||||||
@@ -31,4 +31,20 @@
|
|||||||
|
|
||||||
第四步,直接打开游戏即可。
|
第四步,直接打开游戏即可。
|
||||||
|
|
||||||
> 一切准备就绪,那么开始我们的节奏医生编辑器中文教程吧!
|
> 一切准备就绪,那么开始我们的节奏医生编辑器中文教程吧!
|
||||||
|
|
||||||
|
## 启用高级编辑器功能
|
||||||
|
|
||||||
|
从 v1.0.4 版本开始,高级编辑器功能被加入。其中包含一些在剧情关卡中使用,但之前不能在编辑器直接使用的功能。
|
||||||
|
|
||||||
|
要启用高级编辑器功能,首先按住 `tab`,然后你就会看到这个界面:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
然后狂点右下角的凹兔,直到弹出这个窗口:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
点击接受,此时高级编辑器功能就启用了。
|
||||||
|
|
||||||
|
如果你想关闭高级编辑器功能,只需要点一下 `tab` 界面右下角的凹兔即可。
|
||||||
@@ -1,5 +1,16 @@
|
|||||||
# 更新信息
|
# 更新信息
|
||||||
|
|
||||||
|
#### 2026-01-24
|
||||||
|
- 制作人员表增加了
|
||||||
|
|
||||||
|
#### 2026-01-16
|
||||||
|
- RDTKView(~~RDView2~~) 已完成部署
|
||||||
|
|
||||||
|
#### 2026-01-14
|
||||||
|
- 制作人员表增加了
|
||||||
|
- RDView 对正式版编辑器新特性的部分适配
|
||||||
|
- 正在逐步同步 RDView2(RhythmBase.View) 的适配
|
||||||
|
|
||||||
#### 2025-11-29
|
#### 2025-11-29
|
||||||
- 调整目录结构
|
- 调整目录结构
|
||||||
- 双人模式措辞更改
|
- 双人模式措辞更改
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
第二个选项“**摇摆**”就是剧情模式里老奶奶那样的摇摆普通拍子,可以使用拖动条来调节,对照下方绿色竖线的移动可以很直观地看到摇摆效果。不过拖动滑条你只能得到一些倍数十分规整的摇摆拍子,例如摇摆值为0.25,0.5,0.75等,如果你的音乐里真的有些更为不同寻常的摇摆幅度,那就要直接在框里填写数字了。
|
第二个选项“**摇摆**”就是剧情模式里老奶奶那样的摇摆普通拍子,可以使用拖动条来调节,对照下方绿色竖线的移动可以很直观地看到摇摆效果。不过拖动滑条你只能得到一些倍数十分规整的摇摆拍子,例如摇摆值为0.25,0.5,0.75等,如果你的音乐里真的有些更为不同寻常的摇摆幅度,那就要直接在框里填写数字了。
|
||||||
|
|
||||||
```rdview2 8 9
|
```rdtkview 8 9
|
||||||
{"beat": 2, "y": 1, "type": "AddClassicBeat", "row": 0, "tick": 1, "swing": 0.25, "hold": 0 },
|
{"beat": 2, "y": 1, "type": "AddClassicBeat", "row": 0, "tick": 1, "swing": 0.25, "hold": 0 },
|
||||||
{"beat": 2, "y": 2, "type": "AddClassicBeat", "row": 0, "tick": 1, "swing": 0.5, "hold": 0 },
|
{"beat": 2, "y": 2, "type": "AddClassicBeat", "row": 0, "tick": 1, "swing": 0.5, "hold": 0 },
|
||||||
{"beat": 2, "y": 3, "type": "AddClassicBeat", "row": 0, "tick": 1, "swing": 0.75, "hold": 0 },
|
{"beat": 2, "y": 3, "type": "AddClassicBeat", "row": 0, "tick": 1, "swing": 0.75, "hold": 0 },
|
||||||
@@ -36,6 +36,8 @@
|
|||||||
|
|
||||||
> 尽量不要在第一拍设置`X`记号,这对纯音押玩家是不可预知的,在轨道数量多的情况下也会显得比较难理清节奏。如果你觉得你的音乐中这段节奏第一拍不放`X`就浑身难受,那也需要打开护士数拍(参考`X-WOT`这关中的`xx-----`)。
|
> 尽量不要在第一拍设置`X`记号,这对纯音押玩家是不可预知的,在轨道数量多的情况下也会显得比较难理清节奏。如果你觉得你的音乐中这段节奏第一拍不放`X`就浑身难受,那也需要打开护士数拍(参考`X-WOT`这关中的`xx-----`)。
|
||||||
|
|
||||||
|
> 此外像马克的短轨道,需调用自定义方法来完成,语句为`SetRowLength(轨道编号[0索引的], 轨道长度[1~7的整数])`。如果你还不明白自定义方法是什么,需要先翻到[自定义方法](../pages/custommethod.md)看看。考虑到这玩意问的人比较多,所以就先写在此处了。
|
||||||
|
|
||||||
最后的选项“**分解为自由时长节拍**”较为复杂,我们放到[这里](/pages/moreclassicbeats)再介绍。
|
最后的选项“**分解为自由时长节拍**”较为复杂,我们放到[这里](/pages/moreclassicbeats)再介绍。
|
||||||
|
|
||||||
## 护士节奏提示
|
## 护士节奏提示
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
- [NoMathExpectation](https://space.bilibili.com/184862524)
|
- [NoMathExpectation](https://space.bilibili.com/184862524)
|
||||||
- [0x4D2](https://space.bilibili.com/478595264)
|
- [0x4D2](https://space.bilibili.com/478595264)
|
||||||
- [正宗的小绿君](https://space.bilibili.com/1782518253)
|
- [正宗的小绿君](https://space.bilibili.com/1782518253)
|
||||||
|
- [kuanpan](https://space.bilibili.com/1928620300)
|
||||||
- 视频制作:
|
- 视频制作:
|
||||||
- [吾夜犹明](https://space.bilibili.com/29219948)
|
- [吾夜犹明](https://space.bilibili.com/29219948)
|
||||||
- [山不转水到渠成](https://space.bilibili.com/4870582)
|
- [山不转水到渠成](https://space.bilibili.com/4870582)
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ es.SayReadyGetSetGo_Go 3 1[];
|
|||||||
|
|
||||||
与方块单发拍的概念类似,**细分拍**也是“一次性”的单发拍,但是会产生多次等间距的心跳脉冲,按键的次数也变为多次。
|
与方块单发拍的概念类似,**细分拍**也是“一次性”的单发拍,但是会产生多次等间距的心跳脉冲,按键的次数也变为多次。
|
||||||
|
|
||||||
细分拍最早出现于`MD-1`,当时官方谱面没有官方名称,且最多仅有两个三角拍;后来第五章更新后三角拍首次出现在`5-1`并正式更名为**细分拍**,并出现了多个细分。
|
细分拍最早出现于`MD-1`,当时官方谱面没有官方名称,且最多仅有两个三角拍;后来第五章更新后三角拍首次出现在`5-1`并正式更名为**细分拍**,并出现了多个细分;在关卡`5-2N`中最多涉及到了五级细分;正式版更新后,在关卡`7-X2`中甚至有了八细分。
|
||||||
|
|
||||||
在修饰符中选择方块后,将细分数量设为大于 1 的值或者点击细分符号`△`,即可创建细分拍。特殊音效的添加与方块单发拍类似。最多可以将细分数量设置为 10 。
|
在修饰符中选择方块后,将细分数量设为大于 1 的值或者点击细分符号`△`,即可创建细分拍。特殊音效的添加与方块单发拍类似。最多可以将细分数量设置为 10 。
|
||||||
|
|
||||||
@@ -338,6 +338,52 @@ g 0 -1 3 2 #0000[offset=0 1]{
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 长按单发拍
|
||||||
|
|
||||||
|
长按单发拍第一次出现在`7-1`
|
||||||
|
|
||||||
|
当听到`咻噗`的声音时,说明下一次击拍需要长按到下一个出拍点。
|
||||||
|
|
||||||
|
在修饰符中选中进度条符号就创建了一个长按单发拍,会多出`间隔`与`长按提示选项`选项。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
对于长按单发拍,一共有四个关键的时间点:十字,出拍点(绿线),按拍点(黄线),长按(紫色格子)。
|
||||||
|
```rdtkview 5 3
|
||||||
|
{ "bar": 1, "beat": 3, "y": 2, "type": "AddOneshotBeat","interval": 2, "tick": 1, "hold": true }
|
||||||
|
```
|
||||||
|
|
||||||
|
- **十字**显示长按单发拍子的提示音。
|
||||||
|
- **绿线**代表单发拍会在此响一次。
|
||||||
|
- **黄线**表示长按的起始点,也就是说玩家应当从这里开始长按。
|
||||||
|
- **紫格**就是此次长按所需的时间。
|
||||||
|
- **绿线**与**黄线**之间的间隔就是`拍长`的时间。
|
||||||
|
- **十字**与**黄线**或者**绿线**与**紫格**末端之间的间隔就是`间隔`的时间。
|
||||||
|
|
||||||
|
在`长按提示`有三个选项,分别是:自动、偏早、偏晚。
|
||||||
|
|
||||||
|
- 选择**偏早**时,`十字`的位置会在`绿线`之前。
|
||||||
|
- 选择**偏晚**时,`十字`不会显示,但提示音会与`绿线`重合。
|
||||||
|
- 选择**自动**时:
|
||||||
|
- 若**拍长**小于或等于1,则像`偏早`一样,长按单发拍子的提示音会在`绿线`之前;
|
||||||
|
- 若**拍长**大于1,则像`偏晚`一样,`十字`不会显示,但提示音会与`绿线`重合。
|
||||||
|
|
||||||
|
```rdtkview 5 5
|
||||||
|
{ "bar": 1, "beat": 3, "y": 2, "type": "AddOneshotBeat","interval": 2, "tick": 1, "hold": true },
|
||||||
|
{ "bar": 1, "beat": 3, "y": 3, "type": "AddOneshotBeat","interval": 2, "tick": 1, "hold": true },
|
||||||
|
{ "bar": 1, "beat": 3, "y": 4, "type": "AddOneshotBeat","interval": 2, "tick": 1, "hold": true }
|
||||||
|
```
|
||||||
|
|
||||||
|
若设计长按单发拍,还必须满足松手音效与提示音不可重叠
|
||||||
|
|
||||||
|
如果需要在长按单发拍上叠加多个拍子来凸显鼓点,可以将他们的按拍点放在同一位置。
|
||||||
|
|
||||||
|
```rdtkview 5 3
|
||||||
|
{ "bar": 1, "beat": 1.5, "y": 2, "tick": 2.5, "type": "AddOneshotBeat" },
|
||||||
|
{ "bar": 1, "beat": 2.5, "y": 2, "tick": 1.5, "type": "AddOneshotBeat" },
|
||||||
|
{ "bar": 1, "beat": 3, "y": 2, "type": "AddOneshotBeat","interval": 2, "tick": 1, "hold": true }
|
||||||
|
```
|
||||||
|
|
||||||
最后我们针对细分拍、冻结拍、灼热拍做一个技术总结:
|
最后我们针对细分拍、冻结拍、灼热拍做一个技术总结:
|
||||||
|
|
||||||
```rdview 19 7
|
```rdview 19 7
|
||||||
|
|||||||
@@ -116,6 +116,19 @@
|
|||||||
|
|
||||||
5、**移动角色和心脏:**角色和心脏的移动是以他们在轨道中的位置为原点来移动的,比如填写`x: 0, y: 20px`则角色或者心脏向上移动20个像素。你可以使用这个功能把整个轨道挪到屏幕外面去,再以此为原点把心脏挪回来,就可以做到在屏幕内只显示心脏。
|
5、**移动角色和心脏:**角色和心脏的移动是以他们在轨道中的位置为原点来移动的,比如填写`x: 0, y: 20px`则角色或者心脏向上移动20个像素。你可以使用这个功能把整个轨道挪到屏幕外面去,再以此为原点把心脏挪回来,就可以做到在屏幕内只显示心脏。
|
||||||
|
|
||||||
|
## 旋转轨道
|
||||||
|
|
||||||
|
> 目前该事件还不稳定,部分动作可能与轨道移动有冲突。
|
||||||
|
|
||||||
|
如果想要实现`6-2`马克一人多轨的情况就可以使旋转轨道事件。
|
||||||
|
|
||||||
|
先准备两个轨道,使用`连接`将轨道2连接到轨道1上,此时心会自动变成裂纹状态,如果不想要这个效果,可以用下一章轨道涂色里的`心`设置回默认。
|
||||||
|
|
||||||
|
在连接后,通过`分割`可以将两个轨道都显示出来,这两个轨道都会对被连接到的轨道对应角色产生影响。
|
||||||
|
|
||||||
|
`断开`后这些轨道都会回复正常状态。
|
||||||
|
|
||||||
|
|
||||||
## 演示关卡
|
## 演示关卡
|
||||||
|
|
||||||
版面已经占的有些多了,更多的轨道移动脑洞我们留在演示关卡中间。
|
版面已经占的有些多了,更多的轨道移动脑洞我们留在演示关卡中间。
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# 05 单发拍子
|
# 单发拍子
|
||||||
|
|
||||||
## 单发拍子功能介绍
|
## 单发拍子功能介绍
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 6.5 KiB |
@@ -0,0 +1,79 @@
|
|||||||
|
import { createElementEvent } from "./rdtkview.js";
|
||||||
|
export function render(data, width, height, scale = 2.0) {
|
||||||
|
const canvas = document.createElement("div");
|
||||||
|
let objs = [];
|
||||||
|
try {
|
||||||
|
objs = JSON.parse(`[${data}]`);
|
||||||
|
canvas.innerHTML = "";
|
||||||
|
} catch (e) {
|
||||||
|
canvas.innerHTML =
|
||||||
|
`<div style="color: red; font-weight: bold;">JSON解析错误:${e.message}</div>`;
|
||||||
|
return canvas;
|
||||||
|
}
|
||||||
|
const eventStyle = { "scale": scale, "showDuration": false };
|
||||||
|
const elems = [];
|
||||||
|
for (let obj of objs) {
|
||||||
|
const style = { ...eventStyle, enabled: obj.active ?? true };
|
||||||
|
const elem = createElementEvent(obj, style);
|
||||||
|
if (!elem) continue;
|
||||||
|
elem.eventStyle = style;
|
||||||
|
elem.style.position = "absolute";
|
||||||
|
elem.style.left = `${
|
||||||
|
((((obj.bar ?? 1) - 1) * 8) + (obj.beat ?? 1) - 1) * 14 *
|
||||||
|
scale
|
||||||
|
}px`;
|
||||||
|
elem.style.top = `${(obj.y ?? obj.row ?? 0) * 14 * scale}px`;
|
||||||
|
elem.addEventListener("mousedown", (event) => {
|
||||||
|
const eStyle = elem.eventStyle;
|
||||||
|
eStyle.active = true;
|
||||||
|
elem.onStateChange(eStyle);
|
||||||
|
});
|
||||||
|
elem.addEventListener("mouseover", (event) => {
|
||||||
|
const eStyle = elem.eventStyle;
|
||||||
|
eStyle.hover = true;
|
||||||
|
elem.onStateChange(eStyle);
|
||||||
|
});
|
||||||
|
elem.addEventListener("mouseout", (event) => {
|
||||||
|
const eStyle = elem.eventStyle;
|
||||||
|
eStyle.hover = false;
|
||||||
|
eStyle.active = false;
|
||||||
|
elem.onStateChange(eStyle);
|
||||||
|
});
|
||||||
|
elem.addEventListener("mouseup", (event) => {
|
||||||
|
const eStyle = elem.eventStyle;
|
||||||
|
eStyle.active = false;
|
||||||
|
elem.onStateChange(eStyle);
|
||||||
|
});
|
||||||
|
elems.push(elem);
|
||||||
|
}
|
||||||
|
canvas.style.position = "relative";
|
||||||
|
canvas.style.width = `${width * 14 * scale}px`;
|
||||||
|
canvas.style.height = `${height * 14 * scale}px`;
|
||||||
|
elems.forEach((elem) => {
|
||||||
|
canvas.appendChild(elem);
|
||||||
|
});
|
||||||
|
return canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.$docsify.plugins = [].concat(window.$docsify.plugins, (hook) => {
|
||||||
|
hook.doneEach(() => {
|
||||||
|
const pres = document.querySelectorAll("pre");
|
||||||
|
pres.forEach((codeBlock) => {
|
||||||
|
const lang = codeBlock.getAttribute("data-lang") || "";
|
||||||
|
if (lang.startsWith("rdtkview")) {
|
||||||
|
const code = codeBlock.querySelector("code").innerText || "";
|
||||||
|
const args = lang.slice(8).trim().split(" ");
|
||||||
|
const width = parseInt(args[0]) || 8;
|
||||||
|
const height = parseInt(args[1]) || 4;
|
||||||
|
const scale = parseFloat(args[2]) || 2.0;
|
||||||
|
const result = render(code, width, height, scale);
|
||||||
|
const parent = document.createElement("div");
|
||||||
|
parent.style.width = 100 + "%";
|
||||||
|
parent.style.display = "flex";
|
||||||
|
parent.style.justifyContent = "center";
|
||||||
|
parent.appendChild(result);
|
||||||
|
codeBlock.replaceWith(parent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -849,6 +849,9 @@ function ReadProperties(reader, event, pairs) {
|
|||||||
if (value = FindValue(pairs, "loop")) {
|
if (value = FindValue(pairs, "loop")) {
|
||||||
event.BeatStyle.Loop = parseInt(value.Value);
|
event.BeatStyle.Loop = parseInt(value.Value);
|
||||||
}
|
}
|
||||||
|
if (value = FindValue(pairs, "hold")) {
|
||||||
|
event.BeatStyle.Hold = parseFloat(value.Value);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -938,7 +941,7 @@ function ConstructDivObject(obj) {
|
|||||||
result += (((_a = obj.BeatStyle) === null || _a === void 0 ? void 0 : _a.RowXs[i]) === 'x') ? `<div class="x"></div>` : `<div></div>`;
|
result += (((_a = obj.BeatStyle) === null || _a === void 0 ? void 0 : _a.RowXs[i]) === 'x') ? `<div class="x"></div>` : `<div></div>`;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}], [(obj.BeatType === BeatsSpecial.AddClassicBeat || obj.BeatType === BeatsSpecial.AddFreeTimeBeat || obj.BeatType === BeatsSpecial.PulseFreeTimeBeat) && ((_s = (_r = obj.BeatStyle) === null || _r === void 0 ? void 0 : _r.Hold) !== null && _s !== void 0 ? _s : 0) > 0, `holdbeat`, () => ''], [obj.BeatType === BeatsSpecial.AddOneshotBeat && obj.BeatStyle !== undefined && obj.BeatStyle.OneshotBeatSubType.includes(RDOneshotBeatSubType.Skipshot), `skipshot`, () => ''])}</div>`;
|
}], [(obj.BeatType === BeatsSpecial.AddClassicBeat || obj.BeatType === BeatsSpecial.AddFreeTimeBeat || obj.BeatType === BeatsSpecial.PulseFreeTimeBeat || obj.BeatType === BeatsSpecial.AddOneshotBeat) && ((_s = (_r = obj.BeatStyle) === null || _r === void 0 ? void 0 : _r.Hold) !== null && _s !== void 0 ? _s : 0) > 0, `holdbeat`, () => ''], [obj.BeatType === BeatsSpecial.AddOneshotBeat && obj.BeatStyle !== undefined && obj.BeatStyle.OneshotBeatSubType.includes(RDOneshotBeatSubType.Skipshot), `skipshot`, () => ''])}</div>`;
|
||||||
}
|
}
|
||||||
else if (obj instanceof RDGroup) {
|
else if (obj instanceof RDGroup) {
|
||||||
return `<div class="event group"style="${ConstructCssPropertiesIfExists(new Map([
|
return `<div class="event group"style="${ConstructCssPropertiesIfExists(new Map([
|
||||||
|
|||||||
@@ -829,6 +829,9 @@ function ReadProperties(reader: Reader, event: RDEvent | RDGroup, pairs: KeyValu
|
|||||||
if (value = FindValue(pairs, "loop")) {
|
if (value = FindValue(pairs, "loop")) {
|
||||||
event.BeatStyle.Loop = parseInt(value.Value);
|
event.BeatStyle.Loop = parseInt(value.Value);
|
||||||
}
|
}
|
||||||
|
if (value = FindValue(pairs, "hold")) {
|
||||||
|
event.BeatStyle.Hold = parseFloat(value.Value);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -925,7 +928,7 @@ function ConstructDivObject(obj: RDEvent | RDGroup): string {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}],
|
}],
|
||||||
[(obj.BeatType === BeatsSpecial.AddClassicBeat || obj.BeatType === BeatsSpecial.AddFreeTimeBeat || obj.BeatType === BeatsSpecial.PulseFreeTimeBeat) && (obj.BeatStyle?.Hold ?? 0) > 0, `holdbeat`, () => ''],
|
[(obj.BeatType === BeatsSpecial.AddClassicBeat || obj.BeatType === BeatsSpecial.AddFreeTimeBeat || obj.BeatType === BeatsSpecial.PulseFreeTimeBeat || obj.BeatType === BeatsSpecial.AddOneshotBeat) && (obj.BeatStyle?.Hold ?? 0) > 0, `holdbeat`, () => ''],
|
||||||
[obj.BeatType === BeatsSpecial.AddOneshotBeat && obj.BeatStyle !== undefined && obj.BeatStyle.OneshotBeatSubType.includes(RDOneshotBeatSubType.Skipshot), `skipshot`, () => ''],
|
[obj.BeatType === BeatsSpecial.AddOneshotBeat && obj.BeatStyle !== undefined && obj.BeatStyle.OneshotBeatSubType.includes(RDOneshotBeatSubType.Skipshot), `skipshot`, () => ''],
|
||||||
)}</div>`;
|
)}</div>`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,143 +0,0 @@
|
|||||||
import { Extensions, IconStyle } from "/script/Extensions.js";
|
|
||||||
function rend() {
|
|
||||||
const codeBlocks = document.querySelectorAll("canvas.rdview2-canvas");
|
|
||||||
codeBlocks.forEach((block) => {
|
|
||||||
// 清理旧的实例
|
|
||||||
if (block.cleanup) {
|
|
||||||
block.cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
const width = block.getAttribute("data-width") || 8;
|
|
||||||
const height = block.getAttribute("data-height") || 4;
|
|
||||||
const scale = block.getAttribute("data-scale") || 2;
|
|
||||||
const data = block.getAttribute("data");
|
|
||||||
const ctx = block.getContext("2d", { alpha: false, desynchronized: true });
|
|
||||||
block.width = width * scale * 14;
|
|
||||||
block.height = height * scale * 14;
|
|
||||||
if (!data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const jsonData = JSON.parse(`[${JSON.parse(data)}]`);
|
|
||||||
|
|
||||||
let lastActive = false;
|
|
||||||
let evtId = 0;
|
|
||||||
let frameId = null;
|
|
||||||
let cachedRect = null;
|
|
||||||
let mdown = false;
|
|
||||||
|
|
||||||
const eventPositions = jsonData.map(evt => ({
|
|
||||||
evt,
|
|
||||||
x: ((evt.beat ?? 1) - 1) * scale * 14,
|
|
||||||
y: (evt.y ?? 0) * scale * 14
|
|
||||||
}));
|
|
||||||
|
|
||||||
const style = new IconStyle();
|
|
||||||
style.Scale = scale;
|
|
||||||
|
|
||||||
const updateRect = () => {
|
|
||||||
cachedRect = block.getBoundingClientRect();
|
|
||||||
};
|
|
||||||
updateRect();
|
|
||||||
|
|
||||||
let rectTimeout;
|
|
||||||
const debouncedUpdateRect = () => {
|
|
||||||
clearTimeout(rectTimeout);
|
|
||||||
rectTimeout = setTimeout(updateRect, 100);
|
|
||||||
};
|
|
||||||
|
|
||||||
const observer = new ResizeObserver(debouncedUpdateRect);
|
|
||||||
observer.observe(block);
|
|
||||||
|
|
||||||
window.addEventListener("resize", debouncedUpdateRect, { passive: true });
|
|
||||||
window.addEventListener("scroll", debouncedUpdateRect, { passive: true });
|
|
||||||
|
|
||||||
block.render = (x, y, active) => {
|
|
||||||
const point = { x, y };
|
|
||||||
let currentEvtId = null;
|
|
||||||
for (let i = 0; i < eventPositions.length; i++) {
|
|
||||||
const { evt } = eventPositions[i];
|
|
||||||
if (evt.area?.contains(point)) {
|
|
||||||
currentEvtId = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentEvtId === evtId && active === lastActive) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
evtId = currentEvtId;
|
|
||||||
lastActive = active;
|
|
||||||
|
|
||||||
ctx.clearRect(0, 0, block.width, block.height);
|
|
||||||
|
|
||||||
for (const { evt, x: posX, y: posY } of eventPositions) {
|
|
||||||
const isHovered = evt.area?.contains(point) ?? false;
|
|
||||||
style.Hover = isHovered;
|
|
||||||
style.Active = isHovered ? active : false;
|
|
||||||
evt.area = Extensions.DrawEventIcon(ctx, evt, { x: posX, y: posY }, style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const throttledRender = (x, y, active) => {
|
|
||||||
if (frameId !== null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
frameId = requestAnimationFrame(() => {
|
|
||||||
block.render(x, y, active);
|
|
||||||
frameId = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const getMousePos = (e) => ({
|
|
||||||
x: e.clientX - cachedRect.left,
|
|
||||||
y: e.clientY - cachedRect.top
|
|
||||||
});
|
|
||||||
|
|
||||||
block.addEventListener("mousemove", (e) => {
|
|
||||||
const { x, y } = getMousePos(e);
|
|
||||||
console.log(x, y);
|
|
||||||
throttledRender(x, y, mdown);
|
|
||||||
}, { passive: true });
|
|
||||||
|
|
||||||
block.addEventListener("mousedown", (e) => {
|
|
||||||
const { x, y } = getMousePos(e);
|
|
||||||
mdown = true;
|
|
||||||
block.render(x, y, mdown);
|
|
||||||
});
|
|
||||||
|
|
||||||
block.addEventListener("mouseup", (e) => {
|
|
||||||
const { x, y } = getMousePos(e);
|
|
||||||
mdown = false;
|
|
||||||
block.render(x, y, mdown);
|
|
||||||
});
|
|
||||||
|
|
||||||
block.addEventListener("mouseleave", () => {
|
|
||||||
if (frameId !== null) {
|
|
||||||
cancelAnimationFrame(frameId);
|
|
||||||
frameId = null;
|
|
||||||
}
|
|
||||||
mdown = false;
|
|
||||||
evtId = null;
|
|
||||||
lastActive = false;
|
|
||||||
block.render(-1, -1, mdown);
|
|
||||||
}, { passive: true });
|
|
||||||
|
|
||||||
block.cleanup = () => {
|
|
||||||
observer.disconnect();
|
|
||||||
window.removeEventListener("resize", debouncedUpdateRect);
|
|
||||||
window.removeEventListener("scroll", debouncedUpdateRect);
|
|
||||||
clearTimeout(rectTimeout);
|
|
||||||
if (frameId !== null) {
|
|
||||||
cancelAnimationFrame(frameId);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
block.render(-1, -1, mdown);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins, function (hook) {
|
|
||||||
hook.doneEach(function () {
|
|
||||||
rend();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { createElementEvent } from "./rdtkview.js";
|
||||||
|
|
||||||
let curx = 0;
|
let curx = 0;
|
||||||
let cury = 0;
|
let cury = 0;
|
||||||
let rowx;
|
let rowx;
|
||||||
@@ -21,7 +23,7 @@ let positions = {
|
|||||||
"r5": { "y": 78, "name": "二轨时的第二轨/四轨时的第二轨" },
|
"r5": { "y": 78, "name": "二轨时的第二轨/四轨时的第二轨" },
|
||||||
"r6": { "y": 57, "name": "三轨时的第一轨" },
|
"r6": { "y": 57, "name": "三轨时的第一轨" },
|
||||||
"r7": { "y": 36, "name": "四轨时的第一轨" },
|
"r7": { "y": 36, "name": "四轨时的第一轨" },
|
||||||
}
|
};
|
||||||
|
|
||||||
function themePosition() {
|
function themePosition() {
|
||||||
let theme_positions = document.getElementById("theme-positions");
|
let theme_positions = document.getElementById("theme-positions");
|
||||||
@@ -61,45 +63,74 @@ function themePosition() {
|
|||||||
rowx = null;
|
rowx = null;
|
||||||
columny = null;
|
columny = null;
|
||||||
for (let key in positions) {
|
for (let key in positions) {
|
||||||
if (positions[key].x !== null && Math.abs(x - positions[key].x) < 5)
|
if (
|
||||||
|
positions[key].x !== null && Math.abs(x - positions[key].x) < 5
|
||||||
|
) {
|
||||||
rowx = positions[key].x;
|
rowx = positions[key].x;
|
||||||
if (positions[key].y !== null && Math.abs(y - positions[key].y) < 5)
|
}
|
||||||
|
if (
|
||||||
|
positions[key].y !== null && Math.abs(y - positions[key].y) < 5
|
||||||
|
) {
|
||||||
columny = positions[key].y;
|
columny = positions[key].y;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rowx) {
|
if (rowx) {
|
||||||
perx.innerText = (rowx / 3.52).toFixed(2) + "%"
|
perx.innerText = (rowx / 3.52).toFixed(2) + "%";
|
||||||
perx.classList.add("c")
|
perx.classList.add("c");
|
||||||
pxx.innerText = rowx + "px"
|
pxx.innerText = rowx + "px";
|
||||||
pxx.classList.add("c")
|
pxx.classList.add("c");
|
||||||
}
|
} else {
|
||||||
else {
|
perx.innerText = x_percent.toFixed(2) + "%";
|
||||||
perx.innerText = x_percent.toFixed(2) + "%"
|
perx.classList.remove("c");
|
||||||
perx.classList.remove("c")
|
pxx.innerText = x + "px";
|
||||||
pxx.innerText = x + "px"
|
pxx.classList.remove("c");
|
||||||
pxx.classList.remove("c")
|
|
||||||
}
|
}
|
||||||
if (columny) {
|
if (columny) {
|
||||||
pery.innerText = (100 - columny / 1.98).toFixed(2) + "%"
|
pery.innerText = (100 - columny / 1.98).toFixed(2) + "%";
|
||||||
pery.classList.add("c")
|
pery.classList.add("c");
|
||||||
pxy.innerText = 198 - columny + "px"
|
pxy.innerText = 198 - columny + "px";
|
||||||
pxy.classList.add("c")
|
pxy.classList.add("c");
|
||||||
}
|
} else {
|
||||||
else {
|
pery.innerText = (100 - y_percent).toFixed(2) + "%";
|
||||||
pery.innerText = (100 - y_percent).toFixed(2) + "%"
|
pery.classList.remove("c");
|
||||||
pery.classList.remove("c")
|
pxy.innerText = 198 - y + "px";
|
||||||
pxy.innerText = 198 - y + "px"
|
pxy.classList.remove("c");
|
||||||
pxy.classList.remove("c")
|
|
||||||
}
|
}
|
||||||
infobox.style.right = "calc(" + (100 - x_percent) + "% + 10px)";
|
infobox.style.right = "calc(" + (100 - x_percent) + "% + 10px)";
|
||||||
infobox.style.top = "calc(" + (y_percent) + "% + 10px)";
|
infobox.style.top = "calc(" + y_percent + "% + 10px)";
|
||||||
selectbox.style.left = rowx ? (rowx / 3.52 - 3.41) + "%" : columny ? "0" : "calc(" + x_percent + "% - 3px";
|
selectbox.style.left = rowx
|
||||||
selectbox.style.bottom = columny ? (100 - columny / 1.98 - 5.305) + "%" : rowx ? "0" : "calc(" + (100 - y_percent) + "% - 3px)";
|
? (rowx / 3.52 - 3.41) + "%"
|
||||||
|
: columny
|
||||||
|
? "0"
|
||||||
|
: "calc(" + x_percent + "% - 3px";
|
||||||
|
selectbox.style.bottom = columny
|
||||||
|
? (100 - columny / 1.98 - 5.305) + "%"
|
||||||
|
: rowx
|
||||||
|
? "0"
|
||||||
|
: "calc(" + (100 - y_percent) + "% - 3px)";
|
||||||
selectbox.style.width = rowx ? "6.82%" : columny ? "100%" : "6px";
|
selectbox.style.width = rowx ? "6.82%" : columny ? "100%" : "6px";
|
||||||
selectbox.style.height = columny ? "10.61%" : rowx ? "100%" : "6px";
|
selectbox.style.height = columny ? "10.61%" : rowx ? "100%" : "6px";
|
||||||
let rowname = rowx ? positions[Object.keys(positions).find(key => positions[key].x === rowx)].name : "";
|
let rowname = rowx
|
||||||
let columnname = columny ? positions[Object.keys(positions).find(key => positions[key].y === columny)].name : "";
|
? positions[
|
||||||
theme_positions.querySelector(".other").innerText = rowname && columnname ? columnname + "上的" + rowname : rowname ? rowname : columnname ? columnname : "";
|
Object.keys(positions).find((key) => positions[key].x === rowx)
|
||||||
|
].name
|
||||||
|
: "";
|
||||||
|
let columnname = columny
|
||||||
|
? positions[
|
||||||
|
Object.keys(positions).find((key) =>
|
||||||
|
positions[key].y === columny
|
||||||
|
)
|
||||||
|
].name
|
||||||
|
: "";
|
||||||
|
theme_positions.querySelector(".other").innerText =
|
||||||
|
rowname && columnname
|
||||||
|
? columnname + "上的" + rowname
|
||||||
|
: rowname
|
||||||
|
? rowname
|
||||||
|
: columnname
|
||||||
|
? columnname
|
||||||
|
: "";
|
||||||
});
|
});
|
||||||
theme_positions.addEventListener("mouseleave", function (event) {
|
theme_positions.addEventListener("mouseleave", function (event) {
|
||||||
perx.innerText = "100%";
|
perx.innerText = "100%";
|
||||||
@@ -112,13 +143,12 @@ function themePosition() {
|
|||||||
selectbox.style.bottom = "50%";
|
selectbox.style.bottom = "50%";
|
||||||
selectbox.style.width = "0";
|
selectbox.style.width = "0";
|
||||||
selectbox.style.height = "0";
|
selectbox.style.height = "0";
|
||||||
perx.classList.remove("c")
|
perx.classList.remove("c");
|
||||||
pery.classList.remove("c")
|
pery.classList.remove("c");
|
||||||
pxx.classList.remove("c")
|
pxx.classList.remove("c");
|
||||||
pxy.classList.remove("c")
|
pxy.classList.remove("c");
|
||||||
other.innerText = "";
|
other.innerText = "";
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function rdview() {
|
function rdview() {
|
||||||
@@ -129,21 +159,25 @@ function rdview() {
|
|||||||
let number = rdview.querySelector(".number");
|
let number = rdview.querySelector(".number");
|
||||||
const textareaStyles = window.getComputedStyle(textarea);
|
const textareaStyles = window.getComputedStyle(textarea);
|
||||||
[
|
[
|
||||||
'fontFamily', 'fontSize', 'fontWeight',
|
"fontFamily",
|
||||||
'letterSpacing', 'lineHeight', 'padding',
|
"fontSize",
|
||||||
].forEach(property => {
|
"fontWeight",
|
||||||
|
"letterSpacing",
|
||||||
|
"lineHeight",
|
||||||
|
"padding",
|
||||||
|
].forEach((property) => {
|
||||||
number.style[property] = textareaStyles[property];
|
number.style[property] = textareaStyles[property];
|
||||||
});
|
});
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement("canvas");
|
||||||
const context = canvas.getContext('2d');
|
const context = canvas.getContext("2d");
|
||||||
const font = `${textareaStyles.fontSize} ${textareaStyles.fontFamily}`;
|
const font = `${textareaStyles.fontSize} ${textareaStyles.fontFamily}`;
|
||||||
context.font = font;
|
context.font = font;
|
||||||
function calcStringLines(sentence, width) {
|
function calcStringLines(sentence, width) {
|
||||||
if (!width) return 0;
|
if (!width) return 0;
|
||||||
const words = sentence.split('');
|
const words = sentence.split("");
|
||||||
let lineCount = 0;
|
let lineCount = 0;
|
||||||
|
|
||||||
let currentLine = '';
|
let currentLine = "";
|
||||||
for (let i = 0; i < words.length; i++) {
|
for (let i = 0; i < words.length; i++) {
|
||||||
const wordWidth = context.measureText(words[i]).width;
|
const wordWidth = context.measureText(words[i]).width;
|
||||||
const lineWidth = context.measureText(currentLine).width;
|
const lineWidth = context.measureText(currentLine).width;
|
||||||
@@ -154,18 +188,22 @@ function rdview() {
|
|||||||
currentLine += words[i];
|
currentLine += words[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (currentLine.trim() !== '') lineCount++;
|
if (currentLine.trim() !== "") lineCount++;
|
||||||
return lineCount;
|
return lineCount;
|
||||||
}
|
}
|
||||||
function calcLines() {
|
function calcLines() {
|
||||||
const lines = textarea.value.split('\n');
|
const lines = textarea.value.split("\n");
|
||||||
const textareaWidth = textarea.getBoundingClientRect().width;
|
const textareaWidth = textarea.getBoundingClientRect().width;
|
||||||
const textareaScrollWidth = textareaWidth - textarea.clientWidth;
|
const textareaScrollWidth = textareaWidth - textarea.clientWidth;
|
||||||
const parseNumber = (v) => v.endsWith('px') ? parseInt(v.slice(0, -2), 10) : 0;
|
const parseNumber = (v) =>
|
||||||
|
v.endsWith("px") ? parseInt(v.slice(0, -2), 10) : 0;
|
||||||
const textareaPaddingLeft = parseNumber(textareaStyles.paddingLeft);
|
const textareaPaddingLeft = parseNumber(textareaStyles.paddingLeft);
|
||||||
const textareaPaddingRight = parseNumber(textareaStyles.paddingRight);
|
const textareaPaddingRight = parseNumber(textareaStyles.paddingRight);
|
||||||
const textareaContentWidth = textareaWidth - textareaPaddingLeft - textareaPaddingRight - textareaScrollWidth;
|
const textareaContentWidth = textareaWidth - textareaPaddingLeft -
|
||||||
const numLines = lines.map(lineString => calcStringLines(lineString, textareaContentWidth));
|
textareaPaddingRight - textareaScrollWidth;
|
||||||
|
const numLines = lines.map((lineString) =>
|
||||||
|
calcStringLines(lineString, textareaContentWidth)
|
||||||
|
);
|
||||||
let lineNumbers = [];
|
let lineNumbers = [];
|
||||||
let i = 1;
|
let i = 1;
|
||||||
while (numLines.length > 0) {
|
while (numLines.length > 0) {
|
||||||
@@ -173,8 +211,8 @@ function rdview() {
|
|||||||
lineNumbers.push(i);
|
lineNumbers.push(i);
|
||||||
if (numLinesOfSentence > 1) {
|
if (numLinesOfSentence > 1) {
|
||||||
Array(numLinesOfSentence - 1)
|
Array(numLinesOfSentence - 1)
|
||||||
.fill('')
|
.fill("")
|
||||||
.forEach((_) => lineNumbers.push(''));
|
.forEach((_) => lineNumbers.push(""));
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@@ -184,24 +222,172 @@ function rdview() {
|
|||||||
const lines = calcLines();
|
const lines = calcLines();
|
||||||
const lineDoms = Array.from({
|
const lineDoms = Array.from({
|
||||||
length: lines.length,
|
length: lines.length,
|
||||||
}, (_, i) => `<div>${lines[i] || ' '}</div>`);
|
}, (_, i) => `<div>${lines[i] || " "}</div>`);
|
||||||
number.innerHTML = lineDoms.join('');
|
number.innerHTML = lineDoms.join("");
|
||||||
}
|
}
|
||||||
function initCanvas() {
|
function initCanvas() {
|
||||||
const lang = textarea.value.split('\n')[0]
|
const lang = textarea.value.split("\n")[0];
|
||||||
const code = textarea.value.split('\n').slice(1).join('\n')
|
const code = textarea.value.split("\n").slice(1).join("\n");
|
||||||
rdcanvas.innerHTML = RDViewRender(lang, code)
|
rdcanvas.innerHTML = RDViewRender(lang, code);
|
||||||
}
|
}
|
||||||
initCanvas()
|
initCanvas();
|
||||||
initLineNumbers()
|
initLineNumbers();
|
||||||
textarea.addEventListener('input', initLineNumbers);
|
textarea.addEventListener("input", initLineNumbers);
|
||||||
textarea.addEventListener('input', initCanvas);
|
textarea.addEventListener("input", initCanvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function rdview2() {
|
||||||
|
let rdview = document.getElementsByClassName("rdview2")[0];
|
||||||
|
if (!rdview) return;
|
||||||
|
let rdcanvas = rdview.querySelector(".canvas");
|
||||||
|
let textarea = rdview.querySelector("textarea.area");
|
||||||
|
let number = rdview.querySelector(".number");
|
||||||
|
const textareaStyles = window.getComputedStyle(textarea);
|
||||||
|
[
|
||||||
|
"fontFamily",
|
||||||
|
"fontSize",
|
||||||
|
"fontWeight",
|
||||||
|
"letterSpacing",
|
||||||
|
"lineHeight",
|
||||||
|
"padding",
|
||||||
|
].forEach((property) => {
|
||||||
|
number.style[property] = textareaStyles[property];
|
||||||
|
});
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
const context = canvas.getContext("2d");
|
||||||
|
const font = `${textareaStyles.fontSize} ${textareaStyles.fontFamily}`;
|
||||||
|
context.font = font;
|
||||||
|
function calcStringLines(sentence, width) {
|
||||||
|
if (!width) return 0;
|
||||||
|
const words = sentence.split("");
|
||||||
|
let lineCount = 0;
|
||||||
|
|
||||||
|
let currentLine = "";
|
||||||
|
for (let i = 0; i < words.length; i++) {
|
||||||
|
const wordWidth = context.measureText(words[i]).width;
|
||||||
|
const lineWidth = context.measureText(currentLine).width;
|
||||||
|
if (lineWidth + wordWidth > width) {
|
||||||
|
lineCount++;
|
||||||
|
currentLine = words[i];
|
||||||
|
} else {
|
||||||
|
currentLine += words[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentLine.trim() !== "") lineCount++;
|
||||||
|
return lineCount;
|
||||||
|
}
|
||||||
|
function calcLines() {
|
||||||
|
const lines = textarea.value.split("\n");
|
||||||
|
const textareaWidth = textarea.getBoundingClientRect().width;
|
||||||
|
const textareaScrollWidth = textareaWidth - textarea.clientWidth;
|
||||||
|
const parseNumber = (v) =>
|
||||||
|
v.endsWith("px") ? parseInt(v.slice(0, -2), 10) : 0;
|
||||||
|
const textareaPaddingLeft = parseNumber(textareaStyles.paddingLeft);
|
||||||
|
const textareaPaddingRight = parseNumber(textareaStyles.paddingRight);
|
||||||
|
const textareaContentWidth = textareaWidth - textareaPaddingLeft -
|
||||||
|
textareaPaddingRight - textareaScrollWidth;
|
||||||
|
const numLines = lines.map((lineString) =>
|
||||||
|
calcStringLines(lineString, textareaContentWidth)
|
||||||
|
);
|
||||||
|
let lineNumbers = [];
|
||||||
|
let i = 1;
|
||||||
|
while (numLines.length > 0) {
|
||||||
|
const numLinesOfSentence = numLines.shift();
|
||||||
|
lineNumbers.push(i);
|
||||||
|
if (numLinesOfSentence > 1) {
|
||||||
|
Array(numLinesOfSentence - 1)
|
||||||
|
.fill("")
|
||||||
|
.forEach((_) => lineNumbers.push(""));
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return lineNumbers;
|
||||||
|
}
|
||||||
|
function initLineNumbers() {
|
||||||
|
const lines = calcLines();
|
||||||
|
const lineDoms = Array.from({
|
||||||
|
length: lines.length,
|
||||||
|
}, (_, i) => `<div>${lines[i] || " "}</div>`);
|
||||||
|
number.innerHTML = lineDoms.join("");
|
||||||
|
}
|
||||||
|
function initCanvas() {
|
||||||
|
let objs = [];
|
||||||
|
try {
|
||||||
|
objs = JSON.parse(`[${textarea.value}]`);
|
||||||
|
rdcanvas.innerHTML = "";
|
||||||
|
} catch (e) {
|
||||||
|
rdcanvas.innerHTML =
|
||||||
|
`<div style="color: red; font-weight: bold;">JSON解析错误:${e.message}</div>`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const eventStyle = { "scale": 2.0, "showDuration": false };
|
||||||
|
let maxy = 0;
|
||||||
|
const elemPromises = [];
|
||||||
|
for (let obj of objs) {
|
||||||
|
const style = { ...eventStyle, enabled: obj.active ?? true };
|
||||||
|
const elem = createElementEvent(obj, style).then((elem) => {
|
||||||
|
elem.eventStyle = style;
|
||||||
|
elem.style.position = "absolute";
|
||||||
|
elem.style.left = `${
|
||||||
|
((((obj.bar ?? 1) - 1) * 8) + (obj.beat ?? 1) - 1) * 14 *
|
||||||
|
eventStyle.scale
|
||||||
|
}px`;
|
||||||
|
elem.style.top = `${
|
||||||
|
(obj.y ?? obj.row ?? 0) * 14 * eventStyle.scale
|
||||||
|
}px`;
|
||||||
|
if (
|
||||||
|
((obj.y ?? obj.row ?? 0) * 14 * eventStyle.scale) +
|
||||||
|
elem.offsetHeight > maxy
|
||||||
|
) {
|
||||||
|
maxy = ((obj.y ?? obj.row ?? 0) * 14 * eventStyle.scale) +
|
||||||
|
elem.offsetHeight;
|
||||||
|
}
|
||||||
|
elem.addEventListener("mousedown", (event) => {
|
||||||
|
const eStyle = elem.eventStyle;
|
||||||
|
eStyle.active = true;
|
||||||
|
elem.onStateChange(eStyle);
|
||||||
|
});
|
||||||
|
elem.addEventListener("mouseover", (event) => {
|
||||||
|
const eStyle = elem.eventStyle;
|
||||||
|
eStyle.hover = true;
|
||||||
|
elem.onStateChange(eStyle);
|
||||||
|
});
|
||||||
|
elem.addEventListener("mouseout", (event) => {
|
||||||
|
const eStyle = elem.eventStyle;
|
||||||
|
eStyle.hover = false;
|
||||||
|
eStyle.active = false;
|
||||||
|
elem.onStateChange(eStyle);
|
||||||
|
});
|
||||||
|
elem.addEventListener("mouseup", (event) => {
|
||||||
|
const eStyle = elem.eventStyle;
|
||||||
|
eStyle.active = false;
|
||||||
|
elem.onStateChange(eStyle);
|
||||||
|
});
|
||||||
|
return elem;
|
||||||
|
});
|
||||||
|
elemPromises.push(elem);
|
||||||
|
}
|
||||||
|
Promise.all(elemPromises).then((elems) => {
|
||||||
|
elems.forEach((elem) => {
|
||||||
|
rdcanvas.appendChild(elem);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
rdcanvas.style.position = "relative";
|
||||||
|
rdcanvas.style.height = `${maxy + 4 * 14 * eventStyle.scale}px`;
|
||||||
|
}
|
||||||
|
initCanvas();
|
||||||
|
initLineNumbers();
|
||||||
|
textarea.addEventListener("input", initLineNumbers);
|
||||||
|
textarea.addEventListener("input", initCanvas);
|
||||||
|
}
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins, function (hook) {
|
window.$docsify.plugins = [].concat(window.$docsify.plugins, function (hook) {
|
||||||
hook.doneEach(function () {
|
hook.doneEach(function () {
|
||||||
themePosition();
|
themePosition();
|
||||||
rdview();
|
rdview();
|
||||||
|
rdview2();
|
||||||
document.documentElement.scrollTop = document.body.scrollTop = 0;
|
document.documentElement.scrollTop = document.body.scrollTop = 0;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
import("./docsify.min.js").then(() => {
|
||||||
|
});
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 130 B After Width: | Height: | Size: 129 B |
@@ -1146,6 +1146,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&>.beat:not(:has(.classicbeat)) {
|
||||||
|
&>div.holdbeat {
|
||||||
|
position: absolute;
|
||||||
|
width: calc((var(--hold) * 28 - 2) * 1px);
|
||||||
|
height: inherit;
|
||||||
|
background-size: auto 100%;
|
||||||
|
background-image: url(./assets/beats/hold-area.png);
|
||||||
|
transform: translate(calc(((var(--tick) + var(--delay) + var(--loop) * var(--interval)) * 28 - 4) * 1px), 0);
|
||||||
|
z-index: -2;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 2px;
|
||||||
|
height: 28px;
|
||||||
|
background-size: auto 100%;
|
||||||
|
transform: translate(calc((var(--hold) * 28 - 2) * 1px), 0);
|
||||||
|
background-image: url(./assets/beats/hold-right.png);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active>div.holdbeat {
|
||||||
|
background-image: url(./assets/beats/hold-hover-area.png);
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
background-image: url(./assets/beats/hold-hover-right.png);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&>.beat:has(.row-xs) {
|
&>.beat:has(.row-xs) {
|
||||||
width: 56px;
|
width: 56px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
@@ -2180,6 +2210,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&>.beat:not(:has(.classicbeat)) {
|
||||||
|
&>div.holdbeat {
|
||||||
|
position: absolute;
|
||||||
|
width: calc((var(--hold) * 28 - 2) * 1px);
|
||||||
|
height: inherit;
|
||||||
|
background-size: auto 100%;
|
||||||
|
background-image: url(./assets/beats/hold-area.png);
|
||||||
|
transform: translate(calc(((var(--tick) + var(--delay) + var(--loop) * var(--interval)) * 28 - 4) * 1px), 0);
|
||||||
|
z-index: -2;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 2px;
|
||||||
|
height: 28px;
|
||||||
|
background-size: auto 100%;
|
||||||
|
transform: translate(calc((var(--hold) * 28 - 2) * 1px), 0);
|
||||||
|
background-image: url(./assets/beats/hold-right.png);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active>div.holdbeat {
|
||||||
|
background-image: url(./assets/beats/hold-hover-area.png);
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
background-image: url(./assets/beats/hold-hover-right.png);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&>.beat:has(.row-xs) {
|
&>.beat:has(.row-xs) {
|
||||||
width: 56px;
|
width: 56px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ div.theme-positions>.infobox {
|
|||||||
|
|
||||||
.markdown-section:has(div.rdview) {}
|
.markdown-section:has(div.rdview) {}
|
||||||
|
|
||||||
div.rdview {
|
div.rdview, div.rdview2 {
|
||||||
& div.canvas {
|
& div.canvas {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||