Skip to content

About

A Dance of Fire and Ice 的 4K 下落式辅助读谱 mod:读取内存里的实时谱面,4 条下落轨道 + 两套自动排键算法(含移植 ReADOFAIMacro 的拆手序)

Topics

Resources

Stars

4 stars

Watchers

0 watching

Forks

Repository files navigation

LaneReader

A Dance of Fire and Ice(冰与火之舞)的 4K 下落式辅助读谱 mod。 直接读取游戏内存里已经构建好的谱面,渲染成 4 条下落轨道,并用三种算法自动排键。

⚠️ 本 mod 旨在帮助理解谱面配置,或者提供一种玩法。请不要只是为了直接打出更好的成绩而使用它 —— 某种意义上这是一种外挂。

面向 ADOFAI 3.3.1 / Unity 6000.3.10f1 / Mono / UnityModManager 0.32.4。当前版本 1.1.1。


三种排键手法

同一张谱,三种算法给出三种完全不同的手序。它们没有绝对的好坏,只有取舍;面板「排键(手序)」里 点「手法」按钮循环切换。1.0.1 起默认是「时间重置法」。

手法 依据什么 优势 劣势
AdoFalling 原版 相邻音符的间隔(RollWindowMs) 简单、和原版 mod 一致 密集段整条手序就是楼梯;处理不了双押(同时的两颗音只能挤在一起)
拆手序(移植自 ReADOFAIMacro,MIT) 谱面 BPM + 节拍(180° = 一个窗口) 手序非常合理:强拍给主手、弱拍给副手;双押和轮指都能处理 三连音交互会很灾难:窗口按 180° 切,三条 1/3 拍的音只在归一化缩放正好为 1 时才凑成一个窗口,其余情况边界落进音组中间、还把组后面那颗音一起吞掉,整段手序从此错位一位;依赖原曲 BPM
时间重置法(默认,1.0.0 新增) 只有音符间隔(一个参数:主手重置间隔) 和谱面 BPM 完全无关,只有「换手时间」的概念,各种奇怪节拍/变速都能处理;足够快的三连音锁成 JKF 循环 每次一定从主手开始;不看强弱拍、不考虑节奏,只考虑怎么让手指舒服

双押合并(三种手法共用)

间隔小于「双押合并」的两颗音当作一次双押:后一颗被移到和前一颗同一时刻,两颗画在同一行上。 默认 50 ms,0 = 关。它只改「看起来是什么谱」,不改手指怎么分,所以三种手法都套用; 而且只移按键 —— 长条的尾音是同一个键的松手,必须留在长条末端(同理也不会弄坏任何长条的长度)。 实测(R = 165ms、merge = 50ms,写成 时刻+手指,单位 ms):

音型 合并后
50+400 0J 0K 450J 450K 900J 900K …(5 处合并)
40+40+40 0J 0K 80F 80D 160J 160K …(5 处合并)
100+100+200 不变(间隔 100/200ms > 50ms)

面板「排键(手序)」里还有一个**「刷新手序」按钮:改了设置或换了谱面后发现手序没更新就点它。 每次重算都会先把音符时间还原成谱面自己的**再排,所以点几次结果都一样(不会被上一次合并过的 时间反过来影响)。

时间重置法的规则

只有 R = 主手重置间隔(默认 165ms)一个参数。状态 = 上一个音用的手指 (D 副轮 / F 副手 / J 主手 / K 主轮),判据全部含等号:

上次手指 判据 本次
无 / 副轮 — J
主手 d > R J(慢:一根指头能一直点)
主手 R/2 < d ≤ R F(强拍主、弱拍副)
主手 d ≤ R/2 K(一只手来不及,同手轮指)
主轮 d > R J(快速段结束,回落)
主轮 d ≤ R F(还在快速段里,换手)
副手 dOff ≤ R/2 或 dMain ≤ R D(副手也得轮)
副手 否则 J

