永远庭

Domus Hominis Ludentis

0%

RMMZ-从0开始构建自己的UI(2)

写好Clickable类之后便可以开始着手写Window了,不过在这之前还要做一些准备

Window 皮肤绘制

如同RM原生的Window皮肤,基本是一个正方形的格子,绘制的时候是把其上下左右边缘分别绘制,首先四个角按原样单独绘制,四条边进行长度缩放后绘制,中间填充双方向缩放绘制,这样可以将一个正方形在不失真的情况下绘成任意大小的矩形(上面的缩放也可以用 Tilemap 排列,取决于中间区域是不是纯色);为了方便起见为Bitmap单独写这样一个绘制皮肤的方法,难度没有,就是比较麻烦。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* @param {Bitmap} bitmap
* @param {number} dx
* @param {number} dy
* @param {number} dw
* @param {number} dh
* @param {number} lx - 左边缘宽度
* @param {number} rx - 右边缘宽度
* @param {number} uy - 上边缘宽度
* @param {number} by - 下边缘宽度
*/
Bitmap.prototype.DrawTexture = function(bitmap, dx, dy, dw, dh, lx, rx = lx, uy, by = uy) {
let w = bitmap.width - lx - rx;
let h = bitmap.height - uy - by;

//left top
this.blt(bitmap, 0, 0, lx, uy, dx, dy, lx, uy);
//top
this.blt(bitmap, lx, 0, w, uy, dx + lx, dy, dw - lx - rx, uy);
//right top
this.blt(bitmap, lx + w, 0, rx, uy, dx + dw - rx, dy, rx, uy);
//left bottom
this.blt(bitmap, 0, uy + h, lx, by, dx, dy + dh - by, lx, by);
//bottom
this.blt(bitmap, lx, uy + h, w, by, dx + lx, dy + dh - by, dw - lx -rx, by);
//right bottom
this.blt(bitmap, lx + w, uy + h, rx, by, dx + dw - rx, dy + dh - by, rx, by);
//left
this.blt(bitmap, 0, uy, lx, h, dx, dy + uy, lx, dh - uy - by);
//right
this.blt(bitmap, lx + w, uy, rx, h, dx + dw - rx, dy + uy, rx, dh - uy - by);
//center
this.blt(bitmap, lx, uy, w, h, dx + lx, dy + uy, dw - lx - rx, dh - uy - by);
};

另外一个准备,就是写一个Paddings类,方便一点,不然每次都要写四个看起来比较乱,比较简单就不具体说明了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Paddings {
/**
* @param {number} left
* @param {number} top
* @param {number} right
* @param {number} bottom
*/
constructor(left = 0, top = left, right = left, bottom = top) {
this._left = left;
this._top = top;
this._right = right;
this._bottom = bottom;
}
_left = 0;
_top = 0;
_right = 0;
_bottom = 0;
/** @returns number */
get left() { return this._left; }
/** @returns number */
get top() { return this._top; }
/** @returns number */
get right() { return this._top; }
/** @returns number */
get bottom() { return this._bottom; }
}

Window

