设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 2852|回复: 1
打印 上一主题 下一主题

[交流讨论] RPG Maker MV 的源码研究 其三

[复制链接]

Lv3.寻梦者

梦石
0
星屑
1882
在线时间
1552 小时
注册时间
2013-4-13
帖子
917
跳转到指定楼层
1
发表于 2019-8-11 16:03:16 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

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

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

x
第三章 时空管理者的日常

我们从SceneManager.run(Scene_Boot)说起
当web页面加载完毕后这条语句就会被执行

Scene_Boot是一个特殊的场景(Scene)
我们暂且不提,
先来说说SceneManager这个游戏世界的时空主宰者的故事

SceneManager人如其名主要负责场景的加载和卸载
我们先来看看这个SceneManager.run方法都做了什么
  1. SceneManager.run = function (sceneClass) {
  2.     try {
  3.         this.initialize();
  4.         this.goto(sceneClass);
  5.         this.requestUpdate();
  6.     } catch (e) {
  7.         this.catchException(e);
  8.     }
  9. };
复制代码

SceneManager.initialize做了很多检查错误和初始化场景的工作具体内容比如Graphics的内容会放到以后再说
SceneManager.goto(sceneClass)的作用是停止当前场景
创建新场景(根据传入的sceneClass类)并标记为_nextScene
  1. SceneManager.goto = function (sceneClass) {
  2.     if (sceneClass) {
  3.         this._nextScene = new sceneClass();
  4.     }
  5.     if (this._scene) {
  6.         this._scene.stop();
  7.     }
  8. };
复制代码
然后最后执行requestUpdate方法其中requestAnimationFrame方法可以理解为下一帧时执行传入的方法
但是这一帧并不一定是一个稳定的时间间隔, 对于游戏来说并不是可靠的帧

  1. SceneManager.requestUpdate = function () {
  2.     if (!this._stopped) {
  3.         requestAnimationFrame(this.update.bind(this));
  4.     }
  5. };
复制代码
下面我们来看update方法:
  1. SceneManager.update = function () {
  2.     try {
  3.         this.tickStart();
  4.         if (Utils.isMobileSafari()) {
  5.             this.updateInputData();
  6.         }
  7.         this.updateManagers();
  8.         this.updateMain();
  9.         this.tickEnd();
  10.     } catch (e) {
  11.         this.catchException(e);
  12.     }
  13. };
复制代码
其中tickStart和tickEnd使用了fpsMeter库的方法来计算requestAnimationFrame具体到底过了多少帧

其实主要我们在这里关心的只有:updateMain
  1. SceneManager.updateMain = function () {
  2.     if (Utils.isMobileSafari()) {
  3.         this.changeScene();
  4.         this.updateScene();
  5.     } else {
  6.         var newTime = this._getTimeInMsWithoutMobileSafari();
  7.         var fTime = (newTime - this._currentTime) / 1000;
  8.         if (fTime > 0.25) fTime = 0.25;
  9.         this._currentTime = newTime;
  10.         this._accumulator += fTime;
  11.         while (this._accumulator >= this._deltaTime) {
  12.             this.updateInputData();
  13.             this.changeScene();
  14.             this.updateScene();
  15.             this._accumulator -= this._deltaTime;
  16.         }
  17.     }
  18.     this.renderScene();
  19.     this.requestUpdate();
  20. };
复制代码

其中updateInputData()是用来处理用户输入状态的, 跟平台是相关的
抛去和平台相关和处理帧率的代码, 最终总共呼叫了其他4个方法
changeScene();
updateScene();
renderScene();
requestUpdate();

先来看 changeScene
  1. SceneManager.changeScene = function () {
  2.     if (this.isSceneChanging() && !this.isCurrentSceneBusy()) {
  3.         if (this._scene) {
  4.             this._scene.terminate();
  5.             this._scene.detachReservation();
  6.             this._previousClass = this._scene.constructor;
  7.         }
  8.         this._scene = this._nextScene;
  9.         if (this._scene) {
  10.             this._scene.attachReservation();
  11.             this._scene.create();
  12.             this._nextScene = null;
  13.             this._sceneStarted = false;
  14.             this.onSceneCreate();
  15.         }
  16.         if (this._exiting) {
  17.             this.terminate();
  18.         }
  19.     }
  20. };
