钢铁雄心4 GUI教程 P7gridboxType(一)

注:观前提示,gridboxType与数组紧密结合,在观看前请确保你有足够的有关array的知识

正式讲解gridbox之前,让我们以KR为数不多添加的gui——经济阵营成员为例子

设想一下,如果我们需要移植这个GUI,我们会怎么写?
首先是附着在外交界面的一个点击按钮,P3(一)已经讲解过
最主要的是这个成员列表,如果按照先前的思路,我们可能会写一大堆iconType,instantTextboxType,并且使用无以计数的trigger,来模拟成员变动时列表的变更,显然这更加的反人类了

当需要创建具有相同结构的多个元件列表,或者基于游戏内容实时增添/减少元件数目的窗口时,我们就需要使用gridboxType

gridboxType,是一种独特的元件类型,它不同于先前介绍的三种基本元件(iconType,buttonType,instantTextboxType),它的工作原理是:关联一个被列举窗口(containerWindowType),依据数组(array)作为参照,批量生成相同的被列举窗口共同构成这个gridbox元件

以KR经济阵营成员为例:

红框部分是这个gridbox本身,而蓝框部分,是这个gridbox的被列举项,如果我们将这个关联打破,令这个被列举窗口单独出现:

可以看见,它本质是由背景,文本框,国旗,主要国家边框,共同组成的一个子窗口。
因此,gridboxType实质是:以所关联的窗口为模板,批量生成相同的窗口,共同组成一个元件。
基于以上对于gridboxType的理解,让我们尝试在原版中复刻KR的阵营列表

素材准备
基于原版风格的KR甚至使用的素材也全是原版图像文件,因此可以略过
前端设计
先让我们构思这个列表窗口:退出按钮,背景,阵营名字,内部是成员列表(gridbox),还需要一个滑轮(P6介绍)

不同于其它的元件,直接写在窗口下,当我们加入一个gridboxType时,它不是以“单一”元件的形式存在,它是根据当前起点,向下“生成”。
因此,直接添加在主窗口下,填写clipping = yes,并且添加滑轮时,会将整个窗口内所有元件一起移动,而不是只移动gridboxType所列举的项目。
为了解决这个问题,我们通常选择将gridboxType添加在子窗口中,令这个子窗口作为承接gridbox列举的“容器”,并且在子窗口添加滑轮,这样就可以使滑轮只生效于gridboxType的列表,而不会作用于整个界面了
还记得蓝色部分的滑轮和窗口边界吗

有关gridbox各项的解释:
由于被列举生成的窗口是后续关联的,我们将gridbox想象成一个很高很宽的大楼,被关联的窗口好比一个个的住户,两个max_slots分别决定大楼每层最多有几间房/最多盖多少层(横向/纵向最多可以列举几个窗口);
这个大楼户型都是相同的,因此每层楼的高(高度),每间房之间的距离(宽度)都是相等的,而slotsize就决定这个距离有多大
下面两张图将带来直观解释:


可以看见,尽管住户们(被列举的窗口)并没有那么大,但是你可以把整个房子(slotsize)设置的很大,让他们彼此距离远一点

上文提到,gridboxType需要以一个窗口作为生成模板,因此我们还需要设计这个被列举的窗口
注,这个被列举的窗口是一个独立的部分,直接写在guitypes下,不需要写在主窗口下
一个底板,一个国旗,一个国家名称,以及当被列举窗口所显示国家是主要国家时,加一个金色的边框突出显示


后端编写
先让我们从简单的开始入手,在外交界面附着一个阵营图标作为按钮:
强调一次,在selected_country_context中,THIS为被选中的国家,ROOT为操作界面的国家(详情看P3(一))

相比前端编写,gridboxType的后端就没有那么的“友善”了,
先前提到,gridboxType是列举生成,但是依据什么来生成呢?答案是数组(array)
gridbox是一个每间房规格相同的大楼,被列举的窗口是一个个房间中的住户,而数组就是区分不同住户的信息的名单
因此我们需要引入一种全新的后端写法:dynamic_lists

以下插入一段有关数组的介绍:
首先明确一点,数组是变量的集合
index,和value后定义两个临时变量(上文为idx和v),当访问数组内每个变量的时候,i会得到该变量在数组中所在次序(第一个为0,第二个为1……);v会得到该变量所拥有的值
通常而言,对于一般数组(内部变量均为普通变量),gridbox所列举生成窗口的数目,将与数组中所含变量数保持一致。而数组内部的每个变量(array^0,array^1……)也将作为每个列举生成窗口的“标签”,以方便后续为每个生成的窗口分配图像/点选效果。

但是游戏中有些变量本身有带有scope(作用域)的性质(国家Tag,人物character id等),而change_scope就是使得对列举生成的窗口进行编辑时,将作用域改为数组中变量的作用域。
听起来一头雾水对吧,当代码最复杂的东西和GUI最复杂的东西交织起来就是这样的