准备好之后可以开始写 Window 了,首先还是先思考一下需求。 Window 由标题、背景、内容构成。(标题我后来放弃,没什么用,就不提了,不过加进去也简单)。背景就是窗口的背景板,背景上画皮肤;而内容是一个透明的空白图片,是写文字图片等内容的地方,覆盖在背景上面,在二者都用 Sprite 作为载体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
class CustomWindow extends Clickable { //继承Clickable
_title = '';
_titleHeight = 0;
/** @type Bitmap */
_titleTexture;
/** @type Bitmap */
_backTexture;
_active = false;
/**
* @param {number} x
* @param {number} y
* @param {number} w
* @param {number} h
* @param {string} title - 标题:废弃了
* @param {string} titlebg - 标题皮肤(背景):废弃了
* @param {number} th - 标题高度:废弃了
* @param {string} bg - 皮肤(背景)
*/
constructor(x, y, w, h, title, th, titlebg = 'wd_title_cmn', bg = 'wd_back_cmn') { //以默认背景图片作为默认参数
super(x, y, w, h);
this._title = title;
this._titleHeight = th;
this._contentPaddings = new Paddings(12, 12, 12, 12); //内容paddings,即内容与内容 sprite 的边距
this.Create();
this.titleTexture = ImageManager.ImageManager.loadBitmap('img/ui', titlebg);
this.backTexture = ImageManager.loadBitmap('img/ui', bg);
this.Move(x, y, w, h);
}

get backTexture() { return this._backTexture;}
set backTexture(val) {
if (this._backTexture !== val) {
this._backTexture = val;
this._backTexture.addLoadListener(this.OnBackTextureLoaded.bind(this)); //因为读取图片需要时间,使用回调函数
}
}

get titleTexture() { return this._titleTexture;}
set titleTexture(val) {
if (this._titleTexture !== val) {
this._titleTexture = val;
this._titleTexture.addLoadListener(this.OnTitleTextureLoaded.bind(this));
}
}

/** @returns {Bitmap} */
get content() { return this._contentSprite.bitmap; }

/**
* content paddings
* @returns {Paddings}
*/
get paddings() { return this._contentPaddings; }

/** @returns number */
get contentAreaHeight() {return this.height - this._titleHeight;}
/** @returns number */
get contentHeight() { return this.contentAreaHeight - this.paddings.top - this.paddings.bottom; }
/** @returns number */
get contentWidth() { return this.width - this.paddings.left - this.paddings.right; }


/**
* 移动窗口,如果宽高发生变化则需要重新绘制
* @param {number} x - x coordinate
* @param {number} y - y coordinate
* @param {number} width
* @param {number} height
*/
Move(x = 0, y = 0, width = 0, height = 0) {
this.x = x;
this.y = y;
if (this._width !== width || this._height !== height) {
this._width = width;
this._height = height;
this.RefreshAllParts();
}
}

Close() {
this.Deactivate();
this.visible = false;
}

Open() {
this.visible = true;
}

// 创建Sprite
Create() {
this.CreateBackSprite();
this.CreateContentSprite();
this.CreateTitleSprite();
}

/** @type Sprite */
_titleSprite;
/** @type Sprite */
_backSprite;
/** @type Sprite */
_contentSprite;
CreateTitleSprite() {
this._titleSprite = new Sprite();
this.addChild(this._titleSprite);
}

CreateBackSprite() {
this._backSprite = new Sprite();
this.addChild(this._backSprite);
this._backSprite.move(0, this._titleHeight);
}

CreateContentSprite() {
this._contentSprite = new Sprite(new Bitmap(this.contentWidth, this.contentHeight));
this.addChild(this._contentSprite);
this._contentSprite.move(this.paddings.left, this._titleHeight + this.paddings.top);
}

//刷新窗口内容
OnTitleTextureLoaded() {
if (this._titleHeight > 0) {
let b = new Bitmap(this.width, this._titleHeight + 12);
b.DrawTexture(this.titleTexture, 0, 0, this.width, this._titleHeight + 4, this.TitleBorders.left, this.TitleBorders.right, this.TitleBorders.top, this.TitleBorders.bottom);
b.drawText(this._title, 0, 0, this.width, this._titleHeight + 4, 'center');
this._titleSprite.bitmap = b;
}
}

OnBackTextureLoaded() {
let b = new Bitmap(this.width, this.height - this._titleHeight);
b.DrawTexture(this.backTexture, 0, 0, this.width, this.height - this._titleHeight, this.BackBorders.left, this.BackBorders.right, this.BackBorders.top, this.BackBorders.bottom);
this._backSprite.bitmap = b;
}

RefreshAllParts() {
this.RefreshTexturePosition();
this.RefreshContent();
}

RefreshTexturePosition() {
this.OnTitleTextureLoaded();
this.OnBackTextureLoaded();
}

RefreshContent() {
this._contentSprite.bitmap = new Bitmap(this.contentWidth, this.contentHeight);
this._contentSprite.move(this.paddings.left, this._titleHeight + this.paddings.top);
}
}

此外还窗口还要有一些绘制函数,用来更方便地绘制文字、图片,这个参考原生的 Window 抄过来就可以了,就不再详细说明。