音游判定原理详解——从触摸屏幕到判定音符【Project SEKAI攻略】
“音乐游戏”一般简称为“音游”,玩家需要配合音乐的节奏来进行一定的动作。
《Project SEKAI》作为一个“移动端音游”,绝大多数玩家会使用手机、平板电脑等移动设备的触摸屏进行游玩,也有极少数的玩家不按常理出牌,使用手台、键盘乃至于触控板等非常规输入设备进行游戏。同时,作为“下落式音游”的一员,在游戏中音符会从界面的上方落下,玩家需要根据音符时机和类型在正下方的判定区域做出一定的操作,当音符与判定线完全重合时,便是音符完美的判定时机。
本篇文章作为音符具体判定机制分析的前置攻略,将会以使用Unity引擎编写的《Project SEKAI》为例子带领各位读者简要了解一下一款音游是如何实现判定机制的,或许对其他音游也能有所启发。为了便于理解,下文会尽量避免对程序代码的讨论,仅说明基本原理与过程,部分细节可能不够严谨,还请谅解。
本篇文章主要分为3个部分:
第1部分《从触摸屏幕到触摸对象》,介绍操作系统和Unity框架处理触摸事件的流程
第2部分《从触摸对象到触摸处理》,介绍游戏的主循环和触摸事件处理逻辑
第3部分《从触摸处理到音符判定》,介绍音符的判定时间、下落速度、状态机以及判定过程
一、从触摸屏幕到触摸事件
1.1 触摸事件生成与传递
触摸屏作为移动设备上最主要的输入设备,当用户触摸时,硬件会以一定频率对屏幕进行触控采样,采样过程主要是获取用户触摸的位置信息,驱动程序会获取来自硬件的事件,经过处理后将触摸位置的坐标、用于追踪手指的追踪ID等信息生成内核事件。越高的触控采样率会让硬件采样时间间隔变短,对手指运动的追踪也更为准确。例如,iPad Pro提供了240 Hz的触控采样率,代表每秒钟会从触摸屏中采样240次,约4毫秒1次。
在程序的执行过程中,如果需要获取触摸事件,会通过声明对触摸事件的监听器来实现。当触摸事件来临后,操作系统会从内核中取出相关事件,并经过一定处理和封装后,将触摸事件传递给正在监听这一事件的程序进行处理。
这一过程可以简化为下图:

操作系统封装的触摸事件有以下程序关心的内容:
触摸动作:操作系统会识别出按下、移动、松开等动作
触摸位置:触摸位置的坐标
历史触摸位置:上一次触摸事件的触摸位置坐标
事件时间:触摸事件发生的时间
触摸点编号:为当前每个触摸点分配单独的编号
1.2 记录触摸事件
Unity作为游戏引擎,会为游戏预处理来自操作系统的触摸事件,这样做的目的是使用Unity引擎的游戏可以使用统一的接口获取输入事件,有效减少了实际运行的操作系统给游戏带来的差异性。
当接收到触摸事件时,Unity会记录这些触摸点的许多信息,游戏着重关注以下内容:
触摸阶段:按下、移动、按住不动、松开等阶段
触摸位置:触摸位置的二维坐标,以屏幕左下角为原点
手指编号:会为正在触摸的手指各分配一个编号
位移向量:如果产生了一定滑动位移,将会记录滑动的位移向量
例如,一根手指在极短时间内快速执行了“按下”、“移动”、“松开”的过程,Unity会记录下3个“手指编号”相同、“触摸阶段”不同的触摸事件:

二、从触摸事件到触摸处理
2.1 游戏主循环
在设计游戏框架时,需要一个游戏主循环(Game Loop)每隔一段时间循环处理游戏逻辑,比如完成音符判定、音符移动位置等。不过,无论使用什么策略来确定间隔时间,由于硬件的性能限制,是很难做到完全等间隔的。例如,如果在这一步固定让音符位移10像素会因为时间间隔不均匀导致无法匀速运动,需要根据和上一次处理的时间差来进行进一步的运算。

为了方便起见,大部分游戏会以“1帧”作为时间间隔进行循环,“帧”指的是游戏中的单幅画面,例如帧率为每秒60帧代表1秒中之内会有60个画面。同样的,每帧之间的时间间隔并非完完全全固定,也会因为性能原因产生波动。值得一提的是,并非所有游戏会在每帧都处理一遍游戏逻辑,例如知名的沙盒游戏《Minecraft》,无论显示帧率有多少,会以每秒20游戏刻的固定速度进行游戏更新,当然如果卡顿了也会导致游戏刻变少。
同样在Unity引擎中游戏主循环也不需要开发者自己来实现,Unity为游戏提供了2种不同的循环模式:每帧处理一次、与帧率不同的固定频率处理。《Project SEKAI》采用了每帧更新一次的策略,判定相关的逻辑也是每一帧执行一次。
2.2 触摸事件获取
Unity收到操作系统传递的触摸事件后,做的事情仅仅是记录触摸事件,并不会在收到事件后立刻交给游戏处理,需要等待游戏在处理游戏逻辑时主动获取上一帧之后的触摸事件。特别的是,虽然操作系统在传递触摸事件的时候提供了事件发生的时间,但Unity既不会记录事件发生的时间也不会记录收到事件的时间。
例如,玩家在第k帧后立即进行触摸操作,在触控采样后这一事件进入Unity引擎,但游戏到了第k+1帧的时候才能得知这一事件,且游戏认为按下的时间是第k+1帧的时间,而非实际按下的时间:

这样的设计使得Unity引擎下的音游在判定时会产生与真实输入的时间差,会导致“拖判”更容易发生,这一时间差的最大值为两帧的间隔时间,帧率越高间隔越小,拖判也越不明显。
为了从根源上解决拖判的问题,游戏可以使用“Native Touch”插件等手段绕开Unity的触摸处理逻辑,直接接收操作系统的触摸事件。遗憾的是,可能是碍于技术水平或者是性能的问题,《Project SEKAI》并没有进行类似的改进,而是直接使用了Unity提供的触摸事件。更为遗憾的是,虽然提高游戏帧率能在一定程度上改善拖判的问题,但拥有120帧显示屏的iPad Pro却被锁死在了60帧,不得不接受拖判的现实。
2.3 触摸事件处理
无论使用什么方法获取到触摸事件后,都需要对输入事件进行进一步的处理。作为一个下落式音游,一个非常重要的处理就是找到点击位置对应的下落轨道。
《Project SEKAI》将主要的游戏区域分为12根轨道,Unity作为一个支持3D的引擎,这些轨道在三维空间中倾斜一定角度,通过近大远小的视觉效果,给玩家产生一种音符从远处逐渐接近判定线的“距离感”。当玩家触摸判定线及其上下位置时,会通过Unity将二维的屏幕坐标换算为点击位置的三维空间坐标,并使用这一坐标计算对应的轨道。值得注意的是,这一步得到的轨道并非诸如第1轨、第2轨的某一条轨道轨道,而是类似在第1轨的左起33%这样的准确位置。《Project SEKAI》每帧至多处理10个触摸事件。
例如,假设只有2条轨道,下图所示的这条线便是第2轨的50%位置,这条线并非在平面上垂直于判定线,而是在空间上垂直:

当然,在具体实现上可以认为每个轨道的宽度为1,直接以1个小数来表示触摸事件的轨道位置,例如2.5等。
虽然《Project SEKAI》有12根轨道,但实际上音符会拥有一定宽度以达成某种意义上的“无轨下落”,音符可以通过左侧和右侧所在的轨道位置来表示其位置和宽度信息。
三、从触摸处理到音符判定
3.1 音符判定时间
音游讲究的是玩家操作与音乐节奏的配合,一般来说会根据玩家完成操作的时机和音符与判定区域重合的时间进行对比,根据时间差给予一定的结果,这一过程称为“判定”,部分音游也会允许玩家微调判定区域的位置。判定后,根据不同的判定结果可能会获得不同的分数,也可能会发生减少生命、断COMBO等惩罚措施。
例如,《Project SEKAI》会提供PERFECT、GREAT、GOOD、BAD、MISS五个判定结果,GREAT会减少得分、GOOD既会减少得分又会断COMBO、BAD和MISS不仅断COMBO还会扣除生命且不得分。
每种音符有着自己的具体判定时间范围,甚至同一判定在延后和提前的情况下时间范围也不一定相同,但大体上可以总结为下图,比例仅作示意不代表真实比例:

可以看出,两侧的BAD判定时间范围决定了音符最大的判定时间范围,比BAD提前不会进行任何判定、比BAD还延后就会得到MISS。
3.2 音符下落速度
作为一个下落式音游,音符需要从屏幕上方逐渐“下落”到达屏幕下方,大部分音游都提供了调速功能,可以改变音符下落的速度。《Arcaea》等部分音游也会根据BPM的变化来改变音符实际下落速度。
《Project SEKAI》为玩家提供了[1.0, 12.0]区间内步长为0.1的下落速度调速。然而,12速的下落速度并不是1速的12倍,实际上音符从出现在轨道中开始直到到达判定线的时间(以下简称“下落时间”)与下落速度有以下关系:

也可以通过描点法画一个直观的函数图像,请注意这并不是一个连续函数且有定义域的限制:

可以看出,下落时间最长为4秒(1速)、最短为0.35秒(12速),速度约为11.4倍。下落速度决定了音符何时进入画面,只有已经出现在画面中的音符才可以参与到判定过程中,即使音符判定时间范围为无穷大也不可以盲点还未进入屏幕的音符。
3.3 音符状态机
为了控制音符从初始化到消失的生命周期,游戏需要一些方法来管理各个音符,给予每个音符高度定制化的状态便是一个非常好的选择,这些状态之间可以根据预设的条件进行转移,并可以画出直观的状态转移图。
例如,如果要设计一个需要点击3次才能完成判定的音符,可以画出如下状态转移图:

在状态转移图上,圆圈代表状态、有向弧代表状态之间的转移,有转移条件并可以在转移时执行一些操作(比如进行判定)、从空白地方指向的状态代表初态(上图中的“等待”)、同心圆代表终态(上图中的“结束”)。到达终态后,一般认为音符的生命周期已经结束,不应该再进行进一步的操作。这样的数学模型也被称为“有限状态自动机”(简称“状态机”)。
3.4 音符判定过程
为了实现上文所述的状态机,在游戏主循环中每次判定处理时,需要根据音符原始状态、当前条件(比如时间、触摸事件等)确定音符的新状态,并在状态转移过程中执行进入画面、判定等操作。
这样的操作需要遍历当前可用(可以进入画面、未进入终态)的音符。工程上除了从第1个音符开始遍历以外,还可以使用链表加快遍历速度:将音符按时间排序后放入链表,进入终态后从链表中删除,遍历时从链表头开始,当音符超过“当前时间+下落时间”后结束遍历。还有一个更为简单的实现方式也能达到不错的性能:将音符按时间排序后,从第一个非终态的音符依次向后遍历,如果这个音符也到达了终态则更新这一位置,以此类推直到超过显示范围。
《Project SEKAI》在每一帧执行的音符判定过程主要如下:
预处理音符:遍历可用音符,根据当前时间,为音符完成进入画面、离开画面等与触控无关的状态转移
检查音符判定时间:遍历可用音符,如果当前时间在音符判定范围内,就将这个音符放入可分配给触摸事件的音符列表
为触摸事件分配音符:遍历触摸事件,根据触摸轨道位置、触摸阶段等信息,主要检查触摸阶段是否符合当前状态的要求,以及触摸轨道位置是否离音符过远(不同音符类型宽松程度也不一样)。通过检查后为触摸事件分配对应的音符,如果有多个可用判定的音符,则取到达时间最近的音符,如果仍然有多个则取距离最近的音符。1个触摸事件最多只能对应1个音符,1个音符可以有多个触摸事件与之对应
对触摸事件所对应的音符进行判定:遍历触摸事件,寻找离音符中心距离最近的触摸事件,使用这一触摸事件和当前时间判定对应的音符,判定也会导致音符状态的变更。然后给剩下的触摸事件重新执行分配音符,然后继续这一过程
举几个例子方便读者理解,下图的红点代表触摸位置,假设在音符边缘触摸的位置均符合音符要求:

这几种情景下的触控事件分别起到如下效果:
情景1:下方的音符更早出现,触摸事件将用于下方音符的判定
情景2:触摸位置离左侧的音符较近,触摸事件将用于左侧音符的判定
情景3:虽然一开始会将2个触摸事件均按最近距离分配给左侧音符,但优先判定离音符中心最近的左侧触摸事件,左侧音符判定完成后,会将右侧触摸点重新分配给右侧音符完成判定
参考资料
多点触控协议 - Linux 内核文档(英文):https://www.kernel.org/doc/html/v4.16/input/multi-touch-protocol.html
触摸设备 - Android 开源项目:https://source.android.com/devices/input/touch-devices
MotionEvent - Android 开发者(英文):https://developer.android.com/reference/android/view/MotionEvent
MonoBehaviour-Update() - Unity 脚本 API:https://docs.unity.cn/cn/2019.4/ScriptReference/MonoBehaviour.Update.html
Input-touches - Unity 脚本 API:https://docs.unity.cn/cn/2019.4/ScriptReference/Input-touches.html
UnityEngine.Touch - Unity 脚本 API:https://docs.unity.cn/cn/2019.4/ScriptReference/Touch.html
TouchPhase - Unity 脚本 API:https://docs.unity.cn/cn/2019.4/ScriptReference/TouchPhase.html
结语
需要说明的是,操作系统相关内容参考了Android的实现,它在操作系统开发方面提供了较为全面的文档与源代码。期待更为专业的玩家编写更详实的解析。
相关文章:
音游判定原理详解——从触摸屏幕到判定音符【Project SEKAI攻略】
“音乐游戏”一般简称为“音游”,玩家需要配合音乐的节奏来进行一定的动作。 《Project SEKAI》作为一个“移动端音游”,绝大多数玩家会使用手机、平板电脑等移动设备的触摸屏进行游玩,也有极少数的玩家不按常理出牌,使用手台、键…...
【论文阅读】Self-Paced Boost Learning for Classification
论文下载 bib: INPROCEEDINGS{PiLi2016SPBL,title {Self-Paced Boost Learning for Classification},author {Te Pi and Xi Li and Zhongfei Zhang and Deyu Meng and Fei Wu and Jun Xiao and Yueting Zhuang},booktitle {IJCAI},year {2016},pages {1932--1938} …...
通过CSIG—走进合合信息探讨生成式AI及文档图像处理的前景和价值
一、前言 最近有幸参加了由中国图象图形学学会(CSIG)主办,合合信息、CSIG文档图像分析与识别专业委员会联合承办的“CSIG企业行——走进合合信息”的分享会,这次活动以“图文智能处理与多场景应用技术展望”为主题,聚…...
流程图拖拽视觉编程--概述
一般的机器视觉平台采用纯代码的编程方式,如opencv、halcon,使用门槛高、难度大、定制性强、开发周期长,因此迫切需要一个低代码开发的视觉应用平台。AOI缺陷检测的对象往往缺陷种类多,将常用的图像处理算子封装成图形节点,如抓直…...
深度学习中的卷积神经网络
博主简介 博主是一名大二学生,主攻人工智能研究。感谢让我们在CSDN相遇,博主致力于在这里分享关于人工智能,c,Python,爬虫等方面知识的分享。 如果有需要的小伙伴可以关注博主,博主会继续更新的,…...
vue3的介绍和两种创建方式(cli和vite)
目录 一、vue3的介绍 (一)vue3的简介 (二)vue3对比vue2带来的性能提升 二、vue3的两种创建方式 方式一:使用vue-cli创建(推荐--全面) 操作步骤 方式二:使用vite创建 操作步…...
camunda工作流user task如何使用
在Camunda中使用User Task通常需要以下步骤: 1、创建User Task:使用BPMN 2.0图形化设计器(如Camunda Modeler),将User Task元素拖到流程图中,并为任务命名,指定参与者(用户或用户组…...
三元运算符
三元运算符 三元运算符通常在Python⾥被称为条件表达式 这些表达式基于真(true)/假(not)的条件判 断 在Python 2.4以上才有了三元操作。 下⾯是⼀个伪代码和例⼦: 伪代码: 如果条件为真,返回真 否则返回假 condition_is_true if condition else c…...
Vue3 Element-plus el-menu无限级菜单组件封装
对于element中提供给我们的el-menu组件最多可以实现三层嵌套,如果多一层数据只能自己通过变量去加一层,如果加了两层、三层这种往往是行不通的,所以只能进行封装 效果图 一、定义数据 MenuData.ts export default [{id: "1",name…...
( “树” 之 BST) 669. 修剪二叉搜索树 ——【Leetcode每日一题】
二叉查找树(BST):根节点大于等于左子树所有节点,小于等于右子树所有节点。 二叉查找树中序遍历有序。 669. 修剪二叉搜索树 给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&…...
【C语言】浅涉结构体(声明、定义、类型、定义及初始化、成员访问及传参)
简单不先于复杂,而是在复杂之后。 目录 1. 结构体的声明 1.1 结构体的基础知识 1.2 结构的声明 1.3 结构成员的类型 1.4 结构体变量的定义和初始化 2. 结构体成员的访问 3. 结构体传参 1. 结构体的声明 1.1 结构体的基础知识 结构是一些值的集合&…...
设计模式-结构型模式之装饰模式
3. 装饰模式 3.1. 模式动机 一般有两种方式可以实现给一个类或对象增加行为: 继承机制 使用继承机制是给现有类添加功能的一种有效途径,通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。但是这种方法是静态的,用户不能…...
【Chatgpt4 教学】 NLP(自然语言处理)第九课 朴素贝叶斯分类器的工作原理 机器学习算法
我在起,点更新NLP自然语言处理》《王老师带我成为救世主》 为啥为它单独开章,因为它值得,它成功的让我断了一更,让我实践了自上而下找能够理解的知识点,然后自下而上的学习给自己的知识升级,将自己提升到能…...
基于html+css的图片展示17
准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…...
Jupyter Notebook小知识
目录 1 快捷键1.1 常用快捷键1.2 魔法函数 2 常用快捷键2.1 模式切换2.2 命令模式快捷键2.3 编辑模式快捷键3 Matplotlib绘图 4 小技巧4.1 文件默认目录的查看以及更改4.2 更改主题颜色 5 其它5.1 python中 r, b, u, f 的含义5.2 f/format():格式化操作 6 常见问题6.1 查看模块…...
redis原理及进化之路
Redis 的主从复制经历了多次演进,本文将从最基本的原理和实现讲起,并层层递进,逐步呈现 Redis 主从复制的演进历史。大家将了解到 Redis 主从复制的原理,以及各个改进版本解决了什么问题,并最终看清 Redis 7.0 主从复制…...
ai智能写作助手-ai自动写作软件
为什么要用ai智能写作工具 在数字化时代,AI(人工智能)技术已经被广泛应用于各种领域,其中之一是写作。AI智能写作工具是利用自然语言处理技术和机器学习算法来生成高质量的文章、博客、新闻稿等。这些工具不仅提供了便捷、高效的…...
redis持久化
redis提供两种方式进行持久化,一种是RDB持久化(原理是将Reids在内存中的数据库记录定时dump到磁盘上的RDB持久化),另外一种是AOF持久化(原理是将Reids的操作日志以追加的方式写入文件)。那么这两种持久化方…...
Vue项目基于driverjs实现新用户导航
引导页就是当用户第一次或者手动进行触发的时候,提示给用户当前系统的模块介绍,比如哪里是退出,哪里是菜单等等相应的操作。 无论是开发 APP 还是 web 应用,新手引导都是一个很常见的需求,一般在这2个方面需要新手引导…...
自编码器简单介绍—使用PyTorch库实现一个简单的自编码器,并使用MNIST数据集进行训练和测试
文章目录 自编码器简单介绍什么是自编码器?自动编码器和卷积神经网络的区别?如何构建一个自编码器?如何训练自编码器?如何使用自编码器进行图像压缩?总结使用PyTorch构建简单的自动编码器第一步:导入库和数…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...
中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