d = 本次 − 上一个音(「主轮」那一行比的就是主轮那一下本身,不是它回应过的那颗主手音; 长按挡在中间时「上一个音」是尾音,手指松手才算空出来);dOff/dMain = 上次副手/主手音。 外轮是收尾的一遍扫描:每个 K/D 和它前一个按键交换手指 ⇒ 外侧指先落(K J D F)。 长条的尾音永远跟在头的轨道上,并且参照时间推进到尾音(手指松手才空出来),链式长条整段同轨。

离线实测(tools/TimeResetCheck.cs,R = 165ms):

均匀间隔 g 周期 内轮 外轮
g > R(>165ms) 1 J J J… 同
R/2 < g ≤ R(83~165ms) 2 J F J F 同
g ≤ R/2(≤82ms) 4 J K F D K J D F

音型的实测(同样 R = 165ms):

音型 (ms) 内轮 外轮
50+50+100(更快) JKF 循环 KJF 循环
80+150 JKFD 循环 KJDF 循环
80+220 JKJK 循环 KJKJ 循环
100+100+200 JFJ 循环 JFJ 循环
100+100+300 JFJ 循环 JFJ 循环
125+125+250 JFJ 循环 JFJ 循环
200+200+400 J 连点 J 连点
100×4(等分) JFJF JFJF
50+400 JKJK(副手全程不动) KJKJ

三连音要锁成 JKF,需要组内 ≤ R/2、组间 > R/2:R = 165ms 时锁得住的是 50+50+100 这种更快的三连音;100+100+200 在这一档是 JFJ(想把它也锁住,得把 R 调到 200ms 以上)。 最后一行要知道:主轮之后 d > R 就回落主手,所以「快双击再隔 400ms」这种音型副手一直闲着。 想让它换手,把 R 调大到 400 以上。


功能

功能 说明
实时读谱 从游戏内存读已构建的谱面,不需要谱面文件,兼容任意自制谱
4K 视图 4 条轨道 / 判定线 / 判定框 / 键位标签 D F J K
三套手序 「AdoFalling 原版」「拆手序」「时间重置法」(默认)可切换,见上。中旋在读数时就被折算成一颗普通砖(不是被丢掉),再和别的音符一起进手序
多语言面板 中文内置(代码里的中文原文就是 key);英文在 lang\en.txt,复制它只翻译右列就能加任意语言。面板顶部按钮切换,首次运行跟随游戏语言
双押合并 三种手法共用:间隔小于阈值的两颗音当作一次双押,后一颗移到和前一颗同一时刻(同一行),默认 50ms,可关
打击弹跳 音符到线时的弹跳反馈,纯时间驱动,不读游戏判定
暂停保留 暂停时叠加层留在屏幕上、音符停在原地;「按任意键开始」那屏只有静态框
小节线 每拍细线、每小节粗线;节拍按 TBPM 铺,跟着谱面的加速/减速变化
轨道长度可定 独立设置(默认 1620px = 1.5 屏,0 = 一直画到屏幕顶);音符和小节线也从轨道顶端开始出现,所以它同时决定你有多久的反应时间
调色板 轨道底色/边框、音符/音符边框、判定线、判定框、键位标签、细小节线/粗小节线各自带 RGBA;轨道与音符可「四轨单独配置」或一键统一
主题预设 七个一键套用:经典蓝橙、亮底黑线、霓虹紫青、黄金黑底、四轨彩虹、极简纯白、纯黑底经典白线
全屏纯色背景 默认关;打开后用一整屏纯色盖住游戏原本的画面,默认黑色 50%,颜色和透明度都可调
面板 分区可折叠(标题是 ▶/▼ 按钮,不是开关圆圈);分区按层级缩进;每个滑块都能直接填数字;颜色行支持 RRGGBBAA;设置之间留空行;面板顶部写明三种手法各自的优势与劣势(只显示当前那种);「排键(手序)」里有**「刷新手序」**按钮
实验性平滑下落 默认关;沿轨道方向的淡拖尾(有自定义图片时拖尾也用那张图),长度是固定的毫秒窗,不随帧时间忽长忽短
实验性补帧 默认关;一帧把叠加层画三次(上一帧 / 下一帧 / 当前帧),只重画会动的那一半
实验性 3D 轨道 默认关;针孔相机:灭点在轨道顶端,音符是贴在轨道上的板;轨道体是一叠内切横带拼的,把「轨道边框粗细」调粗就能盖住台阶
长按 默认开;读 holdLength 画长条,末端始终接住下一颗音符;开头过线后被判定线慢慢吃掉;尾巴那颗音符强制与开头同轨
自定义音符图片 用 png/jpg(含 alpha)替换音符;有图时就只画那张图;长按线条可另指定一张
娱乐(实验性) 默认关;轨道乱飞 / 持续旋转 / 四花瓣 —— 纯好玩,不影响读谱
整体旋转 0~360°,整个叠加层绕判定线中点旋转
诊断日志 可关闭;排键、拍网格、每帧门控都有一行可对照的日志(时间重置法会打出前 32 个音的手指序列,方便调 R)
显示帧数 默认关;打开后在判定线下方显示 fps + 帧间隔(ms)

