查看: 7134|回复: 17

[原创发布] 解决MV游戏明明FPS为60但运行时不流畅、有抖动感的问题

[复制链接]

宛若

梦石
0
星屑
1888
在线时间
529 小时
注册时间
2007-8-19
回帖
1466

极短24参与开拓者

发表于 2022-2-28 00:45:12 | 显示全部楼层 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有账号?注册会员

×
本帖最后由 逸豫 于 2022-2-28 19:09 编辑

不知道以前有没有人提出过,MV的游戏有时候明明FPS没有降低,但是游戏玩起来就是卡卡的,尤其是在地图滚动时。

最近终于下定决心去研究这个问题,发现问题出在MV的以帧为基础的刷新机制上。

给不懂代码的人:

命名为JitterFix.js,然后放到plugin文件夹中,直接启用。

代码发布到public domain,你可以随意不署名甚至署你自己的名字使用(当然你愿意署我的名更好)。
[pre lang="javascript"]/*:
* @plugindesc Fix the graphics jitter mostly noticeable when scrolling maps.
* @author Zumi Kua
*
* @help just enable this plugin, no need to do anything.
*/
/*: zh
* @plugindesc 修复游戏地图在滚动时不定时出现的卡顿感
* @author Zumi Kua
*
* @help 实际原因为performance.now返回的数值似乎会有误差,导致两帧之间的deltaTime在1/60上下来回横跳,进而导致在一次
* requestAnimationFrame回调中一次updateScene也没调用,然后在下一次requestAnimationFrame回调中连续调用两次updateScene
* 无法解决在非60Hz的显示器上的卡顿问题
*
* 使用requestAnimationFrame作为参数提供的DOMHighResTimeStamp似乎不会有该问题
*
*/

SceneManager.update = function(stamp) {
    try {
        this.tickStart();
        if (Utils.isMobileSafari()) {
            this.updateInputData();
        }
        this.updateManagers();
        this.updateMain(stamp);
        this.tickEnd();
    } catch (e) {
        this.catchException(e);
    }
};

const DEBUG = false;

SceneManager.updateMain = function(stamp) {
    if (Utils.isMobileSafari()) {
        this.changeScene();
        this.updateScene();
    } else {
        let fTime = (stamp - this._currentTime) / 1000;
        if (fTime > 0.25) fTime = 0.25;

        this._currentTime = stamp;
        this._accumulator += fTime;
        const old_ftime = fTime;
        const old_accu = this._accumulator;
        let i = 0;
        while (this._accumulator >= this._deltaTime) {
            i++;
            this.updateInputData();
            this.changeScene();
            this.updateScene();
            this._accumulator -= this._deltaTime;
        }
        if(DEBUG && i !== 1){
            console.log(i, old_ftime, old_accu);
        }
    }
    this.renderScene();
    this.requestUpdate();
};[/pre]

致对原因感兴趣的人:

首先来看MV的游戏主循环代码:
[pre lang="javascript"]SceneManager.updateMain = function() {
    if (Utils.isMobileSafari()) {
        this.changeScene();
        this.updateScene();
    } else {
        var newTime = this._getTimeInMsWithoutMobileSafari();
        var fTime = (newTime - this._currentTime) / 1000;
        if (fTime > 0.25) fTime = 0.25;
        this._currentTime = newTime;
        this._accumulator += fTime;
        const old_accu = this._accumulator;
        let i = 0;
        while (this._accumulator >= this._deltaTime) {
            ++i;
            this.updateInputData();
            this.changeScene();
            this.updateScene();
            this._accumulator -= this._deltaTime;
        }
        if(i !== 1){
            console.log(i, fTime, old_accu);
        }
    }
    this.renderScene();
    this.requestUpdate();
};[/pre]

关于MV的刷新代码为什么这么写,可以参考:
https://gafferongames.com/post/fix_your_timestep/
如果你有Unity的使用经验的话也可以回忆一下Unity的Update和fixedUpdate。


这个updateMain函数每次窗口重绘时都会被调用,可以理解为一般游戏的gameLoop循环体。

