赞 | 65 |
VIP | 231 |
好人卡 | 2 |
积分 | 19 |
经验 | 35171 |
最后登录 | 2024-9-15 |
在线时间 | 1554 小时 |
Lv3.寻梦者
- 梦石
- 0
- 星屑
- 1912
- 在线时间
- 1554 小时
- 注册时间
- 2013-4-13
- 帖子
- 917
|
加入我们,或者,欢迎回来。
您需要 登录 才可以下载或查看,没有帐号?注册会员
x
继续了~继续了~
想了好久要更什么部分, 因为既可以跳到下个场景去还可以看看Title_Scene用了的一些东西
还有一大堆欠债...
不过我最后决定还是讲一个跟实际制作插件关系比较大的内容吧
讲讲用到的CommandWindow, 不过首先还是把基础类整理个大框吧
===============
第六章 Window, 神使
回顾一下, 之前我们在Scene_Title里遇见了CommandWindow:
- Scene_Title.prototype.createCommandWindow = function() {
- this._commandWindow = new Window_TitleCommand();
- this._commandWindow.setHandler('newGame', this.commandNewGame.bind(this));
- this._commandWindow.setHandler('continue', this.commandContinue.bind(this));
- this._commandWindow.setHandler('options', this.commandOptions.bind(this));
- this.addWindow(this._commandWindow);
- };
复制代码
CommandWindow属于RM对象中比较庞大的那一家族Window类
可能许多人开始学RM就是从创建一个对话框开始的(我就是
那也是一个Window
那么今天我们就分析一下连接系统和玩家的界面Window类的原理吧
- function Window() {
- this.initialize.apply(this, arguments);
- }
- Window.prototype = Object.create(PIXI.Container.prototype);
- Window.prototype.constructor = Window;
复制代码
和Scene一样, window也是继承于Container类,
所以他也可以有儿子, 孙子...
Window是一个基础类, 它具有所有Window的子类的共通特性
下面是它的构造器, 能看得出他有很多的属性, 我把简单的几个属性用注释的方法标记了
具体到后面还会再说明
- Window.prototype.initialize = function () {
- PIXI.Container.call(this);
- this._isWindow = true; // 表示这个对象是一个窗口
- this._windowskin = null; // 窗口皮肤
- this._width = 0; // 宽度
- this._height = 0; //高度
- this._cursorRect = new Rectangle(); // 光标矩形
- this._openness = 255; // 开启状态, 255是完全开启, 0是完全关闭
- this._animationCount = 0; // 动画计时
- this._padding = 18; // 内边距(边框到内容的间隔)
- this._margin = 4; // 外边距(边框外的间距)
- this._colorTone = [0, 0, 0]; // 色调
- this._windowSpriteContainer = null; // 精灵容器
- this._windowBackSprite = null; // 后退图标
- this._windowCursorSprite = null; // 光标图标
- this._windowFrameSprite = null; // 帧图标
- this._windowContentsSprite = null; // 内容
- this._windowArrowSprites = []; // 见到图标
- this._windowPauseSignSprite = null; // 暂停符号
- this._createAllParts();
复制代码 注意带下划线的代表是私有属性(只是认为约定, 实际上没有效果)
暴露给其他对象的属性是不带下划线_的, 比如 _windowskin和windowskin
但是它们之间是有联系的:
- Object.defineProperty(Window.prototype, 'windowskin', {
- get: function () {
- return this._windowskin;
- },
- set: function (value) {
- if (this._windowskin !== value) {
- this._windowskin = value;
- this._windowskin.addLoadListener(this._onWindowskinLoad.bind(this));
- }
- },
- configurable: true
- });
复制代码
我们一般读取一个属性的方法是 obj.name
一般设置一个属性的方法是 obj.name = value
但是其实还有一种别的方法来定义属性, 那就是Object.defineProperty和Object.defineProperties
(如果你用过Vue的话你就知道我在说什么, 当然现在还有Proxy了, 不过就好像见到了老朋友一样亲切
Object.defineProperty(Window.prototype, 'windowskin', ... } 就代表定义Window.prototype.windowskin属性
后面的一个对象里定义了这个属性的属性(the attributes of property)
set代表设置这个属性的方法
比如我定义一个属性obj.double的set方法为 x => {obj._x * 2}, 那么当我写 obj.double = 10的时候, 实际上就调用了set方法, 将obj._x的值设置成了20
这个set就好像是一个强盗, 他把你原来的赋值方法给拦截了下来, 重新用他的方法执行了一遍
而get也是同理, 当你访问obj.double的时候就会触发get方法, 如果get方法是 () => obj._x 的话就会把obj._x的值返回来
configurable代表你是否还可以修改这个属性, 如果设置成false你再想修改get和set方法就会报错的
总而言之以后每当对取windowskin这个属读取的时候就会返回内部属性_windowskin,
而对windowskin进行赋值的时候, 则会调用这个set属性,
这里首先会判断_windowskin和将要被赋予的值是否相等
如果相等则跳过赋值和加载的逻辑
因为加载图片是一个很好资源的过程
所以这里这个属性拦截的作用就是避免重复加载相同资源减少浪费
除了优化相关的, set和get拦截还有别的用处
比如: contents
- Object.defineProperty(Window.prototype, 'contents', {
- get: function () {
- return this._windowContentsSprite.bitmap;
- },
- set: function (value) {
- this._windowContentsSprite.bitmap = value;
- },
- configurable: true
- });
复制代码
实际上指向并操作的是this._windowContentsSprite.bitmap
对比 this._windowContentsSprite.bitmap 和 this.contents 你会发现这样写代码变得整洁了
还有width和height(包括margin和padding我就不粘了, 意思是一样的)
- /**
- * The width of the window in pixels.
- *
- * @property width
- * @type Number
- */
- Object.defineProperty(Window.prototype, 'width', {
- get: function () {
- return this._width;
- },
- set: function (value) {
- this._width = value;
- this._refreshAllParts();
- },
- configurable: true
- });
- /**
- * The height of the window in pixels.
- *
- * @property height
- * @type Number
- */
- Object.defineProperty(Window.prototype, 'height', {
- get: function () {
- return this._height;
- },
- set: function (value) {
- this._height = value;
- this._refreshAllParts();
- },
- configurable: true
- });
复制代码
主要作用是每次修改的时候调用强制刷新
- /**
- * The opacity of the window without contents (0 to 255).
- *
- * @property opacity
- * @type Number
- */
- Object.defineProperty(Window.prototype, 'opacity', {
- get: function () {
- return this._windowSpriteContainer.alpha * 255;
- },
- set: function (value) {
- this._windowSpriteContainer.alpha = value.clamp(0, 255) / 255;
- },
- configurable: true
- });
复制代码
在opacity(contentsOpacity, backOpacity, openness, )里, 还可以限制输入的数值在(0, 255)中, 并且转换为百分比记录在变量中
读取的时候又可以还原回原数据
Window类把Object.defineProperty的骚操作基本都给演示了一遍
希望大家写脚本的时候可以学到两招
现在说说window.update, 顺便把之前Scene的update方法再贴出来联系一下
- <blockquote><blockquote>Scene_Base.prototype.updateChildren = function() {
复制代码 之前说它们都是Container, 所以本质上是个树状结构, update父容器就会遍历其所有子容器
因为大家都有这个update方法, 尽管父类和子类不一定是一个类(大概率不是)
依然可以执行update
update其实不执行多少重绘方法的
因为重绘是个非常昂贵的操作
所以只有在需要重绘的地方我们才去重绘
比如移动和改变大小
- Window.prototype.move = function (x, y, width, height) {
- this.x = x || 0;
- this.y = y || 0;
- if (this._width !== width || this._height !== height) {
- this._width = width || 0;
- this._height = height || 0;
- this._refreshAllParts();
- }
- };
复制代码
大家看到 XX || 0 这个操作千万不要迷惑
这个JS的技巧之一
如果我执行window.move()
那么x, y, width, height 的值就是undefined
我们知道JS的对象是有真值的(可以通过!!obj)来判断
而||(or)操作其实是如果左侧的对象的真值为true, 那么返回左边, 否则返回右边
而&&(and)操作其实如果左侧的对象的真值为true, 那么返回右边, 否则返回左边
而且这些操作如果前一个条件满足了, 后面的判断就不继续了
和你认为的是不是有点不一样?
width || 0 其实就是设定width的默认值为0
这个是以前js没有参数默认值时候人们使用的技巧
现在有了拆包操作, 和默认值等方法, 这样的奇怪代码就会减少了
&&也有一个常用技巧
比如我们知道访问undefined的一个属性会报错, 这也是我们经常遇到的错误信息
- Uncaught TypeError: Cannot read property 'a' of undefined
复制代码
诶呀, 是不是都很熟悉啊, 有没有看到这个就想要哭啊
fear not, 这一般都是没处理好空值引起的
比如我们有一个对象 x, x可能是个对象也可能undefined
那么如果新手来写就会写成
- if(x !== undefined){
- return x.y
- } else {
复制代码 实际上undefined的真值是false我们可以直接写
如果x是undefined就不会执行x.y这个会报错的指令了!
好回到move方法上来
我们注意的核心方法是_refreshAllParts
这个方法之前也出现了很多次比如在windowSkin的set方法里
- this._windowskin.addLoadListener(this._onWindowskinLoad.bind(this));
复制代码 的回调函数
- Window.prototype._onWindowskinLoad = function () {
- this._refreshAllParts();
- };
复制代码
那它实际上也是一组方法的调用
- Window.prototype._refreshAllParts = function () {
- this._refreshBack();
- this._refreshFrame();
- this._refreshCursor();
- this._refreshContents();
- this._refreshArrows();
- this._refreshPauseSign();
- };
复制代码 里面都是和重绘内容相关的
因为我线性代数学的不好, 绘图部分我就先无视了=. =(留下了学渣的泪水
我们只要记住这些方法是更新画面的就好了
不过有一点refresh只负责绘制, 它不会改变数据
这些就是Window类的主要方法, 还有一些设置方法大家有兴趣可以自己去看
等到以后如果遇到我们就拿出来讲一讲
Window类只是提供了基本功能, Window家族也是个继承关系非常复杂的类
我们接下来主要是还是围绕CommandWindow来讲
再然后推进到下一个场景去
|
评分
-
查看全部评分
|