GameMaker8.0 新手教程 Part 7 -对象与代码(三)-

分类栏目:gamemaker教程

769

GameMaker8.0 新手教程 Part 7 -对象与代码(三)-

4、对象之间的交流:with语句

如果我们不只是想让对象/实例之间进行数据的交流,而是想让某个对象/实例命令另一个对象/实例执行一个函数或者一段代码呢?

with(obj/ID)
{
xxxx;
xxxxx;
}

作用是让括号里的对象/实例执行花括号里的代码。和if一样,如果只有一个语句可以不用花括号(见第五章),形如with(obj/ID)xxxxx;。

像if,while,for,with等这类在花括号里的语句只有一句时,可以不写花括号。但是究竟是写在同一行里:

if(xxx)xxxx;

还是分开两行写:

if(xxx)
xxxx;

一般而言整个语句较短就写同一行,如:

if(x == y)z = x;

比较长就分开写,如:

if(objBlock.x < view_xview || objBlock.y < view_yview || move == 0)
instance_create(view_xview + 100, view_yview + 100, objBlockSlip);

原则上怎么写更方便阅读就怎么写。
for()语句通常不会写在同一行(因为for(xx1;xx2;xx3)本身就包含了三个语句,比较长)。

注意:
若在objA里写objB.xx = yy;,这个yy是objA的yy;
若在objA里写with(objB)xx = yy;,这个yy则是objB的yy,等效于objB.xx = objB.yy;。
如果要在with(objB){}里使用objA的值,得用objA.yy来调用。

但是有时候事情并没有这么简单。
举个例子,屏幕里面有很多个objA的实例,每一个实例的yy的值都不一样。这时候其中一个objA的实例创建了一个objB的实例(inst = instance_create(x,y,objB);,用变量ins储存了这个objB的实例的索引),并且想在with(inst){}里面使用自己的yy,要怎么做?
GML中提供了关键字other来处理这个问题。只要在with里写other.yy:

with(inst)
{
xx = other.yy
}

这里的other.yy就会准确无误地调用创建了objB的那个objA的实例的yy。

提示1:with和成员运算符可以混着用。
例如:

inst = instance_create(此处省略...);
inst.speed = 6;
with(inst)
{
xx = 15;
inst = instace_create(此处省略);//此处的inst和上面的inst不是同一个
}
inst.yy = 6;


提示2:instance_create函数的返回值可以作为成员运算符的前缀,那么也自然可以直接作为with的参数:

with(instance_create(x, y, obj))
{
xxx;
}


提示3:以对象作为成员运算符的前缀时,若该对象的实例数量大于1,则严禁自增自减,即严禁出现下面的情况:

objFruit.x = objFruit.x + 12;
objFruit.x += 12;

一定要写成with结构:

with(objFruit)x += 12;

在GML教程第15页有介绍,本质原因是:
GM中以对象作为成员运算符的前缀来调用它的某个变量时,一律调用该对象ID最小的那个实例的变量。
例如,objFruit有三个实例,ID分别是100315,100368,100396,对应的x分别是100,200,300,
在执行了objFruit.x = objFruit.x + 12;之后
那么三个objFruit都会跳到x为112的位置,因为ID100315最小,所以以它的x值100作为objFruit.x的值被调用。
objFruit.x += 12;则仅仅只是objFruit.x = objFruit.x + 12;的缩写,并不会导致结果不同。

5、对象之间的交流:全局变量

全局(global),即指整个游戏,或者说整个程序。
我们在对象中使用的变量叫做局部变量(local variables),因为局部变量是属于单个实例的,一旦销毁了一个实例(instance_destroy()、房间结束、游戏结束等),这个实例的所有变量也会销毁。当从一个房间跳到另一个房间时(room_goto(房间名);),或者用room_restart();重新开始这个房间,原本的房间就会结束,它的所有实例都会被销毁(除了勾选“保持”的对象的实例。参考第三章)。

全局变量(global variables),不属于某一个实例,而是属于整个游戏的,不会因为实例的销毁或者房间的结束而销毁。基于这个特性,全局变量可以用于对象/实例之间的数据交流,尤其是跨房间的数据交流。
①global
以global.xx的形式的变量即是全局变量,小数点是成员运算符,表示这个变量是属于global的。
使用方法和正常的变量几乎没有区别,任何一个对象/实例可以访问/改变改变全局变量。
global也相当于是一个特殊的实例,它的索引是-5。
即你可以使用(-5).xxx来调用全局变量。但是with(global)或者with(-5)是不行的。
②globalvar
此方法为全局变量的声明式写法。如果无法理解,请跳过,②和①并没有本质上的区别。
什么叫做“声明”?
即通过形如globalvar 变量名1,变量名2,变量名3;这种形式,在一个变量被使用之前,先把变量名写在globalvar的后面陈述一遍。
globalvar声明全局变量的特点是:变量只需要声明一次,一旦声明不需要前缀即可调用。
即,在游戏开始时某个地方写了globalvar tempx;之后,就可以在任意地方像

x = tempx;
tempx = 3;

这样,不写global.这个前缀就可以使用tempx这个全局变量。
由于不需要写global.作为前缀,globalvar声明的全局变量在使用时就和局部变量是一样的写法,有时候很难分别出哪个是全局变量,哪个是局部变量,所以规范的写法是,把用globalver方法声明的变量全写大写字母。
例如上面的例子就应该把变量tempx写成TEMPX,这样一看就能看出来是全局变量还是局部变量。
在声明过globalvar val;之后在对象/实例里使用val都是使用的全局变量,但是如果要想再定义一个对象的局部变量val怎么办呢?利用GM自带的关键字self来表示局部变量。
一般情况下,局部变量val,直接写val和写作self.val是等效的,所以一般不写"slef."。但是在有globalvar声明了val的情况下,就必须要用self.val来表示局部变量。
但是为了避免不必要的bug,不应当出现变量名字冲突的情况。
注意:global.和globalvar是互通的!
即:

globalvar NEXT;
NEXT = 3;
global.NEXT = 5;
show_message(string(NEXT) + " " + string(global.NEXT));

输出:5 5。即NEXT和global.NEXT是同一个变量。

③临时全局变量:var
这个var正是第六章的"脚本"一节的var。
var也是变量的声明式写法,和globalvar一样是以
var 变量名1,变量名2;
的方法声明变量的。
声明之后,就可以直接用变量名1和变量名2,而不用加什么前缀。
使用var声明的变量有两个特点:
一是全局性,即声明变量之后所有实例/对象都可以使用它(们),不需要任何前缀,和globalvar一样。
二是临时性,即声明的变量并不是一直存在,而是只有很短的生存期间。

var的生存期间有多短?就是从被声明开始到该代码段结束为止。
说直白点,就是一个代码框的代码执行完毕,就销毁其中的var变量。

var变量生存期间那么短,如何体现它的全局性?
一般还是在with语句体现地比较多:

var count;
for(count = 0;count < 360;count += 20)
with(instance_create(200,200,objBullet))
{
speed = 6;
direction = count; //速度的方向,即运动方向,0为右,90为上,180为左,270为下
}

这个代码可以自己去执行一下试试,是一个由objBullet组成的扩散圆。
在这里由于count只用一次,不需要一直留着,为了节省内存就声明为var变量,在这段代码结束后就会自动销毁count。
新建的objBullet需要count的值作为自己的运动方向,却不是"other.count"而是"count",体现出var的全局性。