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

Project1

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

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

[复制链接]

Lv3.寻梦者

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

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

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

x
本帖最后由 沉滞的剑 于 2019-8-19 22:42 编辑

逐渐进入一些表层的部分
于是以后会陆陆续续加入一点练手的部分
希望大家能够觉得有所帮助

====================
第七章 标题什么的以后就不起了, 都多大了, 还中二


继续讲Window相关的东西
Window还是个很底层的类, Window_Base对这个类继续填充功能
Window只是定义了一些属性和基本的生命周期
实际上它并不具备显示的效果
比如它定义了窗口皮肤, 但又没有直接加载皮肤的方法
所以一般我们写Window类的时候不会继承Window

那我们先来看看Window_Base
  1. Window_Base.prototype = Object.create(Window.prototype);
  2. Window_Base.prototype.constructor = Window_Base;

  3. Window_Base.prototype.initialize = function(x, y, width, height) {
  4.     Window.prototype.initialize.call(this);
  5.     this.loadWindowskin(); // 加载窗口皮肤
  6.     this.move(x, y, width, height); // 设定窗口位置和大小
  7.     this.updatePadding(); // 初始化内边距
  8.     this.updateBackOpacity(); // 初始化可见度
  9.     this.updateTone(); // 初始化色调
  10.     this.createContents(); // 重绘内容
  11.     this._opening = false; // 设置窗口正在打开的状态为false
  12.     this._closing = false; // 设置窗口正在关闭的状态为false
  13.     this._dimmerSprite = null; // 遮挡背景色
  14. };
复制代码

Window_Base提供了一些列简单到复杂的属性计算
比如contentsWidth, contentsHeight, fittingHeight等

窗口是遵循Box模型


所以内容的宽高都要减去两个padding的宽高
  1. Window_Base.prototype.contentsWidth = function() {
  2.     return this.width - this.standardPadding() * 2;
  3. };

  4. Window_Base.prototype.contentsHeight = function() {
  5.     return this.height - this.standardPadding() * 2;
  6. };
复制代码

fittingHeight则是文字高度计算, 不过Window_Base并没有定义一个文字区域, 所以这个方法是给其子类使用的
  1. Window_Base.prototype.fittingHeight = function(numLines) {
  2.     return numLines * this.lineHeight() + this.standardPadding() * 2;
  3. };
复制代码

当然还有不知道为啥还保留着远古气息的通过点阵图取色的骚操作
看下Window.png右下角那个区域你们就懂了...

  1. Window_Base.prototype.textColor = function(n) {
  2.     var px = 96 + (n % 8) * 12 + 6;
  3.     var py = 144 + Math.floor(n / 8) * 12 + 6;
  4.     return this.windowskin.getPixel(px, py);
  5. };
复制代码

还有一些reset字体, 字体大小, 颜色, 边距, 色调, 背景透明度等等重置为常量的方法我就不再赘述了
createContents()就是重绘内容, 之前的内容会被清空
  1. Window_Base.prototype.createContents = function() {
  2.     this.contents = new Bitmap(this.contentsWidth(), this.contentsHeight());
  3.     this.resetFontSettings();
  4. };
复制代码

剩下就是关于开启关闭窗口的动画相关的了
先看看周期函数update
  1. Window_Base.prototype.update = function() {
  2.     Window.prototype.update.call(this);
  3.     this.updateTone(); // 更新色调
  4.     this.updateOpen(); // 更新打开动画
  5.     this.updateClose(); // 更新关闭动画
  6.     this.updateBackgroundDimmer(); // 遮挡背景色
  7. };
复制代码
'
窗口打开动画的程度通过this.openness来判断0~255
打开状态由this._opening 和 this._closing判断
  1. Window_Base.prototype.updateOpen = function() {
  2.     if (this._opening) {
  3.         this.openness += 32;
  4.         if (this.isOpen()) {
  5.             this._opening = false;
  6.         }
  7.     }
  8. };

  9. Window_Base.prototype.updateClose = function() {
  10.     if (this._closing) {
  11.         this.openness -= 32;
  12.         if (this.isClosed()) {
  13.             this._closing = false;
  14.         }
  15.     }
  16. };
复制代码

如果在关闭中已经是0了, 则不再关闭
如果在打开中已经是255了则不再打开

我们看一下open和close方法, 也很简单粗暴:
  1. Window_Base.prototype.open = function() {
  2.     if (!this.isOpen()) {
  3.         this._opening = true;
  4.     }
  5.     this._closing = false;
  6. };

  7. Window_Base.prototype.close = function() {
  8.     if (!this.isClosed()) {
  9.         this._closing = true;
  10.     }
  11.     this._opening = false;
  12. };
复制代码

  1. Window_Base.prototype.isOpening = function() {
  2.     return this._opening;
  3. };

  4. Window_Base.prototype.isClosing = function() {
  5.     return this._closing;
  6. };


  7. /**
  8. * Returns true if the window is completely open (openness == 255).
  9. *
  10. * @method isOpen
  11. */
  12. Window.prototype.isOpen = function() {
  13.     return this._openness >= 255;
  14. };
  15. // 这两个方法是继承于Window方法的
  16. /**
  17. * Returns true if the window is completely closed (openness == 0).
  18. *
  19. * @method isClosed
  20. */
  21. Window.prototype.isClosed = function() {
  22.     return this._openness <= 0;
  23. };
复制代码

还有几个状态是不需要动画效果的, 所以只有是或者不是两个状态
  1. Window_Base.prototype.show = function() {
  2.     this.visible = true;
  3. };

  4. Window_Base.prototype.hide = function() {
  5.     this.visible = false;
  6. };

  7. Window_Base.prototype.activate = function() {
  8.     this.active = true;
  9. };

  10. Window_Base.prototype.deactivate = function() {
  11.     this.active = false;
  12. };
