启言
在RPG Maker MV中使用的语言是JavaScript作为脚本语言,在HTML中的Canvas画布上进行绘制图像及交互行为。
在游戏中能够感知到的最大的容器是场景,往下就是各种用于菜单操作的窗口,再往下可以看做是容器的精灵,之后在精灵中添加位图然后绘制出来。
场景
场景是游戏中的环境、建筑、机械、道具等。游戏场景通常可以理解为根据企划的要求还原出游戏中的建筑物、树木、天空、道路等可用元素(包含武器道具与NPC等)。
在RPG Maker MV的游戏中可以看到的装着窗口的容器就是场景,地图、主菜单、状态、装备、交易、标题等等,这些都是一个个的场景,场景中有人物移动、有效果变化,有窗口等等。
可以说游戏中的所有看得到的,听得到的一切元素构成了场景;只不过现在我们说的场景只是装着构成场景的各元素的一个盒子。
窗口
在游戏中窗口就是可被绘制出来的一个矩形区域,这个区域有着背景,边框,及内容,内容中有些是直接进行显示的,有些是可进行交互的。
WindowLayer 包含游戏的窗口层,简单解释就是游戏中各个窗口基本都是放在这里面的。
场景和窗口问题
通过之前各篇的内容,可以看到我在开发的代码中有些时候窗口并不是放在窗口层中的,而是直接放在场景中的,这是为什么呢?
窗口在窗口层中时会发现这样的一个怪象,窗口和窗口之间互相遮挡时会显示场景中的背景信息,而将精灵放入其中时会发现所有的精灵都在最顶层,即覆盖遮挡了所有的窗口,这也是之前绘制做战斗物品、法术窗口时出现的问题;由于代码功底较少,现在调试着代码比较麻烦,但也可以推测出是窗口层进行渲染时进行的操作。
WindowLayer.prototype.renderCanvas = function(renderer) {
if (!this.visible || !this.renderable) {
return;
}
if (!this._tempCanvas) {
this._tempCanvas = document.createElement('canvas');
}
this._tempCanvas.width = Graphics.width;
this._tempCanvas.height = Graphics.height;
var realCanvasContext = renderer.context;
var context = this._tempCanvas.getContext('2d');
context.save();
context.clearRect(0, 0, Graphics.width, Graphics.height);
context.beginPath();
context.rect(this.x, this.y, this.width, this.height);
context.closePath();
context.clip();
renderer.context = context;
for (var i = 0; i < this.children.length; i++) {
var child = this.children[i];
if (child._isWindow && child.visible && child.openness > 0) {
this._canvasClearWindowRect(renderer, child);
context.save();
child.renderCanvas(renderer);
context.restore();
}
}
context.restore();
renderer.context = realCanvasContext;
renderer.context.setTransform(1, 0, 0, 1, 0, 0);
renderer.context.globalCompositeOperation = 'source-over';
renderer.context.globalAlpha = 1;
renderer.context.drawImage(this._tempCanvas, 0, 0);
for (var j = 0; j < this.children.length; j++) {
if (!this.children[j]._isWindow) {
this.children[j].renderCanvas(renderer);
}
}
};
WindowLayer.prototype.renderWebGL = function(renderer) {
if (!this.visible || !this.renderable) {
return;
}
if (this.children.length==0) {
return;
}
renderer.flush();
this.filterArea.copy(this);
renderer.filterManager.pushFilter(this, this.filters);
renderer.currentRenderer.start();
var shift = new PIXI.Point();
var rt = renderer._activeRenderTarget;
var projectionMatrix = rt.projectionMatrix;
shift.x = Math.round((projectionMatrix.tx + 1) / 2 * rt.sourceFrame.width);
shift.y = Math.round((projectionMatrix.ty + 1) / 2 * rt.sourceFrame.height);
for (var i = 0; i < this.children.length; i++) {
var child = this.children[i];
if (child._isWindow && child.visible && child.openness > 0) {
this._maskWindow(child, shift);
renderer.maskManager.pushScissorMask(this, this._windowMask);
renderer.clear();
renderer.maskManager.popScissorMask();
renderer.currentRenderer.start();
child.renderWebGL(renderer);
renderer.currentRenderer.flush();
}
}
renderer.flush();
renderer.filterManager.popFilter();
renderer.maskManager.popScissorMask();
for (var j = 0; j < this.children.length; j++) {
if (!this.children[j]._isWindow) {
this.children[j].renderWebGL(renderer);
}
}
};
通过该渲染代码渲染了窗口层,后期看能否详细解读下。
场景中直接加入窗口就发现不会出现这样的情况,个人看法是场景的渲染将直接放入的窗口当成了类似图片一样的渲染,这样的重叠遮挡就不会暴露后面的背景了。
其实这用的话,之前遇到的指令问题也可以迎刃而解了!!!
不过场景与场景之前的问题也是一个头疼的问题,比如返回上一个场景,并显示之前打开的窗口,就发现了问题了!所以需要场景管理器来从中间进行协调管理。
战斗场景
战斗的场景刚进入时会出现敌人出现的消息弹窗,这和原来的游戏方式不搭配,毕竟刚进去时弹出合理,但从其他的场景返回战斗场景时就显得非常突兀了,这时的解决办法是在战斗管理器中开始战斗时注释掉对应的方法调用,但又会出现一个问题,默认的调用使用时会默认显示战斗的角色信息,及窗口,这样就是非常大的BUG,现在找到方法处理了后,又出现了一个战斗操作及人物状态的显示太慢,又是得优化的地方!!!
从其他场景返回战斗场景时可能人物的数据和敌人的数据会发生重置(还没有做到这里),这也是后面得处理的问题。