复制代码
这个方法主要负责场景的切换
如果有下一个场景, 那么当前场景中止, 场景管理将_scene指向_nextScene并将_nextScene清空
如果没有下一个场景, 那么整个游戏将被结束

isSceneChanging() 是检测是否存在this._exiting或this._nextScene的判断方法
也就是是不是要退出场景或者跳转到下一个场景
我们在呼叫run()的时候就创建了一个新场景并标记为this._nextScene
所以这里会第一次返回true
this.isCurrentSceneBusy()是判断当前场景是否在忙碌detachReservation, detachReservation是图像缓存相关的方法, 以后有机会再仔细分析
_scene.create是各个Scene类自己重载的初始化方法

接下来是updateScene()
  1. SceneManager.updateScene = function () {
  2.     if (this._scene) {
  3.         if (!this._sceneStarted && this._scene.isReady()) {
  4.             this._scene.start();
  5.             this._sceneStarted = true;
  6.             this.onSceneStart();
  7.         }
  8.         if (this.isCurrentSceneStarted()) {
  9.             this._scene.update();
  10.         }
  11.     }
  12. };
复制代码
这个方法调用了场景的update方法从而让场景的状态刷新
this._scene.isReady是判断资源是否加载完毕的, 每个Scene类都可以覆写这个方法
如果this._sceneStarted没有设置为true, 就需要进行一次初始化
执行_scene的start方法, 每个Scene类都可以覆写这个方法
如果已经started了, 那么就会执行this._scene.update()方法
一样这个方法各个不同的scene都可以覆写父类的规则

那么问题来了之前在changeScene里已经有了create这个方法
那么create和start有什么区别呢?

答案就是create不需要资源加载完毕, 而start必须资源加载完毕
因为资源加载是个异步的过程
在run的第一个requestAnimationFrame里_scene就已经create()了
但是下一requestAnimationFrame里_scene并不一定会执行start()
玩家会停留在loading界面里直到资源完全加载完毕, 使得this._scene.isReady()返回true

renderScene用来执行场景的图形渲染, 如果资源未加载完毕则渲染加载界面
图形部分留到以后再说吧~偷懒~

最后再一次呼叫requestUpdate让整个流程在下一个requestAnimationFrame里再循环

这就是一个简略的Game Loop的源码分析了
SceneManager除了run以外还有操作场景栈, 预加载, 暂停和恢复等等其他方法
由于篇幅的原因留到以后遇到了再说.

评分

参与人数 1+1 收起 理由
fux2 + 1 棒~

查看全部评分

夏普的道具店

塞露提亚-道具屋的经营妙方同人作品
发布帖:点击这里

Lv4.逐梦者

梦石
0
星屑
14250
在线时间
718 小时
注册时间
2011-7-16
帖子
1428

开拓者

2
发表于 2019-8-12 11:51:22 | 只看该作者
不错,学到了不少rmmv的相关知识
RMMV网络插件,开源免费,内含服务器端,无需强制登录,云数据,弹幕,云存档,排名,兑换码,版本检测,可自由上架下架删除。q群399090587
免打包运行MV游戏,云游戏,安卓App雷神游戏厅,在线玩游戏,上传下载游戏
开源游戏:重装机兵之重装归来【RMMV制作】全球首款按照美剧分季分集的方式发布的游戏
体素画 -- MV画3D像素图的画板
RMMV显示3D模型和场景的插件
RMMV显示spine骨骼动画的插件
RMMV秘密通道插件
突破敌群数量上限8个的插件
在rmmv中显示gif动态图片的插件
一款可以在mv游戏界面的任意位置显示任意文字的插件
RMMV Toast 插件 带物品得失提示,可以设置开启关闭 兼容yep itemcore
制作一个改名卡道具插件、调整标题页面菜单的插件、在标题页面之前显示大段文字的插件、标题页面显示版本号的插件
物品得失自动提示自动上色自动换行插件
我的Q群 663889472
另外,我的插件、范例、游戏都在这里
回复 支持 反对

使用道具 举报

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

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-5-11 01:44

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表