Project1
标题:
RPG Maker MV 的源码研究 其三
[打印本页]
作者:
沉滞的剑
时间:
2019-8-11 16:03
标题:
RPG Maker MV 的源码研究 其三
第三章 时空管理者的日常
我们从SceneManager.run(Scene_Boot)说起
当web页面加载完毕后这条语句就会被执行
Scene_Boot是一个特殊的场景(Scene)
我们暂且不提,
先来说说SceneManager这个游戏世界的时空主宰者的故事
SceneManager人如其名主要负责场景的加载和卸载
我们先来看看这个SceneManager.run方法都做了什么
SceneManager.run = function (sceneClass) {
try {
this.initialize();
this.goto(sceneClass);
this.requestUpdate();
} catch (e) {
this.catchException(e);
}
};
复制代码
SceneManager.initialize做了很多检查错误和初始化场景的工作具体内容比如Graphics的内容会放到以后再说
SceneManager.goto(sceneClass)的作用是停止当前场景
创建新场景(根据传入的sceneClass类)并标记为_nextScene
SceneManager.goto = function (sceneClass) {
if (sceneClass) {
this._nextScene = new sceneClass();
}
if (this._scene) {
this._scene.stop();
}
};
复制代码
然后最后执行requestUpdate方法其中requestAnimationFrame方法可以理解为下一帧时执行传入的方法
但是这一帧并不一定是一个稳定的时间间隔, 对于游戏来说并不是可靠的帧
SceneManager.requestUpdate = function () {
if (!this._stopped) {
requestAnimationFrame(this.update.bind(this));
}
};
复制代码
下面我们来看update方法:
SceneManager.update = function () {
try {
this.tickStart();
if (Utils.isMobileSafari()) {
this.updateInputData();
}
this.updateManagers();
this.updateMain();
this.tickEnd();
} catch (e) {
this.catchException(e);
}
};
复制代码
其中tickStart和tickEnd使用了fpsMeter库的方法来计算requestAnimationFrame具体到底过了多少帧
其实主要我们在这里关心的只有:updateMain
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;
while (this._accumulator >= this._deltaTime) {
this.updateInputData();
this.changeScene();
this.updateScene();
this._accumulator -= this._deltaTime;
}
}
this.renderScene();
this.requestUpdate();
};
复制代码
其中updateInputData()是用来处理用户输入状态的, 跟平台是相关的
抛去和平台相关和处理帧率的代码, 最终总共呼叫了其他4个方法
changeScene();
updateScene();
renderScene();
requestUpdate();
先来看 changeScene
SceneManager.changeScene = function () {
if (this.isSceneChanging() && !this.isCurrentSceneBusy()) {
if (this._scene) {
this._scene.terminate();
this._scene.detachReservation();
this._previousClass = this._scene.constructor;
}
this._scene = this._nextScene;
if (this._scene) {
this._scene.attachReservation();
this._scene.create();
this._nextScene = null;
this._sceneStarted = false;
this.onSceneCreate();
}
if (this._exiting) {
this.terminate();
}
}
};
复制代码
这个方法主要负责场景的切换
如果有下一个场景, 那么当前场景中止, 场景管理将_scene指向_nextScene并将_nextScene清空
如果没有下一个场景, 那么整个游戏将被结束
isSceneChanging() 是检测是否存在this._exiting或this._nextScene的判断方法
也就是是不是要退出场景或者跳转到下一个场景
我们在呼叫run()的时候就创建了一个新场景并标记为this._nextScene
所以这里会第一次返回true
this.isCurrentSceneBusy()是判断当前场景是否在忙碌detachReservation, detachReservation是图像缓存相关的方法, 以后有机会再仔细分析
_scene.create是各个Scene类自己重载的初始化方法
接下来是updateScene()
SceneManager.updateScene = function () {
if (this._scene) {
if (!this._sceneStarted && this._scene.isReady()) {
this._scene.start();
this._sceneStarted = true;
this.onSceneStart();
}
if (this.isCurrentSceneStarted()) {
this._scene.update();
}
}
};
复制代码
这个方法调用了场景的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以外还有操作场景栈, 预加载, 暂停和恢复等等其他方法
由于篇幅的原因留到以后遇到了再说.
作者:
walf_man
时间:
2019-8-12 11:51
不错,学到了不少rmmv的相关知识
欢迎光临 Project1 (https://rpg.blue/)
Powered by Discuz! X3.1