下载 App

关于游戏《因果》为什么需要在屏幕刷新率为60hz的环境下运行

修改于2025/11/0632 浏览综合
在这里感谢《窗口旅行 Out Of Bounds》的作者发现了最大的问题,请大家多多支持《窗口旅行 Out Of Bounds》
如标题所示,这一篇帖子用于说明最大的恶性bug出现的原因
在完整说明之前需要知道开发《因果》时使用的技术和前置知识储备
在游戏引擎的普及下,大部分开发者会遗漏一个很致命且隐性的问题:游戏运行时的帧数
有一小部分pvp游戏,会出现“屏幕刷新率高,游戏运行的帧数越高”的问题,例如《Bodycam》
这看起来是一个好结果,但它实际达成的效果却是由设备优劣带来的不对等体验,尤其是基础体验
拿最基础的移动举例子,如果奉行“屏幕刷新率高,游戏运行的帧数越高”就会出现以下结果:
“屏幕刷新率越高,相较于其他屏幕刷新率比你低的设备,你移动的越快”
为什么会出现这种情况?
在游戏开发时都会选择一个基础的动画更新函数来制作和判断角色当前的状态和信息
本质上来讲,动画更新函数就是一个定时器,在启动的时候就规定每隔一段时间就执行一次,直到这个定时器被移除(停止)
例如移动,先写一个函数,告诉编译环境“我需要将玩家移动到离当前位置距离为x,方向为v的位置”
然后将其放进动画函数里,动画函数就会按照规定的时间跨度一直持续执行“我需要……”这个函数
很多时候,由于开发者的疏漏或考虑不周,如果移动速度为x,他们会直接设定为“每一帧移动x这么远”
这是非常严重的错误
如果没有额外的锁帧技术,游戏每一帧将会以一个绝对的距离更新位置,但游戏运行的帧速不一定是绝对的
例如在一些高端设备游戏能够跑到90帧,意味着每一秒会执行移动90次,如果移动速度为10,那么在这个高端设备上一秒的移动距离为90*10=900
但在一些中低端设备只能跑60帧,这时一秒只会执行移动60次,在这个低端设备一秒的移动距离为60*10=600,一秒移动的距离直接少了接近一半
连最基础的移动都会受到如此大的影响,更不用说其他需要额外判定的功能和需要实际播放的动画
这下问题明了了,但如何解决?
我的答案是规定一个绝对的时间跨度(每隔16毫秒)更新一次动画,也就是一秒更新60次(1000/60≈16.6,这里取整为16毫秒)
请注意,这并不是说等到16毫秒后再执行一次动画更新函数,动画的执行和动画更新函数的执行是分开的
动画更新函数依旧按照原有的最高帧率执行,但在其中获取此次调用动画更新函数的时间戳,在判断到下一个时间戳之间的差大于等于16毫秒后就执行一次动画函数
具体例子为:
我有一个函数的名称为fun移动(),动画更新函数的名称为fun更新()
fun更新()执行的时候会获取一次时间戳,假设当前的时间戳为0
fun更新()第二次执行的时候又获取一次时间戳,假设当前的时间戳为8毫秒(模拟120帧的情况),此时时间差为8-0=8毫秒,不够16毫秒,所以继续执行fun更新()
fun更新()第三次执行的时候又获取一次时间戳,假设当前的时间戳为16毫秒,满足16-0=16毫秒的条件,此时执行一次fun移动(),恢复时间戳记录时间为0,继续进行从第一步的判断
这套方法的好处是对于一些帧动画有很强的适配性(例如2d格斗游戏中的动作序列帧),不会出现因为帧数高而导致快速鬼畜播放的效果
知道了帧数如何影响游戏体验后,再来谈谈《因果》中出现的“高于60hz就会卡死”的bug
我目前的所有游戏作品均由H5技术(html+css+javascript)开发,目的是为了进行多平台打包和分发,其中pc端使用nw.js来打包成exe文件,移动端使用cordova来打包成安装包
在js(javascript)中一般使用requestAnimationFrame()来更新动画,这个函数可以跑满设定的最高屏幕刷新率的效果来执行,且每次调用的时候都会返回一个名为currentTime的时间戳
方案和上文中提到的方案一样,但问题是我错误地将“恢复时间戳记录时间”这一行写到了函数末尾而不是“判断时间差大于16毫秒”里
TapTap
这是冲刺代码,本来lastTime=null这一行应该写成lastTime=currenTime的
下面是较为完整的代码
TapTap
谨记不要熬夜写代码
我当时的思路应该是通过给lastTime赋值为null,再进行判断就会达到更新lastTime的目的
结果把lastTime=currentTime放到了函数的最后面
这就导致每执行一次动画更新函数都会恢复一次时间戳记录时间,在高刷屏上根本不会达成判断条件(即frametimeset>=16),预期中的下一帧根本不会到来,导致画面卡死(但仍然在运行)
解决方法很简单,将lastTime=null改成lastTime=currenTime,或将本存在于函数末尾的lastTime=currenTime移动到最前面
问题是所有的动画控制方案都是这个模板,想全改是一项十分难绷的工程
另外还有一些有动态刷新率的设备,保不准真的会以一个很低的刷新率来运行
还是谨记不要熬夜写代码吧
还好在正式上线之前《窗口旅行 Out Of Bounds》的作者反映了游戏按下shift键就会卡死的现象,经过排查后确定是屏幕刷新率的问题,紧急处理后在开头新增了需要将刷新率调到60hz才能游玩的提示
在这里再次感谢《窗口旅行 Out Of Bounds》的作者,也希望大家多多支持《窗口旅行 Out Of Bounds》!
猜你想搜
因果 60hz刷新率原因
完整更新截图
完整更新
截至这篇帖子发布时间时间为止,所有功能均已开发完成,包括游戏机制与玩法,剧情与过场动画以及无限模式,新版本正在审核中。 游戏玩法就是俯视角射击(废话),其中有意思的“bug”是“无限子弹”,即枪里没有子弹也能正常攻击。 一切恐惧来自火力不足,但真的就是这样爽玩,开局直接突突突? 只要你进行“bug”射击(空枪射击),主角的理智将会降低,带来的后果是敌人将变得越来越强,被攻击后起身的时间越来越长,闪
官方
4 赞
1 回复
攻略截图
攻略
以防大家卡关,现在对游戏机制做一些说明 首先需要将屏幕刷新率调整到60hz才能正常游玩是硬性bug,属于代码层面的问题,不是节目效果,是真的bug 因为熬夜写代码的原因在请求动画帧的时候把一个关键语句写错了顺序,导致屏幕刷新率高于60hz的情况下永远无法请求下一帧,整个游戏就会卡死(但误打误撞发现了时停和时缓的写法,也算因祸得福?) 角色有san值,在枪里有子弹的情况下射击不会扣除san值,枪里没
官方
5 赞
1 回复
重大决定截图
重大决定
《T.B.I》世界观将与《因果》世界观合并(毕竟两个都是受规则怪谈启发,且都为俯视角游戏) 《因果》后续关卡正在开发中,屏幕刷新率适配问题正在全力抢修 后续更新将包含二周目内容,boss战以及更多机制 敬请期待吧
官方
2 赞
计划有变截图
计划有变
由于不可抗力因素(好吧我承认因为沉迷游戏导致进度拖慢了),现决定在10月28日上传完整版,包括第二章内容(不包含战斗部分,纯剧情)以及一个无尽模式,安卓版需要等到29日或以后。 总之就是这样吧 :)
官方
3 赞
1 回复
00:28
玩法落地截图
玩法落地
游戏系统已经构建完成,剩下只有场景构建了。 由于场景的特殊性,我只需要一张大图片就可以完成整张地图的碰撞检测,之后的进度可以说是相当之快了。 目前设计了两种敌人,难度很低,但要小心,绝对没有看上去那么简单。 游戏名暂定为《涤荡》,可能后面会改。 #聚光灯gamejam开发者日志 #单机游戏 #
官方
4 赞
2 回复
00:20
子弹判定与打击感截图
子弹判定与打击感
除了完善基础功能外,额外添加了子弹判定与后座力的视觉感受 地砖是上一个游戏遗留下来的资产,在这里仅充当占位符 下次更新就是敌人行为的大更新了 #聚光灯gamejam开发者日志 #单机游戏
官方
4 赞
关于安卓版
还在适配中,预计28日上架安卓版 目前鉴定为不是所有机型都能正常运行,在测试的三台设备中只有oppo A35这个型号能较好地运行,其余的要么闪退要么非常卡
官方
2 赞
《因果》内容更新
截至到这篇帖子发布为止,新版本已提交审核。 这次更新将带来新挑战,新敌人,新背景音乐以及新剧情 所以屏幕刷新率的问题修了吗? 没有修,而是用更“强势”的方式绕过了这个问题 为什么不修? 史山代码已经堆得看不到头了,就算是我也无能为力 那这次更新有什么值得玩的吗? 这次更新略微调整了游戏平衡和游戏细节,多次死亡之后会重置整个游戏,避免后期因为特性无法继续游戏的问题 那之后还会继续更新吗? 应该不会了
官方
3 赞
游戏开发
游戏玩法其实在主题公布后就已经确定了,依旧沿用我上一个游戏遗留下来的操作方式,但这次引擎已经进行了全面重置和优化,不会再像我的第一个游戏那样bug满天飞了(这是真bug,影响体验的那种)。 这次将引入俯视角射击机制,主打战斗和随机性,以及轻微的恐怖元素。 玩家将使用一把有着七发弹容的霰弹枪,在固定的地图上面对不同的强力的敌人。 至于bug的部分需要在实际游玩的时候靠自己发掘。 与其说是bug倒不如
官方
2 赞