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

Project1

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

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

[复制链接]

Lv3.寻梦者

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

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

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

x
这里是还活着的楼主, 不多说, 继续
对了想起来一个JS细节
关于function.bind(this)的解释
JavaScript的特性是静态作用域, 所有变量的作用域都在声明的时候就被确定下来了
唯独有一个例外就是this, 它并不是在声明而是在调用的时被确定指向
而使用bind可以强行绑定this的到当前的对象上来
否则再别的场景调用的时候, this可不一定指向哪里的

=================
第五章 Scene_Title: 自带BGM的男人

我们之前看完了Scene_Boot
得知了Scene是具有生命周期钩子的, 比如create和run, 并被SceneManager调度
虽然Scene_Boot是伟大的, 但它的生命却是短暂的
而Scene_Title是第一个真正拥有update能力的Scene
那我们就先了解下Scene_Title的生命周期吧

构造函数什么都没干, 略过
先看看第一个create函数
  1. Scene_Title.prototype.create = function() {
  2.     Scene_Base.prototype.create.call(this);
  3.     this.createBackground();
  4.     this.createForeground();
  5.     this.createWindowLayer();
  6.     this.createCommandWindow();
  7. }
复制代码
嗯, 看得出它调用了更多的create...
我们以createBackground为例来看看每个create具体都做了什么
  1. Scene_Title.prototype.createBackground = function() {
  2.     this._backSprite1 = new Sprite(ImageManager.loadTitle1($dataSystem.title1Name));
  3.     this._backSprite2 = new Sprite(ImageManager.loadTitle2($dataSystem.title2Name));
  4.     this.addChild(this._backSprite1);
  5.     this.addChild(this._backSprite2);
  6. };
复制代码

$dataSystem就是之前说的由DataManager加载进来的System.json游戏数据
对应工具数据库中的系统选项卡
然后, 嗯, ImageManager又出现了, 估计下一篇就要先讲它了, 不然欠得债就越来越多啦~

我们目前只要知道每个图片的底层加载的是一个 new Image() 对象就可以了
Image本质上就是个<image>标签, 通过设置src路径而让浏览器加载图片

图片加载是个极其复杂的过程, 一个不完整的调用栈如下:
ImageManager.loadTitle1 => 加载标题背景图
ImageManager.loadBitmap => 加载位图共通方法
ImageManager.loadNormalBitmap => 加载非空位图共通方法
Bitmap.load => 创建Bitmap对象静态方法
Bitmap.prototype._requestImage => 创建Image对象, 异步请求资源
Bitmap.prototype._onLoad => 资源加载完毕回调函数
....

这回我们还是主要关心场景的流程部分
addChild是之前继承的PIXI.Container的方法
Scene_Title是背景图, 前景图的父容器,也同样是WindowLayer的父容器,
而CommandWindow是WindowLayer的子节点,
其中关键的addWindow方法和createWindowLayer是由Scene_Base继承而来的
  1. Scene_Title.prototype.createCommandWindow = function() {
  2.     this._commandWindow = new Window_TitleCommand();
  3.     this._commandWindow.setHandler('newGame',  this.commandNewGame.bind(this));
  4.     this._commandWindow.setHandler('continue', this.commandContinue.bind(this));
  5.     this._commandWindow.setHandler('options',  this.commandOptions.bind(this));
  6.     this.addWindow(this._commandWindow);
  7. };
复制代码
WindowLayer和Window也是很重要的游戏对象不过...你懂的, 以后再说
现在只要理解的就是选项窗口的每一项都绑定了一个Scene_Title的回调函数
也就是每当玩家做出选择行为的时候, 对应的回到函数就会做出相应
比如你选择新游戏, 那么commandNewGame就会被触发

create看完了看start
  1.     Scene_Base.prototype.start.call(this);
  2.     SceneManager.clearStack();
  3.     this.centerSprite(this._backSprite1);
  4.     this.centerSprite(this._backSprite2);
  5.     this.playTitleMusic();
  6.     this.startFadeIn(this.fadeSpeed(), false);
  7. };
复制代码


嗯, 在SceneManager调用了Scene_Title的start方法,
回过头来Scene_Title又调用了SceneManager的clearStack方法
嗯有来有回, 这是个很容易的不需要留到以后去看的方法
  1. SceneManager.clearStack = function () {
  2.     this._stack = [];
  3. };
复制代码
这个方法将SceneManager的场景栈典型的应用时用到压栈的方式从父场景进入子场景
父场景暂停, 压入子场景,等待子场景结束, 子场景弹出就恢复了父场景centerSprite只是单纯得让精灵居中的方法

  1. Scene_Title.prototype.centerSprite = function(sprite) {
  2.     sprite.x = Graphics.width / 2;
  3.     sprite.y = Graphics.height / 2;
  4.     sprite.anchor.x = 0.5;
  5.     sprite.anchor.y = 0.5;
  6. };
复制代码
后两个方法是播放背景音乐和开头的淡入效果
  1. Scene_Title.prototype.playTitleMusic = function() {
  2.     AudioManager.playBgm($dataSystem.titleBgm);
  3.     AudioManager.stopBgs();
  4.     AudioManager.stopMe();
  5. };
复制代码
  1. Scene_Base.prototype.startFadeIn = function(duration, white) {
  2.     this.createFadeSprite(white);
  3.     this._fadeSign = 1;
  4.     this._fadeDuration = duration || 30;
  5.     this._fadeSprite.opacity = 255;
  6. };
复制代码
具体相关的AudioManager和FadeSprite按照惯例以后再说~

start里的内容都是只需要加载一次的, 而Scene_Title的内容其实实在游戏循环中不断更新的
用户可以一直呆在标题画面, 让bgm循环一遍又一遍都是没问题的
这就是updae周期函数的作用
还记得在SceneManger里讲过的:
start执行一遍后, started标记设置为true, 之后下一帧以后就不会执行start而会执行update

  1. Scene_Base.prototype.update = function() {
  2.     this.updateFade();
  3.     this.updateChildren();
  4. };

  5. Scene_Base.prototype.updateChildren = function() {
  6.     this.children.forEach(function(child) {
  7.         if (child.update) {
  8.             child.update();
  9.         }
  10.     });
  11. };
复制代码

恩, 其实update在Title这里没有什么太多的事情要做
除了淡入淡出的动画需要自己动手以外就没有什么亲自要改变的东西了
对于场景而言, 绝大多数的状态更新是由每个子节点自己决定的
父容器只是帮助他们调用了update方法而已

Scene_Title于是乎就开始等待用户的输入, 来决定下一个Scene是什么
  1. Scene_Title.prototype.commandNewGame = function() {
  2.     DataManager.setupNewGame();
  3.     this._commandWindow.close();
  4.     this.fadeOutAll();
  5.     SceneManager.goto(Scene_Map);
  6. };

  7. Scene_Title.prototype.commandContinue = function() {
  8.     this._commandWindow.close();
  9.     SceneManager.push(Scene_Load);
  10. };

  11. Scene_Title.prototype.commandOptions = function() {
  12.     this._commandWindow.close();
  13.     SceneManager.push(Scene_Options);
  14. };
复制代码
注意一个细节, 在 continue和option这里用的全是push
因为读取界面和选项界面对于Scene_Title是一个子场景, 可以随时返回并保持状态的
而New game则是直接goto的

不过今天就先到这里吧, 下一篇搞什么还没想好, 嗯再说吧~
有点困, 收工



夏普的道具店

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

Lv4.逐梦者

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

开拓者

2
发表于 2019-8-21 19:50:12 | 只看该作者
辛苦楼主无私奉献,写的很好
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-9-24 17:15

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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