回到界面制作,先构思好最终希望实现的效果:点击不同阵营的按钮可以列举出不同阵营的所有国家
想要实现这个效果,需要在每次点击的时候,有一个数组(储存在当前tag),它将承接点击该国家时,该国家所在的阵营的全部国家,这样列举这个数组就可以呈现所有成员的信息
进一步思考,我们需要将游戏开始时的国家分类,依据阵营,将它们装进不同的数组(储存在global),点击时执行的效果即为访问不同阵营的数组,然后将它们加入到储存在当前TAG的被列举数组,这样就可以实现了

首先将一些国家添加到阵营中

然后执行第一个效果,将每个阵营分装成不同的数组,存储到global(全局),执行这些效果后将会产生三个数组,分别为各个阵营包含的国家
global.faction_menber_ENG,global.faction_menber_SOV,global.faction_menber_GER
其实我可以写的简单一点(比如分别建立三个数组再把成员加进去),但是因为你已经看到gridbox了,对数组的操作应该很熟悉了
(较为简单的写法放在了本篇附录)

第一阶段目标结束,我们得到了储存在global的三个不同数组,分别承接了三个不同阵营的所有国家

然后我们需要填写阵营按钮的点击效果(注,不是gridbox的窗口!是我们最开始建立的第一个!)


很简洁不是么
可能你会对一连串的ROOT和THIS弄得眼花缭乱了,如果感觉问题很大请复习P3(一)
(较为简单的写法请看附录)

最后完成gridbox窗口下与图像(国旗)/文本(国家名)分配
注意,此处为了进一步方便理解change_scope,我将以两种不同的写法,呈现填写change_scope = yes与否的区别(不填写默认为no)

change_scope = no(或者不填写)
设想gridbox是一个大楼,而大楼每一间房间,都有一个专属的门牌号,数组存储了所有的住户信息,住户可以是不同的人(不同的变量),也可以是相同的人(相同的变量),但是每间房对应的门牌号都是独一无二的,使用所列举数组的次序进行区分(array^i)
当change_scope = no时,我们只依据每间房的门牌号(array^i)而不是内部的实际的住户(变量)进行操作;如果有必要,也可以通过查询住户信息(array^v),将内部变量的值作为trigger或effect使用

现在我们需要对其中的住户(数组中的国家)进行查询,首先需要得知它是谁(Tag.GetFlag显示国旗);它是主要国家时,显示金色边框,于是有:

gridboxType实质是依据数组进行窗口的生成,因此对于被列举窗口中的元件,它们都关联了所列举数组中的每一条变量,对于第一个生成的窗口来说,i为0;v为数组中第一个tag(阵营领袖)
对于第二个生成的窗口来说,i为1,v为阵营中其它某个国家……以此类推
而只有这个国家(var:v)是主要国家时,才会显示主要国家金色边框
文本内容同理


最后让我们为这个窗口分配阵营名称,还记得点击按钮时设置的变量吗

ROOT.grid_faction_leader这个变量存储在操作界面的国家,THIS.faction_leader则是被点击国家的阵营领袖,于是阵营名称只需要访问ROOT.grid_faction_leader这个变量就可以了


change_scope = yes
这里介绍使用change_scope的写法,上文提到游戏中有些变量本身有带有scope(作用域)的性质,现在我们所使用的数组是由国家Tag组成的,而国家tag是一个变量,它也可以作为一个作用域使用
gridbox是一个大楼,每个住户有的不同信息(不同变量),而change_scope就是使得生成的每个窗口默认作用域变更为该变量的作用域

可以看见这两者的区别,由于每个窗口的作用域都变成该窗口的变量了,因此THIS就是当前生成窗口时所对应的Tag
第一个窗口,Tag为阵营领袖,THIS即为阵营领袖的Tag(ENG/SOV/GER)
第二个窗口,Tag为阵营内的某个国家,当它为主要国家时,显示主要国家的边框
……
文本内容同理



最后就是游戏测试


附录1:
较为简单易懂的代码写法(不建议,除非你实在难以理解上文使用的)
分装不同阵营的国家:
将ENG改写为SOV,GER写三次就可以了

按钮点击效果:
一样的,把ENG改写成SOV/GER写三次

不过这样的话你还需要改一下点击按钮时候的效果:

后话:为什么不建议使用简单的写法呢,因为gridbox另一个独有的特性在于它可以根据数组变化而变化,即如果数组中变量发生改变,gridbox依然可以依据改变后的数组生成不同的窗口,使用简单写法会扼杀掉查询变化的部分,令gridbox丧失了这一奇妙的属性(后续会专门介绍)
附录2:
在游戏中引用旗帜:
不同于其它的icon,引用旗帜不需要我们单独做一堆旗子的素材,我们可以通过游戏内写好的旗子,配合[Tag.GetFlag]进行使用
这是游戏中自带的所有规格的旗帜图像资源


在iconType中使用quadTextureSprite直接引用这些图像,然后在properties中使用[Tag.GetFlag]即可引用对应Tag的国旗了(也可以替换成scripted loc,然后把[Tag.GetFlag]写进localisationkey中)



gridbox第一篇告一段落,希望对你理解使用gridbox提供帮助