Auto Otto 机制说明(公开版)
注:本文档面向机制讨论,只保留工程视角和设计边界。
它不会作为插件实现教程,也不用于指导复现自动通关行为。
这个插件里的“AI”不是机器学习模型,也不是外部识图脚本。它本质上是对《冰与火之舞》内部自动播放机制的一次本地研究:观察游戏运行状态,在合适的边界内让游戏沿用自身已有的自动播放路径。
它不应该被理解成“替玩家证明实力”的工具。更准确地说,它是一个用于分析游戏状态机、自动播放路径和运行时 Mod 架构的实验样例。
核心思路
游戏内部本来就存在自动播放相关逻辑。
插件做的不是从外部模拟键盘,也不是自己写一个节奏识别器,而是在游戏运行时观察当前关卡状态,并在游戏自身认为自动播放可以推进时,让游戏继续走内部自动播放流程。
下面的内容会尽量停留在架构、状态机和数据流层面,而不是展开到具体实现步骤。这样既能说明它为什么成立,也不会把讨论变成一份可直接照做的操作说明。
下面只说明概念和工程结构。
为什么它看起来能“完美”
普通玩家输入时,游戏通常会经过输入设备、输入映射、命中窗口、早晚判定等流程。
自动播放路径不同。它更接近游戏内部的一条特殊通道:当运行时状态满足自动推进条件时,游戏可以不依赖真实玩家输入,继续推进关卡状态。
因此它看起来像“完美”的原因,不是插件反应速度很快,也不是它预测了整首歌,而是它站在游戏内部状态之上,利用了游戏本体已有的自动播放路径。
可以抽象成这样:
真实输入路径:
玩家按键
-> 输入系统
-> 命中窗口判断
-> 早/晚/准
-> 推进或失败
自动播放路径:
游戏运行状态满足自动推进条件
-> 游戏走内部自动播放分支
-> 推进关卡
-> 触发视觉、音频和结算流程
这两条路径最后都会让关卡继续推进,但推进请求的来源不同:
真实输入来自玩家设备,自动播放来自游戏内部状态。
它什么时候出手
插件不是提前把整首歌算完,也不是按照固定时间表去按键。
它跟随游戏每帧运行,实时观察当前状态。
概念流程如下:
游戏每帧更新
-> 插件观察当前场景和状态
-> 判断是否属于允许工作的官方内容
-> 判断是否已经进入正式游玩阶段
-> 读取当前旋转对象和推进目标的运行时信息
-> 判断是否满足自动推进条件
-> 如果满足,让游戏走内部自动播放路径
这里故意不写真实函数名和字段名。
因为一旦公开具体调用点和数据字段,这份文档就不再是机制说明,而会变成实现路线图。
更细的自动击打流程
从工程角度看,它可以分成三层。
第一层是运行时接入层。
插件在游戏启动后作为扩展模块运行,不直接修改关卡文件,也不重打包游戏资源。它观察游戏运行状态,而不是预处理谱面。
第二层是状态判断层。
插件不会在所有场景都工作。菜单、编辑器、自定义关卡界面、结算流程、失败动画、场景加载等状态都应当排除。只有进入正式游玩阶段后,插件才会继续检查是否允许自动播放。
第三层是自动播放触发层。
插件不模拟键盘输入,也不依赖外部计时器猜节奏,而是观察游戏内部已经计算好的旋转状态和推进条件。当状态满足要求时,让游戏沿用自身自动播放路径推进一次。
可以抽象为:
Unity 运行时
├─ 游戏状态机
│ ├─ 菜单
│ ├─ 倒计时
│ ├─ 玩家控制
│ ├─ 结算
│ └─ 场景切换
│
├─ 关卡运行数据
│ ├─ 当前地板
│ ├─ 当前旋转对象
│ ├─ 旋转方向
│ ├─ 目标推进位置
│ └─ 当前命中上下文
│
└─ 插件观察层
├─ 判断是否允许工作
├─ 判断是否处于游玩阶段
├─ 判断是否接近自动推进条件
└─ 请求游戏走内部自动播放路径
这个结构的重点是“同步”。
插件必须跟随游戏自己的状态机工作,而不是另起一套独立节奏系统。这样可以减少窗口焦点、系统输入延迟、帧率波动、暂停恢复等外部因素的影响。
角度驱动,而不是时间驱动
ADOFAI 的核心不是单纯时间轴,而是旋转对象围绕地板运动,并在目标位置完成切换。
因此,自动播放更适合从“当前旋转状态是否满足推进条件”来理解,而不是从“第几秒按一下”来理解。
时间驱动方案类似:
歌曲播放到某个时间点
-> 认为这里应该按
角度驱动方案类似:
当前旋转对象接近目标推进位置
-> 游戏状态满足自动推进条件
外部时间表通常会遇到很多问题:
- 音频偏移
- 帧率波动
- 暂停和恢复
- 速度变化
- 关卡事件改变节奏
- 不同设备上的输入延迟
运行时状态观察方案直接跟随游戏内部状态,因此更稳定,也更接近游戏自身自动播放机制的工作方式。
这里更值得关注的是“跟随游戏状态”这个思路,而不是某个具体数值。具体数值会随实现、版本和边界设计变化,不适合放进公开说明里。
自动推进之后发生什么
插件不是直接改存档,也不是把关卡结果硬写成已完成。
更准确地说,它仍然让游戏实际加载关卡、倒计时、播放音乐、推进地板、触发事件、进入结算。区别只是:在需要输入推进时,它让游戏内部自动播放路径接管一次推进。
所以它更接近:
让游戏自己的自动播放机制跑完整关
而不是:
直接把存档改成已通关
这也是为什么它能触发关卡内事件、胜利流程和游戏自己的下一关逻辑。
为什么不用真实按键
插件没有使用系统级键盘模拟。
原因很简单:模拟键盘会受到窗口焦点、输入法、帧率、输入延迟和平台差异影响。
游戏内部已经存在自动播放路径。
从研究角度看,观察并理解这条内部路径,比在外部模拟按键更稳定,也更能解释游戏本体 Otto 的行为。
因此公开讨论更适合停留在路径差异和工程取舍上,而不是展开具体入口。
自动开始和自动进入下一关
从体验上看,插件还可以补上两类流程控制:
- 关卡开始时跳过等待玩家输入的阶段。
- 关卡完成后交还给游戏自己的结束流程。
这里的重点是流程边界:结束流程应当交还给游戏自身,而不是由外部工具硬改结果。
排除创意工坊 / 自定义关卡
插件的边界设计比核心触发逻辑更重要。
它应当排除:
- 关卡编辑器
- 自定义关卡选择界面
- 创意工坊内容
- 自定义关卡路径或自定义关卡 ID 已存在的场景
- 非正式游玩的菜单或过场场景
这些限制不是技术上做不到,而是为了让这个实验停留在本地研究和机制分析的范围内。
F8 隐藏自动播放状态文字
游戏在自动播放开启时,会显示绿色的“自动播放”状态文字。
这个文字不是插件自己画的,而是游戏本体 UI 的一部分。
插件中的 F8 只用于切换这个状态文字是否显示。
它不会关闭自动播放,也不会影响关卡推进。
概念上可以理解为:
自动播放仍然运行
├─ 显示状态文字
└─ 隐藏状态文字
隐藏状态文字只是 UI 层面的处理,不等于关闭自动播放。
这里同样只讨论 UI 行为,不展开具体组件和接入位置。
工程上的难点
从外面看,“自动播放”像是一个开关;实际做起来更麻烦。
主要难点包括:
- 时机问题:太早触发没有意义,太晚触发可能错过推进窗口。
- 场景问题:同一套控制器可能在菜单、关卡、DLC 地图里复用。
- 状态问题:倒计时、玩家控制、胜利、失败都需要区分。
- 重复调用问题:Unity 一帧内可能出现多次相关更新,插件要避免重复推进。
- 边界问题:官方关、自定义关、编辑器、工坊内容要分开处理。
- 显示问题:自动播放状态文字属于游戏自己的 UI,不一定由插件绘制。
这些都是工程上的粘合问题。
核心逻辑可能很短,但让它只在正确场景、正确状态、正确时机工作,才是实际复杂度所在。
公开讨论的边界
这类项目比较适合讨论“结构”,不适合讨论“落点”。
适合公开讨论的是:
状态机、数据流、输入路径差异、边界设计、风险控制、工程取舍
不适合公开展开的是能把这些结构直接落成可执行工具的实现路线。
这样的边界不是为了把技术说空,而是为了让讨论保持在机制分析层面:读者能理解这个实验为什么成立,但不能仅凭文档拼出一个自动通关器。
为什么仍然有技术价值
即使不公开完整实现,这个项目仍然有技术讨论价值。
它涉及到:
- Unity 游戏运行时状态观察
- 游戏状态机理解
- Mod 插件生命周期管理
- 自动播放路径与真实输入路径的区别
- UI 状态提示和隐藏
- 对自定义内容和官方内容的边界区分
- 技术实现与社区风险之间的取舍
这些内容可以作为逆向工程、Mod 架构和游戏机制分析的案例来讨论,但不需要把可直接滥用的实现细节公开出来。
换句话说,公开版文档的价值不在于“教你做一个自动通关器”,而在于展示一个游戏机制分析问题可以如何被拆解:
现象:游戏有自动播放能力
问题:自动播放依赖哪些运行时状态
分析:输入路径和自动路径有什么不同
边界:哪些场景不应介入
风险:哪些细节公开后会导致滥用
结论:讨论机制和边界,而不是发布可执行路线
这仍然是技术讨论,只是刻意停在不会直接伤害社区公平性的层级。
关于是否公开发布
我不打算公开发布这个插件的成品,也不打算把它包装成可以随手安装的一键工具。
原因不是想故意藏着,也不是想靠“不公开”制造神秘感,而是因为这个插件的能力很容易被拿去作假。它可以让游戏自动完成官方关卡,如果被随意传播,可能会被用于伪装手打成绩、刷成就、误导他人,甚至影响社区里关于通关、完美、练习成果的正常讨论。
这个插件更适合被当作一个本地研究样例:理解 ADOFAI 的 Otto 自动播放机制、理解 Unity 运行时 Mod 如何接入游戏状态、理解自动播放路径为什么能成立。它不适合被当作“自动通关工具”公开推广。
如果有人质疑“为什么不公布”,我的立场是:
不公开不是因为这个东西多高深,而是因为它太容易被滥用。
技术原理可以讨论,风险也应该说清楚,但我不想提供一个能直接拿去伪造成绩的工具。
换句话说,这不是技术保密,而是使用边界。
我可以说明它的机制和实现思路,但不会鼓励把它用于排行榜、成就炫耀、成绩证明或任何会让别人误以为是手打的场景。