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

Project1

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

[交流讨论] RPG Maker MV 的源码研究 十二

[复制链接]

Lv3.寻梦者

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

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

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

x

Scene_Title还有一些其他能说的地方
比如WindowLayer, 比如Scene_Load, Scene_Options...
不过我们先加快一下进度, 直接开始Scene_Map

但是, 我们还是要先分析一下它们的交接工作
于是Scene_Title可以再水一篇啦
=================================

第十一章

因为是创建新游戏,
Scene_Title在把指挥权交给Scene_Map之前
还是需要先做一点初始化

  1. Scene_Title.prototype.commandNewGame = function () {
  2.   DataManager.setupNewGame(); // 初始化游戏数据
  3.   this._commandWindow.close(); // 关闭(动画)菜单
  4.   this.fadeOutAll(); // 淡出动画
  5.   SceneManager.goto(Scene_Map); // 切换场景
  6. };
复制代码


我DataManager又回来了...
  1. DataManager.setupNewGame = function () {
  2.   this.createGameObjects(); // 创建游戏对象
  3.   this.selectSavefileForNewGame(); // 指向一个新的存档位置
  4.   $gameParty.setupStartingMembers(); // 初始化队伍
  5.   $gamePlayer.reserveTransfer($dataSystem.startMapId,
  6.     $dataSystem.startX, $dataSystem.startY); // 初始化玩家位置
  7.   Graphics.frameCount = 0;
  8. };
复制代码


创建游戏对象
这些对象代表了当前游戏的状态
不过日后再表

  1. DataManager.createGameObjects = function () {
  2.   $gameTemp = new Game_Temp();
  3.   $gameSystem = new Game_System();
  4.   $gameScreen = new Game_Screen();
  5.   $gameTimer = new Game_Timer();
  6.   $gameMessage = new Game_Message();
  7.   $gameSwitches = new Game_Switches();
  8.   $gameVariables = new Game_Variables();
  9.   $gameSelfSwitches = new Game_SelfSwitches();
  10.   $gameActors = new Game_Actors();
  11.   $gameParty = new Game_Party();
  12.   $gameTroop = new Game_Troop();
  13.   $gameMap = new Game_Map();
  14.   $gamePlayer = new Game_Player();
  15. };
复制代码

为存档界面选择一个初始位置, 一个很小但是很贴心的功能
这个和之前我们判断存档是否存在的方法是很类似的

  1. DataManager.selectSavefileForNewGame = function () {
  2.   var globalInfo = this.loadGlobalInfo();
  3.   this._lastAccessedId = 1;
  4.   if (globalInfo) {
  5.     var numSavefiles = Math.max(0, globalInfo.length - 1);
  6.     // 如果还有剩余存档位则指向下一个空存档点
  7.     if (numSavefiles < this.maxSavefiles()) {
  8.       this._lastAccessedId = numSavefiles + 1;
  9.       // 否则指向最新的存档点
  10.     } else {
  11.       var timestamp = Number.MAX_VALUE;
  12.       for (var i = 1; i < globalInfo.length; i++) {
  13.         if (!globalInfo[i]) {
  14.           this._lastAccessedId = i;
  15.           break;
  16.         }
  17.         if (globalInfo[i].timestamp < timestamp) {
  18.           timestamp = globalInfo[i].timestamp;
  19.           this._lastAccessedId = i;
  20.         }
  21.       }
  22.     }
  23.   }
  24. };
复制代码
$gameParty是保存队伍信息的, 它是Game_Party类的实例
Game_Party又是Game_Unit的子类
不过这些以后再说

  1. Game_Party.prototype.setupStartingMembers = function () {
  2.   this._actors = [];
  3.   $dataSystem.partyMembers.forEach(function (actorId) {
  4.     if ($gameActors.actor(actorId)) {
  5.       this._actors.push(actorId);
  6.     }
  7.   }, this);
  8. };
复制代码