安装

从 Releases 下载 LaneReader-<版本>.zip,解压出来的就是 Mods\LaneReader\ (LaneReader.dll + Info.json + lang\ + 示例图片 + 安装说明),整个文件夹丢进 <游戏目录>\Mods\ 即可。手动装法一样:

  1. 把 LaneReader.dll、Info.json、lang\ 放进 <游戏目录>\Mods\LaneReader\
  2. 启动游戏,UMM 的 mod 列表里会出现 LaneReader (4K view)

装之前必须完全退出游戏(dll 会被占用)。

设置面板在 UMM 的 mod 设置里(全部手写 GUILayout,不依赖 UMM 的自动绘制 —— 那套在这台安装上对自绘面板的 mod 无效)。


构建

Set-Location LaneReader
powershell -ExecutionPolicy Bypass -File .\build.ps1            # 只编译
powershell -ExecutionPolicy Bypass -File .\build.ps1 -Install   # 编译并部署到游戏
  • 用 csc.exe(.NET Framework 4.0.30319)编译,/langversion:5(不能用 C# 6+ 语法)
  • 部署前必须完全退出游戏,否则 DLL 被占用(user-mapped section open)
  • 每次编译也顺手打出 release 包 LaneReader/build/LaneReader-<版本>.zip(根目录是 LaneReader\, 和 UMM 的安装结构一致,版本号取自 Info.json)
  • 构建产物 LaneReader/build/ 不入库

默认设置(v1.1.0)

设置 默认 说明
流速 1000 px/s 1080p 基准像素,实际按屏幕高度缩放
手法 时间重置法 可切「拆手序」或「AdoFalling 原版」
主手重置间隔 165 ms 时间重置法唯一的参数。150 BPM 下:8 分音(200ms)=单指、16 分音(100ms)=双手交替、≤82ms=轮指
双押合并 50 ms 三种手法共用:间隔小于它的两颗音当双押,后一颗移到和前一颗同一行;0 = 关
最大硬抗 BPM 850 拆手序的标定旋钮:超过它就把窗口减半一次
轮指方向 外轮 K J D F 拆手序与时间重置法共用,可切内轮 J K F D
轨道长度 1620 px(= 1.5 屏) 固定长度;0 = 一直画到屏幕顶端
判定线 显示、横跨全屏 开关和长度都在「布局」里
长按支持 开 长条 = 从开头音符一直连到下一颗;没长按的谱完全不受影响
全屏纯色背景 关 开了就是黑色 50%
音符边框 / 小节线 开 可关
自定义音符图片 / 整体旋转 关 / 0° 不影响原来的外观
颜色 经典蓝橙(暗底白线) 可在「外观 / 颜色」里逐项改或换主题(共 7 个预设)
平滑下落(实验) 关 开了才有拖尾;长度 40ms、层数 8、浓度 0.45
补帧(实验) 关 一帧画三次,暂停不叠
轨道边框粗细 3 px 轨道两侧那条线;3D 下同时是抗锯齿的手段
长按图片重复间隔 1 拍 每拍把 hold.png 首尾相接一次;0 = 整条只贴一张

已经存过 Settings.json 的机器会继续使用自己保存的值,不会套用新默认。 想用新默认:面板「其它 → 恢复默认」再「保存」,或删掉 Mods\LaneReader\Settings.json。

关于「最大硬抗 BPM = 850」:本机读不到谱面 BPM 时,拆手序会落到自己的兜底 800 BPM。 上限设成 850 时 800 不会被减半,base = 800;旧的 506 会把它减半成 base = 400。 base 越大,一个窗口里塞得下的音符越少,轮指就越少、换手也越少 —— 这是手感差异的来源。


自定义音符图片

把图片(png / jpg)放到 Mods\LaneReader\,面板「显示 → 用自定义音符图片」打开, 路径默认 note.png(相对路径基于 mod 目录,也可以填绝对路径)。 安装目录里带了一张演示图 note.png(圆角胶囊、半透明填充),可以直接开来试。

  • png 的 alpha 生效;jpg 没有 alpha
  • 有图时就只画那张图:不画原来的音符色块,也不画音符边框
  • 「保持宽高比」默认开(contain 居中,按平面音符框算,所以透视下也是这个样子被投影)
  • 「长按线条用自定义图片」是另一张图(默认 hold.png):图片下端 = 长按开头, 上端 = 尾巴;「重复间隔」默认 1 拍 = 每拍把图片首尾相接一次,份数按长按自己的拍数 四舍五入成整数(和下落速度无关)。判定线从下面吃、尾巴从上面切,两端都是裁掉
  • 换图不用重启:最多 1 秒后自动重读,面板上还有「重新加载」按钮

本地化 / Localization

面板支持多语言。中文是内置的 —— 代码里的中文原文就是 key,不需要任何语言文件;英文在 <游戏目录>\Mods\LaneReader\lang\en.txt。面板顶部有语言按钮,点它就在 lang\ 里找到的语言之间循环。

加一种语言:把 lang\en.txt 复制成 lang\jp.txt,左列一个字都别改(那是 key, 连行首的空格也算),把右列翻译掉,存成 UTF-8 —— 面板按钮重新扫目录,下一次点就能选到它, 不用重启。

  • 格式:每行 原文<TAB>译文,用制表符分隔。之所以不用 =:不少原文里本身就有 = (轨道长度 (px, 0=自动到屏幕顶)、间隔小于窗口 = 轮指;大于 = 食指交替。)。不含 = 的 key 也可以写成 原文 = 译文,手写更舒服。
  • # 或 ; 开头是注释;值里的 \n 表示换行;行首空格是面板的缩进,属于 key。
  • 开 / 关、四轨单独 / 四轨统一、主题名、手法名这些都要翻译(列表就在 en.txt 里)。
  • 语言存在 Settings.json 的 Language;留空(新装的默认)= 跟随游戏自己的语言 (非中文一律先落到英文)。某个语言没有文件时回落中文,不会变成空白。
  • 安装时 lang\*.txt 只在缺失时才复制,所以你改过的译文不会被覆盖; 想拿回仓库里那份,删掉文件再装一次。

开发与验证

  • HANDOFF.md:完整技术交接文档 —— 架构、每个踩过的坑、调试方法、门控规则。 三种手法的推导分别在 §14(拆手序)、§18(时间重置法)、§6.7.1(「度」标尺的更正)

  • LaneReader/tools/HandPlanCheck.cs:拆手序的离线 oracle, 把移植版与参考实现逐窗口比对(28 张谱面 / 17558 个窗口,决策与指法符号完全一致)

  • LaneReader/tools/TimeResetCheck.cs:时间重置法的离线校验, 与游戏无关,编译只要两个源文件:

    cd LaneReader\tools
    csc /nologo /target:exe /langversion:5 /out:TimeResetCheck.exe TimeResetCheck.cs `
        ..\src\LaneReader\TimeResetPlanner.cs ..\src\LaneReader\FingeringPlanner.cs `
        ..\src\LaneReader\DoublePressMerge.cs
    .\TimeResetCheck.exe 165                 # 内置表:均匀间隔 / 音型 / 长条 / 双押合并(不填按 300)
    .\TimeResetCheck.exe <dump> 165 [inner]  # 参考宏 dump 整首走一遍

版本变更要点

1.1.1

  • 面板顶部补一行说明:叠加层只在关卡里出现,菜单、选歌界面和设置面板里看不到(有不少人以为没生效); 下面再补一行**「叠加层状态」**,直接显示不画的原因(waiting: scrLevelMaker.instance is null 等)
  • 新增兜底驱动(OverlayWatchdog.cs):叠加层原本只靠 AsyncInputManager.Update/OnGUI 两个补丁,而 MonoBehaviour 所在的 GameObject 不激活就收不到回调 —— 那会让 mod 变成「面板能开、 叠加层永远不画、日志一行都没有」。现在 mod 自己创建一个 DontDestroyOnLoad 的 GameObject, 游戏那两个钩子安静 0.5 秒后由它接管(正常机器上完全不动);接管时把 GUI.depth 设到游戏 UI 之上
  • 新增排查日志:driver: game hooks alive/never fired/stopped firing、 overlay suppressed: Enabled=… hidden=…、idle: <原因>、chart reader disabled: <原因>
  • 排查方法记在 HANDOFF §21

1.1.0

  • 面板多语言(Localization.cs):中文内置(代码里的中文原文就是 key),英文在 lang\en.txt; 复制它、只翻译右列就能加任意语言;面板顶部按钮切换,语言文件缺失时回落中文
  • 首次运行跟随游戏语言(非中文 → 英文),选择记在 Settings.json 的 Language
  • 安装时 lang\*.txt 只在缺失时复制,改过的译文不会被覆盖

1.0.3

  • 新增双押合并(DoublePressMerge.cs,MergeRollMs 默认 50 ms,面板放在「排键(手序)」的 公共位置):间隔小于它的两颗音当作双押,后一颗移到和前一颗同一时刻;三种手法共用, 只移按键,长条尾巴不动;0 = 关
  • 「排键(手序)」里新增**「刷新手序」**按钮(手序没刷新时点一下);每次重算前会把音符时间 还原成谱面自己的,所以反复点结果稳定
  • 面板的手序介绍改成只显示当前模式;设置行之间加空行

1.0.2

  • 主轮之后的判定改看「上一个音」(主轮那一下本身)的间隔,不再回看它回应过的那颗主手音: 一次快速双击后隔 150ms 的音现在仍然换手(旧规则按「距上上个音 230ms」会判回主手); 长条头落在主轮时同理 —— 从尾音(松手)那一刻起算,长条后那颗音因此从 J/K 变成 F/D

1.0.1

  • 默认手法改为时间重置法(FingeringMode 默认 2)
  • 主手重置间隔默认 300 ms → 165 ms:刚好卡在 150 BPM 的 8 分音(200ms)之下 —— 8 分音用单指连点、16 分音(100ms)双手交替、更密的才进轮指

1.0.0

  • 新增第三种手法「时间重置法」:只看音符间隔的排键状态机,一个参数,不读 BPM/角度、不分组, 正面解决「三连音被 2^k 缩放切错、边界落进音组中间」的问题
  • 外轮收尾扫描、长条尾音永远同轨(链式长条整段同轨)、边界加 1ns 容差 (时间是 double,间隔正好等于 R 或 R/2 时 0.45-0.3 = 0.15000000000000002 会让判定逐音翻面)
  • 面板写明三种手法各自的优势与劣势;README 重写

许可

本项目以 MIT 发布,见 LICENSE。

排键算法移植自他人项目,版权与来源见 THIRD-PARTY.md: ReADOFAIMacro(MIT)与 AdoFalling。

About

A Dance of Fire and Ice 的 4K 下落式辅助读谱 mod:读取内存里的实时谱面,4 条下落轨道 + 两套自动排键算法(含移植 ReADOFAIMacro 的拆手序)

Topics

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages