音游判定原理详解——从触摸屏幕到判定音符【Project SEKAI攻略】

news2025/1/23 4:56:57

  “音乐游戏”一般简称为“音游”,玩家需要配合音乐的节奏来进行一定的动作。

  《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的实现,它在操作系统开发方面提供了较为全面的文档与源代码。期待更为专业的玩家编写更详实的解析。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/434096.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

英国站亚马逊纽扣电池标准

英国标准协会(BSI)于2021年4月30日发布了纽扣(非锂)和纽扣(锂)电池的国家标准PAS 7055:2021。 该标准是根据2005年通用产品安全法规的规定制定的(GPSR) 并关注投放到英国市场的产品的安全性。 PAS 7055:202…

数据库实验 | 第1关:建立和调用存储过程(不带输出参数的存储过程)

任务描述 本关任务: 该实验是针对数据表jdxx,该数据表有四个字段,分别是省份(sf)、城市(cs)、区县(qxmc)、街道(name)。 例如,查询天心区(qxmc)的所有字段的值结果如图所示 任务要求 建立存储过程 dqxx(in city varchar(10),i…

6.S081——虚拟内存部分——xv6源码完全解析系列(4)

0.briefly speaking 点击跳转到上一篇博客 好,现在进入下一个话题,就是物理内存分配器(kernel/kalloc.c)。在简单介绍完内核态的物理内存分配器之后,之后简单带过一下两个头文件riscv.h和memorylayout.h这两个头文件,因为它们都…

.Net Core从零学习搭建权限管理系统 - 课程简介

课程简介目录 🚀前言一、课程背景二、课程目的三、系统功能四、系统技术架构五、课程特点六、课程适合人员七、课程规划的章节八、最后 🚀前言 本文是《.Net Core从零学习搭建权限管理系统》教程专栏的导航站(点击链接,跳转到专栏…

(1条消息) CodeForces 1278 B.A and B(Math)

题目如下: 题解 or 思路 首先我们需要知道: 对于: s u m 1 2 3 4 . . . n sum 1 2 3 4 ... n sum1234...n s u m a b , ( a ∈ [ 0 , s u m ] ) sum a b, (a \in [0, sum]) sumab,(a∈[0,sum]) 这个在此就不再证明 于是我…

vcpkg添加自定义包安装

文章目录 前言新建overlay-ports编写baseline.json编写openssl.json编写配置修改vcpkg.json修改portfile.cmake 挂载安装后话 前言 vcpkg收集了很多C的包,可总是会有没收录进去的,以openssl 3.0.0版本举例,这个版本vcpkg没有收录进去&#x…

String的不可变特性

1 问题 如何理解“String是不可变的,但是可以变”? 2 方法 (1)String的不可变特性体现在内容和长度 首先在idea中点开查看String这个类是如何定义的 可以看到这样一行代码:private final char value[]; 正是因为这个数…

基于Simulink单载波链路射频波束成形仿真

一、前言 此示例展示了如何在 Simulink中对 IEEE 802.11ad单载波链路进行建模,其中包括具有射频波束成形功能的相控阵天线。 二、介绍 此模型模拟具有射频波束成形的 802.11ad 单载波 (SC)链路。多个数据包通过自由空间传输,然后射…

图片转为pdf怎么弄?简单几个步骤轻松转换

在日常工作和生活中,我们常常需要将图片转换为PDF格式的文档,以满足资料存档和共享的需要。虽然转换过程看起来有些麻烦,但只要选择正确的工具和方法,就能轻松完成。 下面,小编将为大家介绍两种常见的将图片转换为PDF…

生成对抗网络(GAN) 理论概念、改进模型与练习题

生成对抗网络(GAN) 生成对抗网络的基本概念模型提出模型类型模型功能模型目标模型结构模型训练模型实质模型缺点模型生成器的构造模型损失函数 生成对抗网络的改进模型DCGANWGANWGAN-GPACGAN 生成对抗网络的题型 生成对抗网络的基本概念 模型提出 2014…

Grad-CAM的详细介绍和Pytorch代码实现

Grad-CAM (Gradient-weighted Class Activation Mapping) 是一种可视化深度神经网络中哪些部分对于预测结果贡献最大的技术。它能够定位到特定的图像区域,从而使得神经网络的决策过程更加可解释和可视化。 Grad-CAM 的基本思想是,在神经网络中&#xff…

体验编写Vue框架项目实例的详细步骤2(包括git仓库使用,element-ui的使用和eslint校验关闭)

1.在src目录下新建pages文件夹用来放页面。新建文件Index.vue,首页 在Index.vue中搭建vue基本结构。 在element官网Element - The worlds most popular Vue UI framework中选择想要的组件。 我选择是Container布局容器。选择好样式点击显示代码复制相关代码至Ind…

【安全运维】小微企业的安全运维工具用哪款好?

即使是小微企业,也同样面临着安全运维的困扰,同样面临着数据泄露、资产难管理的问题,因此选择一款合适的安全运维工具是非常必要的。那你知道小微企业的安全运维工具用哪款好? 小微企业的安全运维工具用哪款好? 【回…

全景视角下的世界探索——三维全景地图

引言:随着数字技术和虚拟现实技术的发展,三维全景地图已成为一种新型地图展示方式,深受人们的关注和喜爱。三维全景地图以其真实逼真、互动性强、展示效果好等特点,正在越来越多的领域得到应用。 三维全景地图的特点 1.真实逼真 …

Elasticsearch(黑马)

初识elasticsearch ​​. 安装elasticsearch 1.部署单点es 1.1.创建网络 因为我们还需要部署kibana容器,因此需要让es和kibana容器互联。这里先创建一个网络: docker network create es-net 1.2.加载镜像 这里我们采用elasticsearch的7.12.1版本的…

【云原生概念和技术】1.2 云原生技术概括(下)

如果想了解或者学习云原生的友友们,欢迎订阅哦~🤗,目前一周三更,努力码字中🧑‍💻…目前第一章是一些介绍和概念性的知识,可以先在脑海里有一个知识的轮廓,从第二章开始就…

Talk预告 | ICLR‘23 北京大学楼家宁:针对鲁棒聚类问题的接近最优核心集

本期为TechBeat人工智能社区第485期线上Talk! 北京时间3月29日(周三)20:00,北京大学信息科学技术学院——楼家宁的Talk将准时在TechBeat人工智能社区开播! 他与大家分享的主题是: “针对鲁棒聚类问题的接近最优核心集”,届时将针…

nodejs+vue在线课程管理系统

随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本在线课程管理系统有管理员,教师,学生。管理员功能有个人中心,学生管理,教师管理,在线课程管理,课件信息管理&#x…

maybits就是持久型框架

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Ob…

使用HiBurn烧录鸿蒙.bin文件到Hi3861开发板

使用HiBurn烧录鸿蒙.bin文件到Hi3861开发板 鸿蒙官方文档的“Hi3861开发板第一个示例程序”中描述了——如何使用DevEco Device Tool工具烧录二进制文件到Hi3861开发板; 本文将介绍如何使用HiBurn工具烧录鸿蒙的.bin文件到Hi3861开发板。 获取HiBurn工具 通过鸿蒙…