· engineering· 约 23 分钟精读
Qubitlab Offline Instant Feedback Channel Engineering — Technical Analysis | KeyRotate Technology
一个不联网、不注册、包内没有音频文件的教学沙盒,怎么让学习者在拖动参数的瞬间知道自己做对了还是做偏了?本文以密旋科技 QubitLab 的真实源码为样本,做一次反馈调用普查,拆开声音、触觉、视觉三条通道的实现与失效条件,并说明那张参数提示卡为什么只报区间、不报距离。
一、看不见概率的人,靠什么判断自己做对了
在手机上做一次微观物理实验,和在学校实验室里拧旋钮是两回事。真实现场里,旋钮有阻尼、示波器会抖动、器件会发烫,操作者靠身体感受就能建立因果关系;而在移动端沙盒里,设备与算法之间只有一块玻璃。学习者拖动一个数值,屏幕另一侧发生的是概率幅的变化——他看不见那个概率,只能看见读数、提示文字,以及按下按钮时手上与耳边的那一点动静。
密旋科技的 QubitLab 是一款面向 App Store 的移动端量子物理与 STEM 启蒙沙盒:96 关闯关、可交互的布洛赫球面、双比特逻辑门拼装,全部离线完成。本文不评价它的趣味,而是把”即时反馈”这件事拆开看:三条通道各自由什么构成、调用点分布在哪里、在什么条件下会失效,以及那张参数提示卡为什么故意不把话说满。
二、先做一次反馈调用普查
要谈反馈通道,先看它在代码里长什么样。把整个工程扫一遍——62 个 Swift 源文件、约 3.2 万行代码——反馈调用集中在两个封装里:一类管触觉,一类管声音。按调用点逐项统计如下。
| 通道 | 语义档位 | 调用点数 | 承担的场景 |
|---|---|---|---|
| 触觉 | 轻档冲击 | 40 | 重置、切换、列表选择等可预期动作 |
| 触觉 | 警示通知 | 34 | 结论未达标、参数越界 |
| 触觉 | 确认冲击 | 34 | 参数确认、按钮提交 |
| 触觉 | 庆祝通知 | 28 | 通关与关卡结算 |
| 触觉 | 成功通知 | 25 | 子目标达成 |
| 触觉 | 重档冲击 | 18 | 碰撞、坠落、失败事件 |
| 触觉 | 中档冲击 | 14 | 一般确认 |
| 触觉 | 失败通知 | 1 | 诱骗态校验未通过(全工程唯一一处) |
| 声音 | 点击音 | 38 | 离散动作 |
| 声音 | 胜利音 | 26 | 通关 |
| 声音 | 星级唤醒音 | 13 | 星级写入 |
| 声音 | 星级达成音 | 0 | 已定义,无调用点 |
合计:触觉调用 194 处,声音调用 77 处;触觉散落在 28 个视图文件中,声音集中在 8 个文件里。这张表里真正有信息量的不是总数,而是三个比例。
第一,轻档冲击最多,有 40 处。这说明反馈的主体用途是”我收到了你的操作”,而不是”你做对了”。开关、重置、切换这类动作在物理上没有后果,在心理上却需要即时确认,否则用户会怀疑触摸是否被识别。
第二,警示通知(34 处)多于成功通知(25 处)。实验类任务里,未达标的次数天然多于达标;如果每次未达标都给强反馈,学习过程很快会变成挫败练习。实现上两者同属通知类触觉,差别只在语义,强度没有差别——这是一处刻意的收敛。
第三,失败档只有 1 处,留给”诱骗态平均光子数超出安全范围”这种系统性参数错误,而不是”这题没做对”。报错反馈被压到最少,是为了不把正常试错标注成失败。
还有一处可以直接指出的事实:星级达成音定义了却没有调用点。这类”已声明未接线”的接口在迭代里很常见,它不产生运行时开销,而下面要讲的另一处留白比它更明显。
三、声音通道:四枚系统标识,包内没有音频文件
QubitLab 的音效不来自任何随包分发的音频文件,而是调用系统提供的播放接口,传入四枚系统标识,分别对应点击、星级唤醒、星级达成与胜利。
这样做的工程好处可以直接说清。其一,应用包里没有音频资源文件,音效由系统播放,不需要文件读取、解码与缓存,也不占下载体积。其二,不需要配置音频会话:播放系统标识不要求应用声明会话类别,因此不会打断用户正在播放的音乐,也不会因为会话切换带来额外状态。其三,四枚标识就是四个调用,没有资源清单要维护,也就没有”某个音效文件漏打包”这类发布事故。
对比一下同一应用里的另一条音频路径,取舍会更清楚:课程视频播放器在开始播放前会把音频会话显式设为播放类别、影片播放模式,并选择降低其他音频的选项;停止播放时再把会话交还给其他应用。两条路径分开是刻意的——一次短促的操作反馈不该抢走音频会话,否则一次点击就会变成对用户音乐播放的干扰。
代价同样要写清楚:
- 出不出声由设备侧决定。静音开关状态、系统音量与辅助功能设置共同决定结果,应用既无法查询”刚才那一声是否真的响了”,也无法强制发声。
- 系统标识是平台内部编号,不是公开文档中的稳定契约,音色可能随系统版本变化;也无法停止、淡出或循环播放。
- 因此它不能作为语义的唯一载体:产品里不存在”听声辨状态”的设计,自动化测试也无法断言音效真的响过。
四、触觉通道:一次调用一个发生器,与预热窗口的长度
触觉比声音多一层要求:必须在触发之前让振动硬件进入待命状态。工程里的做法是每次反馈新建一个发生器实例,先调用预热,再立刻触发,然后不保留这个实例。
按 UIKit 的语义,预热的作用是让振动硬件提前启动,缩短从调用到手上感到振动的延迟;而硬件待命只维持很短一段时间,之后系统会收回。由于实例没有被复用,跨页面的”续热”并不存在——预热只对紧接着的那一次触发起作用。这种写法换来的是无状态:没有共享实例,就没有状态污染与泄露风险,代价是每一次反馈都要重新承担那点启动延迟。
档位分两组:冲击类三档(轻、中、重)与通知类三档(成功、警示、失败)。从普查看,分档与场景的对应相当稳定:轻档留给重置与切换这类可预期动作,中档留给确认,重档留给碰撞与坠落;通知类承担”结论对不对”。在通关这一类关键节点上,触觉与声音会一起给出——26 处通关点同时触发庆祝触觉与胜利音效,另外 2 处只给触觉,构成两条通道之间的冗余。
边界有三条。没有振动硬件的设备上,这些调用不会产生任何效果(模拟器与部分平板机型即属此类);系统里关闭系统触觉后同样静默;因此触觉只能算增强通道,不能算承载通道——任何只靠触觉传达的信息,在上述条件下都会丢失,界面必须保留等价的可见表达。
五、视觉通道:一张提示卡,七个输入,四件被画出来的东西
三条通道里,只有视觉具备足够的表达带宽,所以参数提示卡是唯一被认真设计过的通道。它的输入有七个:参数名、公式、当前值、目标值、进度(0 到 1 的亲近度)、位置(低于目标区、在目标区、高于目标区)与提示文字。而它真正画出来的只有四件东西。
| 输入字段 | 是否绘制 | 画面上的形态 |
|---|---|---|
| 参数名 | 是 | 卡片左上角,配一个灯泡图标 |
| 当前值 | 是 | 右上角胶囊内的等宽数字 |
| 提示文字 | 是 | 卡片中部的一行说明 |
| 位置 | 是,仅用颜色 | 外框描边:橙、绿、红三色之一 |
| 公式 | 否 | 签名中声明,界面不使用 |
| 目标值 | 否 | 签名中声明,界面不使用 |
| 进度 | 否 | 初始化时被裁剪,之后不再读取 |
第四行值得单独说:位置三态只通过外框描边颜色表达,描边取状态色的三成半透明度,底色保持固定的深色。也就是说,“我离目标多远”这件事被有意省略了——卡片告诉你身在哪个区,不告诉你离边界还有多少。进度的取值在初始化时会被夹到 0 与 1 之间,防止非法输入把布局撑坏:
但它从未参与绘制。调用点的代码仍然逐处计算这个亲近度,有几处干脆直接填了常量,这恰好说明该字段目前只是签名占位。数值文本则用等宽数字呈现,避免逐帧变化时左右抖动;提示文字按内容换行而不做截断,宁可把卡片撑高,也不让一句话被切掉半截。
这种”话不说满”的设计有明确的教学意图:如果给出连续进度条,任务就会退化成滑块对表——学习者不再追问为什么这个取值能穿过势垒,而是把进度条推到底。代价也要承认:只报区间、不报距离,就缺少”你正在接近”的正向反馈,试错次数偏多的关卡里,学习者只能靠数值与文字自行判断是否在收敛。
另有两处接线情况必须写实。第一,八个闯关小游戏各自声明了一份逐关反馈元组(参数名、公式、当前值、目标值、进度、位置、提示),但扫描全部源码后,这八处声明没有任何引用;真正把提示卡挂到界面上的只有一处——康普顿散射的观测反馈,并且带了一个可访问性标识供自动化检查。放射性时钟对照那一关用的是自己的一套文字反馈块。共享组件与逐关反馈文案之间,存在一层没有接上的断口:这类声明是纯计算属性,不引用就不执行,不留运行时开销,但”可复用的反馈组件”这件事目前只完成了一半。
第二,三态标签自带图标与本地化标签两个属性,全库没有一处引用——颜色是当前唯一的三态区分手段。从无障碍角度看这并不够:仅凭颜色区分状态,属于用色条款要规避的做法。实际风险被摊薄是因为提示卡旁边通常紧邻数值与提示文字,状态可以间接读出;但严格地说,颜色仍是唯一直接通道。
六、时机:反馈落在离散动作与判定出口上
反馈的第二个工程维度是”什么时候给”。扫描调用点可以看到两条规律。
其一,它们几乎都落在离散动作里,而不是连续手势里。重置、切换、提交这类按钮的动作闭包内有反馈,参数拖动过程本身不触发触觉。好处是天然去抖,不会出现每帧一次振动、把反馈变成噪音的情况;代价是拖动过程中没有连续触觉提示——要找到共振点,只能靠数值与提示卡,而不是靠手感。
其二,结论类反馈与判定写在同一个出口处。通关时先把星级写入,再给出庆祝触觉与胜利音效,两者同拍发生,不会出现”声音响了但星级没记上”这种错位。这种”先落数据、再给反馈”的顺序,也让自动化走查有稳定的断言点:状态变化先发生,反馈只是它的伴随现象。
家长门也是这条通道的使用者:答对给成功触觉并放行,答错给警示触觉并重新出题。这里有一处能力边界值得写清:这道门用两位数的乘法题把随手点击的儿童挡在外面,出题范围有限、答错没有次数上限、走查参数与调试构建都能绕过它。它保护的是流程——避免未成年人误入付费与数据处置入口——而不是安全边界;把它当作身份验证使用,是超出它能力的用法。
七、离线、隐私与可核查性
三条通道全部在本机完成:系统音效由系统播放,触觉由本机硬件产生,提示卡由本机数据渲染,没有任何一步需要网络。这与应用的离线承诺一致——数据不出设备,也就不存在第三方遥测。代价是明确的:没有埋点就没有反馈使用数据,无法统计哪个档位最常被触发,也无法用界面实验量化反馈对学习效果的影响。要保留隐私,就必须放弃这类度量。
可核查性走的是另一条路。整个工程注册了 122 个唯一的可访问性标识,配合启动参数(走查模式会跳过家长门的算术校验,调试构建提供自动求解入口),自动化测试可以在没有网络、也没有真实硬件的条件下稳定驱动这些路径:进入关卡、把参数拖进目标区、触发结算、打开家长报告。这里同样要说明上限:触觉与音效在测试环境里不产生可观测信号,可断言的只有状态变化与可见文本,因此自动化覆盖的是”反馈被触发时的条件与结果”,覆盖不了”反馈本身是什么感觉”。
| 通道 | 依赖 | 可被系统关闭的情形 | 可否自动断言 | 关闭后的等价表达 |
|---|---|---|---|---|
| 触觉 | 本机振动硬件 | 无振动硬件的设备、系统触觉开关 | 否 | 数值与提示文字 |
| 声音 | 系统音效库 | 静音开关、音量与辅助功能设置 | 否 | 视觉提示卡 |
| 视觉 | 本机渲染 | 颜色不可用时须保证文本可读 | 是 | 数值与状态文字 |
八、这套反馈设计的能力边界
把前面几节收拢成一份清单。
能够做到的:三条通道互相冗余,任何一条失效都不会让学习者”不知道发生了什么”;反馈落在离散动作与判定出口上,天然避免了高频轰炸;提示卡把”身在哪个区”说明白,同时不把答案交出去;先落数据再给反馈,保证记录与感受不会错位。
做不到的至少有四条。第一,不能保证声音与触觉一定呈现,设备状态与系统设置说了算,应用无法查询结果。第二,不能给出”离目标多远”的连续提示,因为进度字段没有被使用,也没有定义过该用线性还是对数尺度来表达亲近度。第三,不具备反馈层面的数据回流,无法量化体验优化,也拿不出哪一档反馈更有效这类证据。第四,只用颜色区分三态,严格说未满足用色条款,只能依靠旁边的文字与数值兜底。
这四条不是缺陷清单,而是这台设备在没有服务端、没有随包音频资源、没有云端的条件下,对”即时反馈”能给出的答案:用最少的外部依赖做出可感知的响应,再把做不到的部分如实标注出来。
九、结语
离线优先的承诺是数据不出设备。顺着这条承诺往下走会发现,被留在设备里的不只是记录,还有”怎么让用户知道自己做对了”这件事:既然没有服务端下发素材、没有网络请求可以等待,反馈就必须由端侧自己合成——音效借系统的、触觉借硬件的、解释留给一张本地渲染的提示卡,而每一处借来的东西都有它自己会失效的条件。把这三条通道各自的边界写进工程说明,比声称”反馈体验”更有用:一个学习者手上那一点振动、耳边那一声轻响、卡上那一行文字,本来就不是锦上添花,而是他在看不见概率的世界里唯一的坐标系。
参考文献
[1] Apple Inc., UIFeedbackGenerator — Preparing the Generator. https://developer.apple.com/documentation/uikit/uifeedbackgenerator [2] Apple Inc., AudioServicesPlaySystemSound(:). https://developer.apple.com/documentation/audiotoolbox/1405202-audioservicesplaysystemsound [3] Apple Inc., AVAudioSession — setActive(:options:). https://developer.apple.com/documentation/avfaudio/avaudiosession/setactive(_:options:) [4] Apple Inc., Human Interface Guidelines — Playing audio. https://developer.apple.com/design/human-interface-guidelines/playing-audio [5] W3C, Web Content Accessibility Guidelines (WCAG) 2.2, Success Criterion 1.4.1 Use of Color. https://www.w3.org/TR/WCAG22/#use-of-color [6] Apple Inc., Audio Services — System Sound Services. https://developer.apple.com/documentation/audiotoolbox/audio-services