Project1
标题:
RPG Maker MV 的源码研究 十二
[打印本页]
作者:
沉滞的剑
时间:
2019-8-23 05:14
标题:
RPG Maker MV 的源码研究 十二
Scene_Title还有一些其他能说的地方
比如WindowLayer, 比如Scene_Load, Scene_Options...
不过我们先加快一下进度, 直接开始Scene_Map
但是, 我们还是要先分析一下它们的交接工作
于是Scene_Title可以再水一篇啦
=================================
第十一章
因为是创建新游戏,
Scene_Title在把指挥权交给Scene_Map之前
还是需要先做一点初始化
Scene_Title.prototype.commandNewGame = function () {
DataManager.setupNewGame(); // 初始化游戏数据
this._commandWindow.close(); // 关闭(动画)菜单
this.fadeOutAll(); // 淡出动画
SceneManager.goto(Scene_Map); // 切换场景
};
复制代码
我DataManager又回来了...
DataManager.setupNewGame = function () {
this.createGameObjects(); // 创建游戏对象
this.selectSavefileForNewGame(); // 指向一个新的存档位置
$gameParty.setupStartingMembers(); // 初始化队伍
$gamePlayer.reserveTransfer($dataSystem.startMapId,
$dataSystem.startX, $dataSystem.startY); // 初始化玩家位置
Graphics.frameCount = 0;
};
复制代码
创建游戏对象
这些对象代表了当前游戏的状态
不过日后再表
DataManager.createGameObjects = function () {
$gameTemp = new Game_Temp();
$gameSystem = new Game_System();
$gameScreen = new Game_Screen();
$gameTimer = new Game_Timer();
$gameMessage = new Game_Message();
$gameSwitches = new Game_Switches();
$gameVariables = new Game_Variables();
$gameSelfSwitches = new Game_SelfSwitches();
$gameActors = new Game_Actors();
$gameParty = new Game_Party();
$gameTroop = new Game_Troop();
$gameMap = new Game_Map();
$gamePlayer = new Game_Player();
};
复制代码
为存档界面选择一个初始位置, 一个很小但是很贴心的功能
这个和之前我们判断存档是否存在的方法是很类似的
DataManager.selectSavefileForNewGame = function () {
var globalInfo = this.loadGlobalInfo();
this._lastAccessedId = 1;
if (globalInfo) {
var numSavefiles = Math.max(0, globalInfo.length - 1);
// 如果还有剩余存档位则指向下一个空存档点
if (numSavefiles < this.maxSavefiles()) {
this._lastAccessedId = numSavefiles + 1;
// 否则指向最新的存档点
} else {
var timestamp = Number.MAX_VALUE;
for (var i = 1; i < globalInfo.length; i++) {
if (!globalInfo[i]) {
this._lastAccessedId = i;
break;
}
if (globalInfo[i].timestamp < timestamp) {
timestamp = globalInfo[i].timestamp;
this._lastAccessedId = i;
}
}
}
}
};
复制代码
$gameParty是保存队伍信息的, 它是Game_Party类的实例
Game_Party又是Game_Unit的子类
不过这些以后再说
Game_Party.prototype.setupStartingMembers = function () {
this._actors = [];
$dataSystem.partyMembers.forEach(function (actorId) {
if ($gameActors.actor(actorId)) {
this._actors.push(actorId);
}
}, this);
};
复制代码
$dataSystem是数据库对象, 这个之前已经说过了
这里又出现了另外一个游戏对象$gameActors
这个是用来保存角色信息的
其实这个函数写得很莫名其妙啦
直接写this._actors = [...$dataSystem.partyMembers]
一行就搞定了
$gamePlayer.reserveTransfer($dataSystem.startMapId,
$dataSystem.startX, $dataSystem.startY);
Game_Player.prototype.reserveTransfer = function (mapId, x, y, d, fadeType) {
this._transferring = true; // 带过场的传送
this._newMapId = mapId; // 新地图id
this._newX = x; // 新x坐标
this._newY = y; // 新y坐标
this._newDirection = d; // 新朝向
this._fadeType = fadeType; // 过场类型
};
复制代码
$gamePlayer是玩家对象, 是Game_Player类的实例
代表玩家的人物行走图, 继承于Game_Character
它的兄弟类还有, Game_Follower, GameVehicle 和 Game_Event
本质上都是地图事件
setupNewGame就是这些内容
交接还有一些其他画面上的事情要处理
this.fadeOutAll(); // 黑色画面淡出
Scene_Base.prototype.fadeOutAll = function () {
var time = this.slowFadeSpeed() / 60; // 淡出持续时间
AudioManager.fadeOutBgm(time); // 淡出声音
AudioManager.fadeOutBgs(time);
AudioManager.fadeOutMe(time);
this.startFadeOut(this.slowFadeSpeed()); // 初始化淡出
};
Scene_Base.prototype.startFadeOut = function (duration, white) {
this.createFadeSprite(white); // 淡出蒙版精灵
this._fadeSign = -1; // 淡出标识
this._fadeDuration = duration || 30; // 持续时间
this._fadeSprite.opacity = 0; // 初始化可见度
};
Scene_Base.prototype.createFadeSprite = function (white) {
if (!this._fadeSprite) { // 如果淡出蒙版不存在则创建一个新的
this._fadeSprite = new ScreenSprite();
this.addChild(this._fadeSprite);
}
if (white) { // 白色的
this._fadeSprite.setWhite();
} else { // 黑色的
this._fadeSprite.setBlack();
}
};
复制代码
回忆一下update方法
并贴出一个场景淡出的更新方法
Scene_Base.prototype.update = function () {
this.updateFade();
this.updateChildren();
};
Scene_Base.prototype.updateFade = function () {
// 如果持续时间还有
if (this._fadeDuration > 0) {
var d = this._fadeDuration;
// 大于0则是淡入(透明度增大), 小于0是淡出(透明度减小)
// 可以看出每次淡入淡出的幅度和剩余时间是有关系的
// 剩余时间越小, 淡入淡出幅度越大
if (this._fadeSign > 0) {
this._fadeSprite.opacity -= this._fadeSprite.opacity / d;
} else {
this._fadeSprite.opacity += (255 - this._fadeSprite.opacity) / d;
}
// 持续时间递减
this._fadeDuration--;
}
};
`
关闭窗口和淡出都是需要持续一段时间的
如果这时候直接跳转场景, 这些设置就白费了
回顾一下SceneManager是如何判断是否可以跳转场景的
`
SceneManager.goto = function (sceneClass) {
if (sceneClass) {
this._nextScene = new sceneClass();
}
//... 略
};
// update时候调用
SceneManager.changeScene = function () {
if (this.isSceneChanging() && !this.isCurrentSceneBusy()) {
//... 略
}
};
SceneManager.isCurrentSceneBusy = function () {
return this._scene && this._scene.isBusy();
};
Scene_Base.prototype.isBusy = function () {
return this._fadeDuration > 0;
};
复制代码
==========================================================
今天内容比较水, 所以补充点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
再说一说什么是作用域链
假如我们有:
const a = 1
const b = 321
const f = () => {
const a = 2
const f = () => {
const a = 3
console.log(a, b) // 3, 321
}
f()
console.log(a, b) // 2, 321
}
f()
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的不确定性却带给了开发者很大的问题
比如回调函数 我们这么写:
PluginManager.onError = function (e) {
this._errorUrls.push(e.target._url);
};
PluginManager.loadScript = function (name) {
// 略...
var script = document.createElement('script');
// 略...
script.onerror = this.onError // 有什么问题?
// 略...
};
复制代码
问题是: 当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指的是实例本身
class A {
constructor() {
this.abc = 5
}
}
复制代码
然而this还是太灵活, 太容易出错了, 于是乎箭头函数出现了
const f = () => {
console.log(this)
}
const obj = { f }
obj.f() // window
复制代码
在箭头函数中this指向的就是当前作用域中的this
这个还可以将class的成员函数中的this永久绑定
class A {
constructor() {
this.f = () => {
console.log(this)
}
}
}
const f = (new A()).f
f() // A {f: ƒ}
复制代码
值得一提的箭头函数是无法再被bind, call, apply绑定的
好了水了这么多, 收工!
欢迎光临 Project1 (https://rpg.blue/)
Powered by Discuz! X3.1