Project1
标题:
RPG Maker MV 的源码研究 其八
[打印本页]
作者:
沉滞的剑
时间:
2019-8-18 20:51
标题:
RPG Maker MV 的源码研究 其八
本帖最后由 沉滞的剑 于 2019-8-19 22:42 编辑
逐渐进入一些表层的部分
于是以后会陆陆续续加入一点练手的部分
希望大家能够觉得有所帮助
====================
第七章 标题什么的以后就不起了, 都多大了, 还中二
继续讲Window相关的东西
Window还是个很底层的类, Window_Base对这个类继续填充功能
Window只是定义了一些属性和基本的生命周期
实际上它并不具备显示的效果
比如它定义了窗口皮肤, 但又没有直接加载皮肤的方法
所以一般我们写Window类的时候不会继承Window
那我们先来看看Window_Base
Window_Base.prototype = Object.create(Window.prototype);
Window_Base.prototype.constructor = Window_Base;
Window_Base.prototype.initialize = function(x, y, width, height) {
Window.prototype.initialize.call(this);
this.loadWindowskin(); // 加载窗口皮肤
this.move(x, y, width, height); // 设定窗口位置和大小
this.updatePadding(); // 初始化内边距
this.updateBackOpacity(); // 初始化可见度
this.updateTone(); // 初始化色调
this.createContents(); // 重绘内容
this._opening = false; // 设置窗口正在打开的状态为false
this._closing = false; // 设置窗口正在关闭的状态为false
this._dimmerSprite = null; // 遮挡背景色
};
复制代码
Window_Base提供了一些列简单到复杂的属性计算
比如contentsWidth, contentsHeight, fittingHeight等
窗口是遵循Box模型
所以内容的宽高都要减去两个padding的宽高
Window_Base.prototype.contentsWidth = function() {
return this.width - this.standardPadding() * 2;
};
Window_Base.prototype.contentsHeight = function() {
return this.height - this.standardPadding() * 2;
};
复制代码
fittingHeight则是文字高度计算, 不过Window_Base并没有定义一个文字区域, 所以这个方法是给其子类使用的
Window_Base.prototype.fittingHeight = function(numLines) {
return numLines * this.lineHeight() + this.standardPadding() * 2;
};
复制代码
当然还有不知道为啥还保留着远古气息的通过点阵图取色的骚操作
看下Window.png右下角那个区域你们就懂了...
Window_Base.prototype.textColor = function(n) {
var px = 96 + (n % 8) * 12 + 6;
var py = 144 + Math.floor(n / 8) * 12 + 6;
return this.windowskin.getPixel(px, py);
};
复制代码
还有一些reset字体, 字体大小, 颜色, 边距, 色调, 背景透明度等等重置为常量的方法我就不再赘述了
createContents()就是重绘内容, 之前的内容会被清空
Window_Base.prototype.createContents = function() {
this.contents = new Bitmap(this.contentsWidth(), this.contentsHeight());
this.resetFontSettings();
};
复制代码
剩下就是关于开启关闭窗口的动画相关的了
先看看周期函数update
Window_Base.prototype.update = function() {
Window.prototype.update.call(this);
this.updateTone(); // 更新色调
this.updateOpen(); // 更新打开动画
this.updateClose(); // 更新关闭动画
this.updateBackgroundDimmer(); // 遮挡背景色
};
复制代码
'
窗口打开动画的程度通过this.openness来判断0~255
打开状态由this._opening 和 this._closing判断
Window_Base.prototype.updateOpen = function() {
if (this._opening) {
this.openness += 32;
if (this.isOpen()) {
this._opening = false;
}
}
};
Window_Base.prototype.updateClose = function() {
if (this._closing) {
this.openness -= 32;
if (this.isClosed()) {
this._closing = false;
}
}
};
复制代码
如果在关闭中已经是0了, 则不再关闭
如果在打开中已经是255了则不再打开
我们看一下open和close方法, 也很简单粗暴:
Window_Base.prototype.open = function() {
if (!this.isOpen()) {
this._opening = true;
}
this._closing = false;
};
Window_Base.prototype.close = function() {
if (!this.isClosed()) {
this._closing = true;
}
this._opening = false;
};
复制代码
Window_Base.prototype.isOpening = function() {
return this._opening;
};
Window_Base.prototype.isClosing = function() {
return this._closing;
};
/**
* Returns true if the window is completely open (openness == 255).
*
* @method isOpen
*/
Window.prototype.isOpen = function() {
return this._openness >= 255;
};
// 这两个方法是继承于Window方法的
/**
* Returns true if the window is completely closed (openness == 0).
*
* @method isClosed
*/
Window.prototype.isClosed = function() {
return this._openness <= 0;
};
复制代码
还有几个状态是不需要动画效果的, 所以只有是或者不是两个状态
Window_Base.prototype.show = function() {
this.visible = true;
};
Window_Base.prototype.hide = function() {
this.visible = false;
};
Window_Base.prototype.activate = function() {
this.active = true;
};
Window_Base.prototype.deactivate = function() {
this.active = false;
};
复制代码
好了, 现在我们的内容虽然有了, 不过是个空白的
Window_Base提供了几个方法让我们对它进绘制
因为绘制方法实在是太多了, 我就不详解了
而且里面还有很多繁琐的计算, 比如绘制矩形文字区域drawTextEx
就需要计算各种转义符, 计算高度等, 以后还是会用到的, 到时候再说
给大家感受一下, 基本上能画的标准图形在这里都有了
drawTextEx
drawText
drawIcon
drawFace
drawCharacter
drawGauge // 应该是血条之类的计量柱
drawActorCharacter
drawActorFace
drawActorName
drawActorClass
drawActorNickname
drawActorLevel
drawActorIcons
drawCurrentAndMax // 数值显示 比如hp/MaxHp
drawActorHp
drawActorMp
drawActorTp
drawActorSimpleStatus
drawItemName
drawCurrencyValue
复制代码
还有几个零散的方法:
canvasToLocalX/Y是转换canvas的绝对坐标到本地坐标
reserveFaceImages()是将所有队伍头像反转显示
Window_Base.prototype.canvasToLocalX = function(x) {
var node = this;
while (node) {
x -= node.x;
node = node.parent;
}
return x;
};
Window_Base.prototype.canvasToLocalY = function(y) {
var node = this;
while (node) {
y -= node.y;
node = node.parent;
}
return y;
};
Window_Base.prototype.reserveFaceImages = function() {
$gameParty.members().forEach(function(actor) {
ImageManager.reserveFace(actor.faceName());
}, this);
};
复制代码
===================
练手时间:
我们来自己定义一个从Window_Base派生的窗口吧假设我们想要弄一个200*200大小的窗口
class Window_Test extends Window_Base {
constructor(x, y) {
super(x, y, 200, 200)
}
}
复制代码
嗯 我们采取ES5的class写法, 而不是官方Object.create, 都9012了好么!
我们想要一开始的就重绘这个窗口, 显示一段文字
那我们就创建一个refresh方法并在构造器里调用它
class Window_Test extends Window_Base {
constructor(x, y) {
super(x, y, 200, 200)
this.refresh = () => {
this.drawText('Hello World', 0, 0, 200)
}
this.refresh()
}
}
复制代码
我们先了解一下drawText, 这个其实是一个Bitmap对象的方法
Window_Base.prototype.drawText = function(text, x, y, maxWidth, align) {
this.contents.drawText(text, x, y, maxWidth, this.lineHeight(), align);
};
Bitmap.prototype.drawText = function(text, x, y, maxWidth, lineHeight, align) {
// Note: Firefox has a bug with textBaseline: Bug 737852
// So we use 'alphabetic' here.
if (text !== undefined) {
var tx = x;
var ty = y + lineHeight - (lineHeight - this.fontSize * 0.7) / 2;
var context = this._context;
var alpha = context.globalAlpha;
maxWidth = maxWidth || 0xffffffff;
if (align === 'center') {
tx += maxWidth / 2;
}
if (align === 'right') {
tx += maxWidth;
}
context.save();
context.font = this._makeFontNameText();
context.textAlign = align;
context.textBaseline = 'alphabetic';
context.globalAlpha = 1;
this._drawTextOutline(text, tx, ty, maxWidth);
context.globalAlpha = alpha;
this._drawTextBody(text, tx, ty, maxWidth);
context.restore();
this._setDirty();
}
};
复制代码
可以看出我们可以选择位置, 最大宽度, 和对齐方式, 对齐方式'right' 和 'center'还有个默认的左对齐
这里就用默认的左对齐就好了
我把成员方法的定义写在了构造函数里
原因是nw版本不支持在class里直接写箭头函数...怨念
进入游戏, 打开F12控制台
我们把类的定义执行一遍, 再输入
const myWin = new Window_Test()
SceneManager._scene.addWindow(myWin)
复制代码
一个带有文字的对话框就出现了
不过让我们再进一步, 我们希望这个窗口在加载的时候要有一个打开动画
class Window_Test extends Window_Base {
constructor(x, y) {
super(x, y, 200, 200)
this.openness = 0
this.open()
this.refresh = () => {
this.drawText('Hello World', 0, 0, 200)
}
this.refresh()
}
}
复制代码
这样一个创建时播放打开动画的窗口就创造完了
但是这些只是一个窗口最基本的展示能力, 为了有更多的交互行为
还会有更高级的Window类拓展了Window_Base的功能
我们很快就会讲到更多的Window类, 下次再见~
作者:
walf_man
时间:
2019-8-21 20:16
越来越感觉到mv工程的宏大
欢迎光临 Project1 (https://rpg.blue/)
Powered by Discuz! X3.1