数组
所谓数组(Array),是有序的元素序列。说的通俗一点,“数组”和高中数学的“数列”基本是一回事。一个数组内包含多个元素(GML 内指实数和字符串,GML 外还有更多类型),且这些元素是有排列顺序的。
来回忆一下高中数学的数列:
一个数列
{Xn}
,它所含的不同元素用下标来区分,即以X1
,X2
,X3
来表示数列{Xn}
的第一项,第二项,第三项元素。在解题时,我们可能不会直接使用数字下标,而是形如:X(m+2) = 2Xm
,通过未知数m
来表示下标,以便于求解m
。如果我们解出m = 6
,那么很显然,在这里X8 = 2X6
,即:X(m+2)
和X8
都表示数列{Xn}
的第八项元素。
数组也是相同的道理。
数组元素的表达形式是:数组名[表达式]
。即,若存在一个数组 num
,那么它的元素用 num[5]
,num[12]
这样来表示。因此,[]
被称为下标运算符。
注意:数组的第一个元素是 num[0] 不是 num[1]。不仅是数组,几乎所有的索引都是从 0 开始,而不是从 1 开始。从零开始的索引是几乎所有语言的通性,习惯这一点对你学习更高等的语言有很大的帮助。
与数列相同的,数组元素的下标也可以用未知数和表达式表示。如:
num[8 - 3 * 2]
和 num[2]
是同一个元素,若 m
为 6,则 num[m - 3]
和 num[3]
是同一个元素。
也可以将返回值是整数的函数作为下标:
num[irandom(8)]
会随机挑选 num[0]
到 num[8]
之间的某个元素。
数组元素的一般性
数组元素在代码中的地位与普通变量完全相同。即,普通变量能做的,数组元素都能做。
赋值
1
2
3
num[0] = 5;
num[1] = num[0] + 2;
num[2] = num[1] * num[0];
运算
1
2
3
a = 3;
num[0] = irandom(6);
b = a + num[0] * 6;
逻辑判断
1
2
3
4
num[0] = 12;
num[1] = 14;
if (num[0] > num[1] && num[0] > 0)
num[0] -= num[1];
作为函数参数
1
2
3
4
xx[0] = 100;
xx[1] = 200;
instance_create(xx[0], 100, objBullet);
instance_create(xx[1], 100, objBullet);
作为脚本的返回值
1
2
3
4
5
6
7
8
// 这是一个脚本
{
var _array; // 声明临时的数组,只需要声明数组名
_array[0] = argument0;
_array[1] = argument1;
_array[2] = _array[0] + _array[1];
return _array[2];
}
作为全局数组
1
2
3
4
global.BOSS[0] = 1;
globalvar BOSS; // 同样只需要声明数组名
BOSS[0] = 0; // global.BOSS[0] 和 BOSS[0] 是同一个数组元素
show_message(string(global.BOSS[0])); // 此时输出 0 而不是 1
数组元素的特殊性
说到数组元素的特殊性,毫无疑问就是数组的下标可以用表达式表示。这个特性可以方便我们做很多事。例如:
1
2
for (i = 0; i < 10; i += 1)
inst[i] = instance_create(400, 400, objBullet);
根据 for 语句的效果,将在房间中创建 10 个 objBullet 的实例,并且这十个实例的索引(ID)分别被储存在 inst[0]
到 inst[9]
中,这样就可以分别控制十个实例。例如:
1
2
for (j = 0; j < 3; j += 1)
inst[j + 2].speed = 6;
这样,inst[2]
,inst[3]
,inst[4]
所表示的三个实例,就会以速度 6 开始运动。
二维数组
所谓二维数组,就是通过两个值来确定一个数据,比如 a[5, 3]
,在方括号内用逗号隔开。二维数组可以当做是一个书架,a[5, 6]
指第五行,第六列的书(数据)。二维数组的使用等同于一维数组,但是使用二维数组能更好的控制平面的内容。比如在房间内均匀创建 6x6 一共 36 个 objBullet,那么就可以用二维数组来分别储存控制这 36 个实例:
1
2
3
for (i = 0; i < 6; i += 1)
for (j = 0; j < 6; j += 1)
inst[i, j] = instance_create(i * 100, j * 100, objBullet);
注意 inst 数组的取值范围是 inst[0~5, 0~5]
。
GM 数组的特殊性
在 GM8 中,务必记住一个特点:数组名等效于零号元素。
即,对于任意的一个变量名而言,a
和 a[0]
代表同一个变量。即使 a
本身并不是一个数组,你仍然可以用 a[0]
来表示它。不仅是自定义的变量,GM8 内置变量也是如此,如 view_xview
和 view_xview[0]
完全等效(该变量参见视野)。因此,GM8 的脚本并不能传递数组,即使将数组名作为参数或者返回值,实际上只是传递了零号元素的值罢了。如果想要通过脚本的参数或者返回值传递一列数值,请参考数据结构。
而在 GMS2 中,存在“重定义”(或覆盖定义)的概念,即在已经定义 a 的情况下,定义 a[]
会重定义 a
为数组,那么原本变量 a
会被删除,之后 a
就只能作为数组名来使用了,反过来说,定义 a
也会覆盖定义 a[]
。在 GMS2 中脚本可以传递数组,但是必须在脚本中使用访问器来访问,例如:
1
2
3
4
5
{
var a = argument0; // 假设 argument0 传递的是一个数组
a[@ 0] = 5;
a[@ 1] = 6;
}
如上所示,[@ x]
就是数组的访问器了。由于这里是 GM8 的教程,所以不再详讲 GMS2 的访问器。
注意,这仅仅只是 GM 数组的特殊性,在绝大部分其他编程语言中并非如此。