$dataSystem是数据库对象, 这个之前已经说过了
这里又出现了另外一个游戏对象$gameActors
这个是用来保存角色信息的
其实这个函数写得很莫名其妙啦
直接写this._actors = [...$dataSystem.partyMembers]
一行就搞定了


  1. $gamePlayer.reserveTransfer($dataSystem.startMapId,
  2.   $dataSystem.startX, $dataSystem.startY);

  3. Game_Player.prototype.reserveTransfer = function (mapId, x, y, d, fadeType) {
  4.   this._transferring = true; // 带过场的传送
  5.   this._newMapId = mapId; // 新地图id
  6.   this._newX = x; // 新x坐标
  7.   this._newY = y; // 新y坐标
  8.   this._newDirection = d; // 新朝向
  9.   this._fadeType = fadeType; // 过场类型
  10. };
复制代码

$gamePlayer是玩家对象, 是Game_Player类的实例
代表玩家的人物行走图, 继承于Game_Character
它的兄弟类还有, Game_Follower, GameVehicle 和 Game_Event
本质上都是地图事件

setupNewGame就是这些内容
交接还有一些其他画面上的事情要处理


  1. this.fadeOutAll(); // 黑色画面淡出

  2. Scene_Base.prototype.fadeOutAll = function () {
  3.   var time = this.slowFadeSpeed() / 60; // 淡出持续时间
  4.   AudioManager.fadeOutBgm(time); // 淡出声音
  5.   AudioManager.fadeOutBgs(time);
  6.   AudioManager.fadeOutMe(time);
  7.   this.startFadeOut(this.slowFadeSpeed()); // 初始化淡出
  8. };

  9. Scene_Base.prototype.startFadeOut = function (duration, white) {
  10.   this.createFadeSprite(white); // 淡出蒙版精灵
  11.   this._fadeSign = -1; // 淡出标识
  12.   this._fadeDuration = duration || 30; // 持续时间
  13.   this._fadeSprite.opacity = 0; // 初始化可见度
  14. };

  15. Scene_Base.prototype.createFadeSprite = function (white) {
  16.   if (!this._fadeSprite) { // 如果淡出蒙版不存在则创建一个新的
  17.     this._fadeSprite = new ScreenSprite();
  18.     this.addChild(this._fadeSprite);
  19.   }
  20.   if (white) { // 白色的
  21.     this._fadeSprite.setWhite();
  22.   } else { // 黑色的
  23.     this._fadeSprite.setBlack();
  24.   }
  25. };
复制代码

回忆一下update方法
并贴出一个场景淡出的更新方法


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

  5. Scene_Base.prototype.updateFade = function () {
  6.   // 如果持续时间还有
  7.   if (this._fadeDuration > 0) {

  8.     var d = this._fadeDuration;
  9.     // 大于0则是淡入(透明度增大), 小于0是淡出(透明度减小)
  10.     // 可以看出每次淡入淡出的幅度和剩余时间是有关系的
  11.     // 剩余时间越小, 淡入淡出幅度越大
  12.     if (this._fadeSign > 0) {
  13.       this._fadeSprite.opacity -= this._fadeSprite.opacity / d;
  14.     } else {
  15.       this._fadeSprite.opacity += (255 - this._fadeSprite.opacity) / d;
  16.     }
  17.     // 持续时间递减
  18.     this._fadeDuration--;
  19.   }
  20. };

  21. `
  22. 关闭窗口和淡出都是需要持续一段时间的
  23. 如果这时候直接跳转场景, 这些设置就白费了
  24. 回顾一下SceneManager是如何判断是否可以跳转场景的
  25. `

  26. SceneManager.goto = function (sceneClass) {
  27.   if (sceneClass) {
  28.     this._nextScene = new sceneClass();
  29.   }
  30.   //... 略
  31. };

  32. // update时候调用
  33. SceneManager.changeScene = function () {
  34.   if (this.isSceneChanging() && !this.isCurrentSceneBusy()) {
  35.     //... 略
  36.   }
  37. };

  38. SceneManager.isCurrentSceneBusy = function () {
  39.   return this._scene && this._scene.isBusy();
  40. };

  41. Scene_Base.prototype.isBusy = function () {
  42.   return this._fadeDuration > 0;
  43. };
复制代码

==========================================================
今天内容比较水, 所以补充点js的知识吧
今天的课题是JavaScript的作用域和作用域链

Javascript是一门静态作用域/词法作用域的语言
简单来说就是通过阅读代码(编译阶段)就能确定变量的可用范围

这点和之前说的属性是不一样的, 属性是动态委托
obj.a的a 我们并不一定能在这句代码执行前确认a是不是存在
也不能确定a是来源于a, 还是a.__proto__