复制代码

好了, 现在我们的内容虽然有了, 不过是个空白的
Window_Base提供了几个方法让我们对它进绘制
因为绘制方法实在是太多了, 我就不详解了
而且里面还有很多繁琐的计算, 比如绘制矩形文字区域drawTextEx
就需要计算各种转义符, 计算高度等, 以后还是会用到的, 到时候再说
给大家感受一下, 基本上能画的标准图形在这里都有了
  1. drawTextEx
  2. drawText
  3. drawIcon
  4. drawFace
  5. drawCharacter
  6. drawGauge // 应该是血条之类的计量柱
  7. drawActorCharacter
  8. drawActorFace
  9. drawActorName
  10. drawActorClass
  11. drawActorNickname
  12. drawActorLevel
  13. drawActorIcons
  14. drawCurrentAndMax // 数值显示 比如hp/MaxHp
  15. drawActorHp
  16. drawActorMp
  17. drawActorTp
  18. drawActorSimpleStatus
  19. drawItemName
  20. drawCurrencyValue
复制代码
还有几个零散的方法:
canvasToLocalX/Y是转换canvas的绝对坐标到本地坐标
reserveFaceImages()是将所有队伍头像反转显示
  1. Window_Base.prototype.canvasToLocalX = function(x) {
  2.     var node = this;
  3.     while (node) {
  4.         x -= node.x;
  5.         node = node.parent;
  6.     }
  7.     return x;
  8. };

  9. Window_Base.prototype.canvasToLocalY = function(y) {
  10.     var node = this;
  11.     while (node) {
  12.         y -= node.y;
  13.         node = node.parent;
  14.     }
  15.     return y;
  16. };

  17. Window_Base.prototype.reserveFaceImages = function() {
  18.     $gameParty.members().forEach(function(actor) {
  19.         ImageManager.reserveFace(actor.faceName());
  20.     }, this);
  21. };
复制代码

===================
练手时间:

我们来自己定义一个从Window_Base派生的窗口吧假设我们想要弄一个200*200大小的窗口
  1. class Window_Test extends Window_Base {

  2.   constructor(x, y) {
  3.     super(x, y, 200, 200)
  4.   }
  5. }
复制代码

嗯 我们采取ES5的class写法, 而不是官方Object.create, 都9012了好么!
我们想要一开始的就重绘这个窗口, 显示一段文字
那我们就创建一个refresh方法并在构造器里调用它
  1. class Window_Test extends Window_Base {

  2.   constructor(x, y) {
  3.     super(x, y, 200, 200)
  4.     this.refresh = () => {
  5.       this.drawText('Hello World', 0, 0, 200)
  6.     }
  7.     this.refresh()
  8.   }
  9. }
复制代码

我们先了解一下drawText, 这个其实是一个Bitmap对象的方法
  1. Window_Base.prototype.drawText = function(text, x, y, maxWidth, align) {
  2.     this.contents.drawText(text, x, y, maxWidth, this.lineHeight(), align);
  3. };
  4. Bitmap.prototype.drawText = function(text, x, y, maxWidth, lineHeight, align) {
  5.     // Note: Firefox has a bug with textBaseline: Bug 737852
  6.     //       So we use 'alphabetic' here.
  7.     if (text !== undefined) {
  8.         var tx = x;
  9.         var ty = y + lineHeight - (lineHeight - this.fontSize * 0.7) / 2;
  10.         var context = this._context;
  11.         var alpha = context.globalAlpha;
  12.         maxWidth = maxWidth || 0xffffffff;
  13.         if (align === 'center') {
  14.             tx += maxWidth / 2;
  15.         }
  16.         if (align === 'right') {
  17.             tx += maxWidth;
  18.         }
  19.         context.save();
  20.         context.font = this._makeFontNameText();
  21.         context.textAlign = align;
  22.         context.textBaseline = 'alphabetic';
  23.         context.globalAlpha = 1;
  24.         this._drawTextOutline(text, tx, ty, maxWidth);
  25.         context.globalAlpha = alpha;
  26.         this._drawTextBody(text, tx, ty, maxWidth);
  27.         context.restore();
  28.         this._setDirty();
  29.     }
  30. };
复制代码


可以看出我们可以选择位置, 最大宽度, 和对齐方式, 对齐方式'right' 和 'center'还有个默认的左对齐
这里就用默认的左对齐就好了
我把成员方法的定义写在了构造函数里
原因是nw版本不支持在class里直接写箭头函数...怨念

进入游戏, 打开F12控制台
我们把类的定义执行一遍, 再输入
  1. const myWin = new Window_Test()
  2. SceneManager._scene.addWindow(myWin)
复制代码
一个带有文字的对话框就出现了

不过让我们再进一步, 我们希望这个窗口在加载的时候要有一个打开动画
  1. class Window_Test extends Window_Base {

  2.   constructor(x, y) {
  3.     super(x, y, 200, 200)
  4.     this.openness = 0
  5.     this.open()
  6.     this.refresh = () => {
  7.       this.drawText('Hello World', 0, 0, 200)
  8.     }
  9.     this.refresh()
  10.   }
  11. }
复制代码

这样一个创建时播放打开动画的窗口就创造完了

但是这些只是一个窗口最基本的展示能力, 为了有更多的交互行为
还会有更高级的Window类拓展了Window_Base的功能
我们很快就会讲到更多的Window类, 下次再见~

评分

参与人数 2+2 收起 理由
wr282828 + 1 精品文章
康姆图帕帕 + 1 塞糖

查看全部评分

夏普的道具店

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

Lv4.逐梦者

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

开拓者

2
发表于 2019-8-21 20:16:53 | 只看该作者
越来越感觉到mv工程的宏大
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-11-16 09:41

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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