理想状态下,每次重绘距离上次的时间应该都为1/60(假设我们的显示器刷新率为60hz),那么,每次重绘都会对应一次updateScene。

当此次重绘距离上次的时间大于1/60,则此次调用updateScene后,多余的时间会记录在accumulator中,供下次使用

而当此次重绘距离上次的时间小于1/60,则该帧不会进行updateScene调用,全部的时间会记录在accumulator中,供下次使用

那么,如果每次重绘距离上次重绘的时间在1/60左右反复横跳,即每次重绘的时间大概是这样呢:

[pre lang=""]2 0.01699999999254942 0.033333333612730484
0 0.015999999828636646 0.01633333343391655
2 0.018000000156462193 0.03433333359037874
0 0.016000000294297934 0.01633333371331332
2 0.01699999999254942 0.033333333705862736
0 0.015999999828636646 0.016333333527048802
2 0.018000000156462193 0.03433333368351099
0 0.015999999828636646 0.016333333340784285
2 0.01699999999254942 0.0333333333333337[/pre]
(请看中间列,它代表了每次重绘距离上次重绘的时间,顺带一提,第一列是该帧调用updateScene的次数)

可以看出,当距离上次重绘的时间小于1/60时,因为目前距离上一逻辑帧经过的时间不足一逻辑帧的时间,所以这次重绘不会调用updateScene。

当距离上次重绘的时间大于1/60时,加上上一帧小于1/60所留下的时间,我们现在有了两逻辑帧所需要的时间,这次重绘会调用两次updateScene。

而这样重绘时间在大于1/60与小于1/60之间来回横跳,正是卡顿感的成因。

而至于原因,目前我还没法给出一个确切的答案,不过解决方法倒是有:
requestAnimationFrame也会传一个当前时间作为参数,它传回的这个当前时间比我们用_getTimeInMsWithoutMobileSafari获得的要准确的多,将我们通过_getTimeInMsWithoutMobileSafari获取的时间戳改为requestAnimationFrame传回的时间戳,卡顿的情况也的确消失了。
(这也引出了一个这个现象可能的原因,MV在调用updateMain之前也调用了一些其他的函数,而这些函数执行消耗的时间不固定,且消耗的时间长到无法忽略)

另:如果你的显示器刷新率不是60hz,那么卡顿感应该是无法解决的。(以144hz为例,3帧中必然有2帧是不会调用updateScene的)。

这个也发布于rpgmakerweb,作者也是我:https://forums.rpgmakerweb.com/i ... -stuttering.144992/

评分

参与人数 5星屑 +600 赞 +4 收起 理由
rfvtgbzxc + 1 精品文章
竹染蓝 + 1
cbi1990076 + 1
fux2 + 600 精品文章
马铃薯条 + 1

查看全部评分

