Project1

标题: RPG Maker MV 的源码研究 其四 [打印本页]

作者: 沉滞的剑    时间: 2019-8-12 21:02
标题: RPG Maker MV 的源码研究 其四
下班回来了~继续更~
居然有人看~开心~
=====================================
第三章 Scene_Boot —— SceneManager说要有光

Scene_Boot是整个世界的开始
这次我们就来研究它在它短暂的一生中为我们留下了什么

我们先退回一步来分析一下所有场景的祖宗类: Scene_Base
不过, Scene_Base虽然是最基础的Scene, 但是它又是继承与core.js里的Stage类
然后Stage类又是继承于pixiJs的Container类, 实质上只是Container的一层改名换姓的封装
鉴于我不想最后去读PixiJS的源码
我们就先了解下Container的文档吧:

A Container represents a collection of display objects.
It is the base class of all display objects that act as a container for other objects (like Sprites).

简单来说Container类提供了一个树状的渲染结构每一个节点存在一个children的数组用来记录子节点同时子节点也有一个引用指向其父容器,
我们渲染一个容器的时候可以遍历它的children
并让每个子节点执行自己的渲染方法.
本质上就是个深度遍历.

根据以下pixi.js源码(好吧, 真香)我们可以推断渲染的顺序
  1.     Container.prototype.renderWebGL = function renderWebGL(renderer) {
  2.         // if the object is not visible or the alpha is 0 then no need to render this element
  3.         if (!this.visible || this.worldAlpha <= 0 || !this.renderable) {
  4.             return;
  5.         }

  6.         // do a quick check to see if this element has a mask or a filter.
  7.         if (this._mask || this._filters) {
  8.             this.renderAdvancedWebGL(renderer);
  9.         } else {
  10.             this._renderWebGL(renderer);

  11.             // simple render children!
  12.             for (var i = 0, j = this.children.length; i < j; ++i) {
  13.                 this.children[i].renderWebGL(renderer);
  14.             }
  15.         }
  16.     };
复制代码
嗯, 官方写了注释我就不啰嗦了只要注意先渲染容器, 然后从0号child开始依次渲染child, 后渲染的图像会覆盖先渲染的图

好, 我们回到Scene_Base, 它提供了场景比较重要的几个生命周期钩子

initialize: 构造器
  1. Scene_Boot.prototype.initialize = function() {
  2.     Scene_Base.prototype.initialize.call(this);
  3.     this._startDate = Date.now();
  4. };
复制代码
只是记录了开始的时间, 只在判断是否加载字体超时的时候用到了

还有create()和start(), 上一次介绍过create和start的区别了,  让我们直接看代码吧
  1. Scene_Boot.prototype.create = function() {
  2.     Scene_Base.prototype.create.call(this);
  3.     DataManager.loadDatabase();
  4.     ConfigManager.load();
  5.     this.loadSystemWindowImage();
  6. };
复制代码

这里又出现了两个新Manager, 按照惯例我们以后再细看(可能下一篇就讲DataManager), 这里先简单说一说
DataManage管理数据库(也就是data文件夹下的json文件)和游戏对象(如果是测试的话还会读取测试事件信息)
loadDataBase方法会把数据加载进内存, 也就是你在控制台里输入$dataXXXX看到的东西
其中有一个需要关注的是在加载了System.json, 也就是$dataSystem的时候会反过来出发Scene_Boot的静态方法loadSystemImages
源码暂且贴一下, 以后会再解释(我懒嘛)
  1.     if (object === $dataSystem) {
  2.         Decrypter.hasEncryptedImages = !!object.hasEncryptedImages;
  3.         Decrypter.hasEncryptedAudio = !!object.hasEncryptedAudio;
  4.         Scene_Boot.loadSystemImages();
  5.     }
复制代码

然后这个是loadSystemImages, 目前你只需要知道reserveSystem是读取并缓存图像就好了
  1. Scene_Boot.loadSystemImages = function() {
  2.     ImageManager.reserveSystem('IconSet');
  3.     ImageManager.reserveSystem('Balloon');
  4.     ImageManager.reserveSystem('Shadow1');
  5.     ImageManager.reserveSystem('Shadow2');
  6.     ImageManager.reserveSystem('Damage');
  7.     ImageManager.reserveSystem('States');
  8.     ImageManager.reserveSystem('Weapons1');
  9.     ImageManager.reserveSystem('Weapons2');
  10.     ImageManager.reserveSystem('Weapons3');
  11.     ImageManager.reserveSystem('ButtonSet');
  12. };
复制代码
(稍微提一句: 类似data文件或者其他图片/音乐资源的东西加载的时候都是异步)
其实同样在create方法里, 最后还有一个loadSystemWindowImage, 它的实现如下:
  1. Scene_Boot.prototype.loadSystemWindowImage = function() {
  2.     ImageManager.reserveSystem('Window');
  3. };
复制代码
不要问我为啥他们把代码写得如此分散...我也不知道啊!!
ConfigManager是个系统设置相关的, 等哪天实在没东西可说了我再研究研究它吧
这个名字让我感觉他没有牌面

还记得之前说create会在一开始执行, 而run需要检查准备完毕以后才能执行
那么我们先看看检查函数isReady

  1. Scene_Boot.prototype.isReady = function() {
  2.     if (Scene_Base.prototype.isReady.call(this)) {
  3.         return DataManager.isDatabaseLoaded() && this.isGameFontLoaded();
  4.     } else {
  5.         return false;
  6.     }
  7. };

  8. Scene_Boot.prototype.isGameFontLoaded = function() {
  9.     if (Graphics.isFontLoaded('GameFont')) {
  10.         return true;
  11.     } else if (!Graphics.canUseCssFontLoading()){
  12.         var elapsed = Date.now() - this._startDate;
  13.         if (elapsed >= 60000) {
  14.             throw new Error('Failed to load GameFont');
  15.         }
  16.     }
  17. };
复制代码

就是检查DataManager和字体是否加载了,
DataManger内容以后再说, font相关的你们有兴趣可以自己看看, 这些都不重要

一旦isReady成立, 下个周期就会执行start
  1. Scene_Boot.prototype.start = function() {
  2.     Scene_Base.prototype.start.call(this);
  3.     SoundManager.preloadImportantSounds();
  4.     if (DataManager.isBattleTest()) {
  5.         DataManager.setupBattleTest();
  6.         SceneManager.goto(Scene_Battle);
  7.     } else if (DataManager.isEventTest()) {
  8.         DataManager.setupEventTest();
  9.         SceneManager.goto(Scene_Map);
  10.     } else {
  11.         this.checkPlayerLocation();
  12.         DataManager.setupNewGame();
  13.         SceneManager.goto(Scene_Title);
  14.         Window_TitleCommand.initCommandPosition();
  15.     }
  16.     this.updateDocumentTitle();
  17. };

  18. Scene_Boot.prototype.updateDocumentTitle = function() {
  19.     document.title = $dataSystem.gameTitle;
  20. };
复制代码

嗯, 惊了, 居然真的有人去改网页标题了...
咳咳, 这不是重点, 请无视

其实看得出来, 一旦加载完毕Sence_Boot的使命就结束了
和测试相关我们先不去管, Scene_Battle和Scene_Map等到以后遇到了再说
Scene_Title是第一个真正意义上的玩家接触到的场景
等我们把其他系统层面的Manger(可能除了ConfigManager?)分析完了以后再说

嗯, 没错, 这个系列的特点就是日后再说, 也可能日后不说...
水水更健康嘛~


作者: walf_man    时间: 2019-8-21 19:14
写得好,支持楼主的无私奉献




欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1