而变量不一样, 如果有console.log(a), 你一定能在代码中找到它的指代
变量的可用范围, 也就是作用域在声明后就不会改变

变量的可用范围和其声明的关键字有关
ES5之前: var, 现在const, let
var的作用域范围是整个函数体
const的作用域是所在代码块, 也就是一对花括号之间, const变量不可变
let的作用域和const相同, 但是let声明的变量是可变的
可变不可变指的是是否能改变引用
比如const a = 1, a = 2就会报错
const obj = {}, obj = {} 也会报错
但是const obj = {}, obj.a = 1 就不会报错, 因为obj引用的对象没有变

变量分为全局变量和局部变量
在ES5之前, 在全局作用域中声明的变量, 等价于window对象的属性.
而const和let是不会有这种问题的
现在为了避免这种可能带来的困惑, 推荐使用const和let

再说一说什么是作用域链
假如我们有:


  1. const a = 1
  2. const b = 321
  3. const f = () => {
  4.   const a = 2
  5.   const f = () => {
  6.     const a = 3
  7.     console.log(a, b) // 3, 321
  8.   }
  9.   f()
  10.   console.log(a, b) // 2, 321
  11. }
  12. f()
  13. console.log(a, b) // 1, 321
复制代码

js首先会去找在本作用域定义的变量
如果找不到则在上一层作用域里继续寻找, 这就是作用域链

静态作用域虽然简单, 但是会暴露一个问题
我们在用js来写类的时候, 会先写类的成员方法再实例化类的对象
而且一个类会有多个实例
这时候静态作用域就无法满足我们的需求了
我们需要this: 上下文对象

this和变量不一样, 它并不是静态的, 它的指代是在呼叫的时候确定的
比如obj.f(), 如果obj是一个引用类型, 那么f内的this指代就是obj
哪怕f并不是obj的方法, 而是obj.__proto__的方法
f中的this指向依然是obj, 这个this和它声明时候的作用域无关

嗯, 看上去一切都很美好
不过this的不确定性却带给了开发者很大的问题
比如回调函数 我们这么写:

  1. PluginManager.onError = function (e) {
  2.   this._errorUrls.push(e.target._url);
  3. };

  4. PluginManager.loadScript = function (name) {
  5.   // 略...
  6.   var script = document.createElement('script');
  7.   // 略...
  8.   script.onerror = this.onError // 有什么问题?
  9.   // 略...
  10. };
复制代码
问题是: 当script加载错误触发onerror的时候, this指代的是谁?
是script还是PluginManager?
你认为是script, 恭喜你很正确
但是我们想要this指向PluginManager呢?
这就需要bind, call和apply方法在声明时来强行绑定this对象了

call和apply区别不大
用途都是绑定this并呼叫函数
比如 f.call(obj, ...args) 和 f.apply(obj, args)
bind则是直接返回一个新函数, 这个函数的this在被绑定一次后不能再绑定
比如f.bind(obj)(...args) 就等价于f.call(obj, ...args)

在es5中class定义中也可以使用this, class的成员方法中的this指的是实例本身
  1. class A {
  2.   constructor() {
  3.     this.abc = 5
  4.   }
  5. }
复制代码

然而this还是太灵活, 太容易出错了, 于是乎箭头函数出现了
  1. const f = () => {
  2.   console.log(this)
  3. }
  4. const obj = { f }
  5. obj.f() // window
复制代码

在箭头函数中this指向的就是当前作用域中的this
这个还可以将class的成员函数中的this永久绑定

  1. class A {
  2.   constructor() {
  3.     this.f = () => {
  4.       console.log(this)
  5.     }
  6.   }
  7. }

  8. const f = (new A()).f
  9. f() // A {f: ƒ}
复制代码

值得一提的箭头函数是无法再被bind, call, apply绑定的

好了水了这么多, 收工!


评分

参与人数 3+3 收起 理由
walf_man + 1 塞糖
康姆图帕帕 + 1 塞糖
玄羽 + 1 塞糖

查看全部评分

夏普的道具店

塞露提亚-道具屋的经营妙方同人作品
发布帖:点击这里
您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

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

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

GMT+8, 2024-5-12 20:34

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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