[url=http://rpg.blue/thread-219730-1-1.html]http://unhero.sinaapp.com/wi.php[/url]
[color=Red]如你所见这是个死坑,没错这就是打我的脸用的[/color]
梦石
0
星屑
5497
在线时间
825 小时
注册时间
2019-11-7
回帖
328
发表于 2022-2-28 11:59:01 | 显示全部楼层
下载试试  感谢大佬分享
回复

使用道具 举报

梦石
0
星屑
5269
在线时间
733 小时
注册时间
2013-1-18
回帖
525
发表于 2022-2-28 12:04:43 | 显示全部楼层
感谢大佬的分享和讲解,不过我用了一下,感觉好像没有什么区别,是不是地图元素多的时候效果才明显?
回复

使用道具 举报

梦石
0
星屑
5170
在线时间
610 小时
注册时间
2017-10-21
回帖
321
发表于 2022-2-28 12:24:08 | 显示全部楼层
感谢大佬分享,拿来先备着
回复

使用道具 举报

梦石
0
星屑
14317
在线时间
2609 小时
注册时间
2013-6-10
回帖
1692
发表于 2022-2-28 12:50:07 | 显示全部楼层
真是胸怀宽广,实力高强之人呀!
回复

使用道具 举报

宛若

梦石
0
星屑
1888
在线时间
529 小时
注册时间
2007-8-19
回帖
1466

极短24参与开拓者

 楼主| 发表于 2022-2-28 19:01:16 | 显示全部楼层
本帖最后由 逸豫 于 2022-2-28 19:05 编辑
nhycs01 发表于 2022-2-28 12:04
感谢大佬的分享和讲解,不过我用了一下,感觉好像没有什么区别,是不是地图元素多的时候效果才明显? ...


你没有感觉很有可能是……你并没有遇到这种情况?
实际上这种情况的出现本身就带有一定的随机性,从我的经验来说,可能多进行几次地图的切换会增加触发概率?
而触发了这种情况的主要表现是在地图在滚动时的抖动感,即地图上一些边界明显的地方看上去似乎在来回抖动,实际游玩时可能并不是那么明显。

如果你有一台刷新率大于60hz的显示器的话也许能够体会到这种抖动感(虽然高刷新率屏幕下的抖动碍于MV引擎的限制并不是我这个脚本能轻易解决的)。

https://forums.rpgmakerweb.com/i ... t-fps-drops.125067/
这个链接中5楼的视频(来自YouTube,需要翻墙)展示了我说的抖动感是一种什么样的情况。
[url=http://rpg.blue/thread-219730-1-1.html]http://unhero.sinaapp.com/wi.php[/url]
[color=Red]如你所见这是个死坑,没错这就是打我的脸用的[/color]
回复

使用道具 举报

梦石
0
星屑
5269
在线时间
733 小时
注册时间
2013-1-18
回帖
525
发表于 2022-2-28 19:14:16 | 显示全部楼层
逸豫 发表于 2022-2-28 19:01
你没有感觉很有可能是……你并没有遇到这种情况?
实际上这种情况的出现本身就带有一定的随机性,从我的 ...

感谢大佬解释,我确实目前没有感觉到这种抖动感,但不排除可能是我自己没有意识到的原因。
我想最后问一下的是,我把这个插件放在工程里开启,是否起到“有总比没有好”这类的效果?
也就是说,如果别人玩我的游戏,在不同的显示器上或许能够有更明显的效果改善,
即便没有,这个插件放着也没有任何副作用,不占资源,以及没有起冲突的隐患。
回复

使用道具 举报

宛若

梦石
0
星屑
1888
在线时间
529 小时
注册时间
2007-8-19
回帖
1466

极短24参与开拓者

 楼主| 发表于 2022-2-28 19:21:59 | 显示全部楼层
nhycs01 发表于 2022-2-28 19:14
感谢大佬解释,我确实目前没有感觉到这种抖动感,但不排除可能是我自己没有意识到的原因。
我想最后问一 ...

冲突的话……还是有产生的风险,毕竟我覆写了一些MV自带的函数。
不过添加这个脚本应该是不会引入效率问题的。
[url=http://rpg.blue/thread-219730-1-1.html]http://unhero.sinaapp.com/wi.php[/url]
[color=Red]如你所见这是个死坑,没错这就是打我的脸用的[/color]
回复

使用道具 举报

梦石
0
星屑
799
在线时间
81 小时
注册时间
2018-3-27
回帖
21
发表于 2022-3-4 21:24:06 | 显示全部楼层
谢谢大佬,之前一直以为是灯光插件造成的原因。
之前了解到分辨率不能整除也会造成抖动,现在用上插件之后瞬间平滑起来。
回复

使用道具 举报

梦石
0
星屑
1163
在线时间
139 小时
注册时间
2020-6-16
回帖
131
发表于 2022-3-4 21:58:24 | 显示全部楼层
最近在用MV3D插件做游戏,事件多一点地图大一点就变得相当卡。感觉可以试试
纯公益游戏平台 筱咲RPG游戏盒  支持安卓 苹果 PC
开发者可以免费上传游戏资源将游戏变成在线页游
支持MV MZ 2000 2003 wolf 等游戏网页化
官网 https://site-772027-2565-1520.mysxl.cn/
有意联系QQ1943769811
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部