音游制作杂谈(230415)
一、延续性Note与分段优化
// 上次的杂谈讲到了将Note按照一定的时间间隔来进行分组的方法。但是,在一些场合,这个方法存在一些潜在的弊病。考虑这样一种情况:

// 如图是一个具有2个节点的长条:在后面的节点预存好前面节点的信息(时间、x位置、宽度),然后为每一个“后面的节点”生成一个Mesh,使得后面的节点视觉上与前面的节点连接在一起,形成一个长条。
// 现在采用分组优化,在这个例子中我们设想“将两根格线的距离定为分组间距”,这根长条的时间跨度便是4个组。
// 于是出现问题:节点只出现在了第1个组的开头和第5个组的开头,在中间的3个组,长条“后面的节点”不属于这三个组的任何一个组,如果第5个组没有纳入“照度”的范围内,长条“后面的节点”便不会显示在屏幕上,从而导致其附带的Mesh也不会显示在屏幕上,效果变成:

// 非常荒诞。不过,插值法足以解决这个问题:


二、分段索引
// 上面介绍到,在段与段的分界点进行插值处理,可以解决延续性Note因分段优化导致的中断问题。插值法带来的副作用有时非常实用(例如DanceRail3,采用每一个长条中继点均作为一个独立的Catch Note的设计,那么这种“插值副作用”便成功达成了“长条每8分长度增加1Combo”的设计目的),有时不需要将其纳入考虑范围(例如,如果在谱面内指定缓动曲线,那么解析谱面的过程中自然存在一个将曲线插值处理为折线的过程;只要达到了一定的插值精度,插值点与插值点的距离会自然地小于分段组距)。
// 但是,最终还是希望找到一种“无副作用”的分组优化法,从而让分组优化得到更广泛的适用、更自由的运用。分组索引便是“无副作用的分组优化法”之一,它的大体思路如下:
A. 确定分组组距;
B. 开始遍历所有Note。如遇到没有规定父节点的Note,则直接将当前Note的形式参数(时间、dtime、……)整除以规定的分组组距,将该Note的编号(可以是Array的位置,也可以是ID式的编号)记录到索引表的相应位置;
C. 如遇到规定了父节点的Node,则计算n = (t//d) - (t0//d)。将t//d得出的数作为第一个组,录入该Node的编号;然后在其前n个组均录入该Node的编号。
D. 正式使用时,若轮询到了“规定了父节点的Node”,则根据判定线位置的形式时间 f(t) 和绘制最大形式时间(照度形式时间)f(t) + i/k(可参考上期杂谈)判定【父节点是否在屏幕上】及【该Node是否在屏幕上】。对于不在屏幕上的节点,则根据 f(t) , f(t) + i/k 中的一个插值生成一个临时节点,在Mesh的生成过程“取而代之”。
三、实现一个健壮的时间系统
// 时间系统是一个音游赖以工作的前提。玩家常常讨论某个音游的判定范围时间(- xx ms ~ xx ms);谱面作者在创作谱面时,也需要把Note放置在正确的时间位置上。
// 跟上次一样的套路。“如果以调用游戏引擎的API为切入点”,在时间系统的设计上便会进入Timer的迷思,然后开始思考:怎么游戏引擎给的Timer是倒计时?有没有那种时间递增的计时器?……
// 先说结论:“时间递增的计时器”是有的,存在于操作系统中。无论是iOS、Android还是Windows,都可以获取操作系统提供的毫秒时间。关于这个时间为什么是操作系统提供的,个人认为是为了保持“时间来源的统一”:在设备内部实现时间来源的统一,会为权威授时中心在理想条件下建立全体设备“时间来源的统一”提供方便。此处不展开讲。
// 利用操作系统提供的绝对时间,可以进行各种相对时间的计算。常见的用例有:
A. 标记歌曲开始时间 t0 ,然后在后续的每个Gameloop都调取一次当前系统时间 t ,则乐曲进度是 t-t0 ;
B. 玩家设置了谱面偏移 t1,那么乐曲进度是 t-t0-t1 ;
C. 玩家在 t2 按下暂停,又在 t3 继续游戏(假设没有做恢复倒计时),那么乐曲进度是 t-t0- 2*t1 - (t3-t2) 。这里的 2*t1 考虑的是从暂停状态切换到到解除暂停状态后,播放音频又需要一段延迟;
D. 为了解决玩家【暂停后继续】反应不过来的问题,决定在继续游戏时对谱面进行一段倒放。设玩家在 t2 按下暂停,又在 t3 按下继续游戏,倒放的用时是 t4。倒放结束后,乐曲的进度是 t-t0- 2*t1 - (t3-t2) - 2*t4。这么做的话,建议在继续游戏时就修正音频的播放进度,并立即播放音频。
// 接下来依旧是讲解几个注意点:
A. 对于一些关键节点,最好建立起一个 事件-时间戳 表,例如乐曲开始、玩家按下暂停、玩家按下继续游戏、恢复倒计时归零(预期时间)、谱面倒放完毕……
B. 如果需要一个能让玩家在不调整判定线位置的情况下“对正乐曲与谱面”的机制的话,优先考虑谱面偏移。乐曲偏移在动态调整的过程中存在延迟堆积/漂移等问题,相当棘手;此外,游戏开始时也需要动态调整乐曲的播放时机,便会遇到“xx时间发生xx事件”的漂移偏差,而此时乐曲播放决定了乐曲与谱面能否对正,这又是时间敏感事件了;
C. 每次暂停都会导致一次谱面偏移 t1 的增加,以及两次倒放用时 t4 的增加;
D. 玩家在游玩中途将游戏应用切换至后台的情况,通常需要触发一个自动暂停机制。如何判断玩家“将游戏应用切换至后台”呢?iOS和Android对于游戏应用呈现的画面都给出了相应的窗口概念,藉此“游戏应用切换至后台”便可以转化为“游戏窗口失去焦点,进入非活动状态”。这一点不经人点拨很难认识到。