假设一个体感拳击关卡同时收到两条反馈。一条说:“我的拳都打完了,角色才慢悠悠地出手。”另一条说:“我只是抬手挠了下头,角色就打出了一记直拳。”如果把识别调得更灵敏,第一条会好转,第二条会更多;把识别调得更保守,情况正好反过来。很多体感游戏就在这两条反馈之间来回摇摆。九游体育的思路是不再只拧一个旋钮,而是把“快”和“准”拆到识别链条的不同环节,各自用合适的方法解决。

Camera → Pose → Motion → Game Input → Response

延迟和误识别,为什么总是此消彼长

摄像头常见的采集帧率是每秒30帧或60帧,以30帧计,每一帧之间相隔约33毫秒。系统想要更确定“这是一次出拳”,最直接的办法就是多看几帧;但每多等一帧,角色的反应就晚一帧。反过来,只看一两帧就下结论,挠头、整理头发、扶眼镜这些同样有快速手部位移的动作,都可能被当成出拳。

所以问题的关键不是“等几帧”,而是在等待的这段时间里,系统能不能先做一些不会出错的事,把真正不可逆的决定留到有把握之后。

提高帧率也是一种办法:同样等待6帧,60帧采集只需要约100毫秒。但不是每台手机都能在60帧下稳定运行姿态模型,发热后还可能掉帧,帧间隔忽长忽短反而让速度计算失真。因此帧率只是基础条件,真正的取舍仍然要靠后面几层来完成。

姿态层:让滤波器自己判断该平滑多少

姿态模型逐帧输出的关键点带有轻微抖动。普通的低通滤波可以去掉抖动,却会让所有动作都拖出一段尾巴,快速出拳也被“抹慢”了。九游体育在关键点平滑上采用了类似 One Euro Filter 的思路:滤波的截止频率随关键点移动速度变化。

  • 手部几乎静止时,截止频率低,平滑很强,站立时的细微抖动被压住,不会误触发。
  • 手部快速移动时,截止频率升高,平滑变弱,出拳的轨迹几乎不被拖延。

这种滤波的两个参数分别控制静止时的平滑强度和对速度的敏感程度,需要按动作类型分别调整:拳击关卡偏向灵敏,瑜伽类慢动作关卡偏向平稳。与此同时,置信度(Confidence)偏低的关键点不参与速度计算,避免遮挡造成的位置跳变被误读成一次高速动作。

动作层:在滑动窗口里看趋势,而不是看某一帧的峰值

单帧里手腕速度很高,并不能说明是出拳。系统会把最近若干帧组成一个滑动窗口,在示例设定中,30帧采集时取6到8帧,约200多毫秒,并在窗口内同时检查几项特征:

  • 手腕是否持续朝向前方(朝向镜头或对手方向)移动,而不是向上或向侧面。
  • 肘部角度是否在持续打开,也就是手臂在伸展。
  • 肩部是否有配合的前送或转动。

挠头的动作在第一项上就不成立:手腕是向上、向头部移动的,肘部反而在弯曲。窗口会每帧向前滑动一次,所以系统并不需要等窗口“填满”才开始判断,而是每一帧都在更新自己的把握程度。

游戏输入层:快速触发加确认的双阈值状态机

把“开始反应”和“确认出拳”分成两个门槛,是兼顾速度和准确的核心。每只手都有一个独立的状态机:

状态进入条件(示例设定) 游戏里发生什么
待机默认状态角色保持防守姿势
候选 手腕前向速度连续两帧超过低阈值 播放起手动画,不结算任何结果
确认窗口内肘部伸展和前向位移同时超过高阈值 出拳命中判定,结算伤害与音效
取消 候选状态超过时限仍未确认,或方向不符角色自然收手,不消耗体力
冷却 确认之后 手部速度降到释放阈值以下,才能再次触发

低阈值负责“快”,让角色在玩家刚开始出拳时就有反应;高阈值负责“准”,决定这一拳是否真的算数。冷却状态里的释放阈值低于触发阈值,这种回滞设计可以防止一次出拳在阈值附近抖动时被连续算成两拳。

阈值本身也不是一个固定数字。手腕速度以玩家的臂长归一化,也就是用“每秒移动了几个臂长”来衡量,这样身材较小的儿童和臂展较长的成人使用同一套相对门槛。勾拳和上勾拳的主要运动方向不是向前,状态机会换用横向或向上的速度分量,确认条件也相应改为肩部转动和肘角保持。

反馈层:确认之前,只做可以撤销的事

双阈值能成立,前提是游戏在候选阶段做的事情都可以撤销。起手动画、角色重心前移、拳套的轻微发光,这些即使被取消也不会让玩家觉得“被误判”;而命中音效、伤害数字、对手的受击硬直,都必须等到确认之后才出现。一旦把不可逆的反馈放进候选阶段,误识别就会直接变成玩家看得见的错误。

调参时,九游体育会同时观察两个数:候选被取消的比例,以及确认之后被玩家回放判定为误识别的比例。取消比例过高,说明低阈值太敏感,起手动画频繁闪现;误识别比例升高,说明高阈值偏松。两个数分开看,比只看一个“识别准确率”更能说明问题出在哪一层。关于整条识别链路每一步的耗时,可以参考挥拳以后角色应该在什么时候出拳;更完整的处理流程见体感工作流拆解。

文中帧数、阈值与窗口长度均为示例设定,实际参数会随设备帧率、动作类型和关卡节奏调整。