×

GM教程 Gamemaker 小玉米的图文教程 玉米原创

小玉米图文教程No.2 - 关键字var的大用处

Mage松仁玉米 Mage松仁玉米 发表于2015-03-03 23:39:41 浏览6286 评论4

4人参与发表评论

近来偷摸的观察这几个GM群的东西,绝大多数同学认为,var这个东西,就是局部变量,加不加影响不大,跟直接定义变量定义值是一个效果的,比如我举个例子。

fuck = "franniss";
var fuck;
fuck = "franniss";

某些人会觉得,这两个代码的效果完全是一样的,var的那一行,加不加都可以。


你要这么认为你就错了

我们不妨来这么试一下。
启动GM8.0,GMS也行= = 。。。 为了保证大家在实验的时候与本教程完全同步,推荐大家使用论坛的超强汉化破解版GM8.0
新建一个工程,创建物体和房间,并把这个物体放入到房间里。

这里我给object0放了个精灵,目的是让大家知道已经把这货添加进了房间里(我这么做貌似没啥必要,防误解还是扔进去吧。)
0.jpg

之后,就要往物体里面写代码了,在object0的创建事件中,写下如下代码

fuck = "franniss";

这是在物体上简单的定义了一个变量
接下来,在物体的空格键按下事件中,写下如下代码。

show_message(fuck);

效果可想而知,在敲击空格键的时候,屏幕上会弹出一个消息提示框,那我我们来试一下。
敲击空格键,弹出消息框:
1.jpg

一切看上去很正常,嗯。接下来,稍微改动一下代码,将创建事件中的代码改写成如下代码。

var fuck;
fuck = "franniss";

这段代码,就用到了var关键字,定义了一个临时变量。
运行游戏,敲击空格键。
2.jpg

这时我们就发现,在敲完空格键之后,并没有弹出信息框,而是报了错。错误的原因是一个未知的变量fuck
我们已经在创建事件中定义了fuck变量,为什么在按下空格事件中,这个变量不起作用呢?
实验证明,在创建事件中,已经成功的定义出了fuck变量,但是在敲击空格键的时候,这个变量就不存在了,所以,我们得出以下结论:

      使用var关键字定义过的变量,只影响于当前事件,并不作用于整个实例,当事件结束的时候,这个变量会自动的从内存中删除掉。

这个结果只是当前得出的,并不是最标准的,下面,将在这个实验的基础上增加更多的动作来得到更准确的定义。

删除掉刚刚物体中的“按下空格”事件,并对创建事件进行改动。向创建事件中拖入两个代码按钮,如下图所示。
3.jpg

在第一个代码块中,写下如下代码:

var sb;
sb = "franniss";

在第二个代码块中,写下如下代码:

show_message(sb);

接下来,运行游戏。查看效果
4.jpg

根据运行结果我们会发现,程序依然报错,那么问题来了,我的var定义的变量明明和show_message时的变量在同一个事件里面,为什么还会报错。
这时我们再对程序进行修改,删除创建事件的一个代码按钮,留下一个,并改写成如下代码:

var sb;
sb = "franniss";
show_message(sb);

运行后你会发现,程序正常运行,弹出了灰色的信息提示框。
实验证明,在创建事件中,已经成功的定义出了sb变量,但是想让这个变量有效,所有使用到这个变量的语句都要和该变量的var定义处在同一代码段内,因此,我们得出以下结论:

使用var关键字定义过的变量,只影响于当前代码段,并不作用于整个实例,当该代码段结束的时候,这个变量会自动的从内存中删除掉。

我们大家所说的临时变量,其实就是就是以前直接定义的一些变量,并不加var和global关键词的修饰,其实这种说法是不对的,我们可以认为,在GM中存在于四种类型的变量:
        

1.使用关键词globalvar定义的全局变量
2.不使用任何关键词所定义在实例上的成员变量
3.在变量名前加上global.特殊的,定义在global上的成员变量
4.使用关键词var定义的,只作用于当前代码段上临时变量

所谓的成员变量,只得就是该变量是影响整个实例的,在变量名使用global.这个比较特殊,可以称之其为成员变量,但是我认为,在这里为了让大家不要和globalvar发生误解,还是将它当作定义在global实例上的成员变量比较好,而且这个实例会全程存在于游戏中,它是一个比较特殊的实例。

需要注意的是,使用globalvar定义的全局变量,在使用global.进行访问的时候,也可以访问到正确的值,例如:

globalvar fuck;
fuck = "franniss";

上面的这段代码,使用了globalvar进行声明全局变量fuck,那么在之后的代码中,使用fuck直接访问和使用global.fuck来访问都可以得到"franniss"这一结果。当然,使用object0.fuck或是(实例ID).fuck,获取的仍然是作用在实例上的成员变量fuck,而不是全局变量fuck。


既然我们已经得出了var的使用定义,那么这里就说一些关于var妙用。var是一个非常好用的东西,它会让你的代码逻辑更加清晰,而且在代码执行完毕后会马上释放掉内存,且不会与其他的成员变量冲突。因为它只作用于代码段上,所以要比全局变量和成员变量的运行效率快很多。
1.在脚本中使用var:
有的时候,为了实现一些必要的功能,而且这个功能我们会反复的用到,而并不想重复的写代码,我们就会使用到脚本,在脚本中使用var会让脚本内所用到的变量不与调用脚本的实例中的成员变量发生冲突,下面就来举个例子:
创建一个脚本,命名为draw_rectangle_red,用来实现绘制一个红色的矩形,在脚本中写入如下代码:

var x1,x2,y1,y2,color;
x1 = argument0;
y1 = argument1;
x2 = argument2;
y2 = argument3;
color = draw_get_color();
draw_set_color(c_red);
draw_rectangle(x1,y1,x2,y2,false);
draw_set_color(color);

这样,在用var定义好局部变量后,可以使x1,x2,y1,y2,color这些变量不和调用实例的成员变量发生冲突,如果说实例中存在一个color成员变量,如果这个脚本并没有使用var来定义color,则该实例在运行完此脚本后,成员变量color就很有可能被改变,游戏就容易引发BUG。而且如果在脚本中如果使用了一个实例中没有的变量名称,在运行完该脚本后,变量就会残留在当前的实例中,这样就会导致游戏占据很大的内存,在优化方面,一个小小的细节也不要放过,哪怕这个变量只是一个占8字节的double型变量。

2.*在with结构中使用var:
我们已经得出了结论,var所定义的变量只是作用于代码,在with结构中,with后面的东西很容易导致你的思路混乱,让你误认为一个实例上的变量是另一个实例上的,这就造成了游戏的BUG,而且对于某些入门者来说,这都是不容易察觉的出来的问题,这里如果使用了var,思路就会变得相当清晰,而且也不会引发BUG,下面我就做出一个例子:
在这里我先声明一下,有两个实例,分别为obj1和obj2,在obj1上存在一个数据结构列表,这个列表的索引值被存放在该实例的list成员变量中。而obj2没有list这个变量。我希望敲击空格键的时候,创建一个obj2物体,将新创建的obj2实例的id值添加到列表内。
有些人在写的时候可能会这么写:

ins = instance_create(0,0,obj2);
with(ins) {
    ds_list_add(list,id);
}

逻辑上,这种写法是正确的,但是在with结构代码块中,里面的所有变量都是要在被with的实例中应当存在的,这里在运行之后就会报错,错误为“不存在list变量”。
这里我们可以使用var定义一个临时变量,来实现跨实例变量传递,这个听起来比较神,但是确实就这么神,下面我就给出这样的一段代码:

var L;
L = list
ins = instance_create(0,0,obj2);
with(ins) {
    ds_list_add(L,id);
}

这次再运行,游戏就不会报错了,而且会成功的将obj2的id添加到列表中,因为使用var定义的变量,只作用于代码,与实例毫无关系。所以说,这种语句是正确的。
有的孩子特么的抠字眼,就说了,其实我有种写法更好,就像下面这样:

ins = instance_create(0,0,obj2);
ds_list_add(list,ins);

哥,别在意细节= = 我只不过是演示一下var在with结构中的妙用= =在你处理的数据足够多的时候,你会发现这么用要方便的多。
总而言之,var是个不错的东西,灵活的去运用一定会帮助你让你的游戏更加高端。让你的代码思路和逻辑更加清晰。

别着急,还没完,下面说两个关于var的使用注意事项:
1.在使用var定义变量的时候,千万别忘了在定义完成之后加上半角的分号,否则程序会出错的。
GML语言在语句结束的分号上没有要求的这么严厉,在语句末尾不加分号也会正常运行,但是用var和globalvar定义变量的时候一定要记得加分号,否则会引发程序出错。有的时候,还是严厉点比较好呢。
2.如果我的实例上已经有了一个跟var定义的重名的变量,那我要怎么在写代码的时候区分这两个变量?
这个我不多说,依然举出一个代码:

str = "test";
var str;
str = "123";

这里,就定义了一个成员变量str,和一个临时变量str,想要在接下来的代码中区分这两个变量很容易,只要用self就可以了。

show_message(str + self.str);

运行一下看看结果。
5.jpg

这个我想应该是重要的一个问题,因为变量名冲突什么的确实会引发不少的BUG,其实有一个更好的方案就是在编写程序的时候尽量避免变量重名,这样就把发生BUG的概率降了很多。可见,有一个良好的代码编写习惯也是尤为重要的。

以上,就是本次教程的所有内容,特么的写了这么长时间累死哥了。
最后我来说几句,有些朋友很关心大苞米视频教程的更新,在QQ上私聊我来问我视频什么时候更新呀?很想继续学习什么的,看看也是,很长时间一直都在断更的状态,其实自己懒是一方面原因,而且在上了大学以后,做语音教程什么的也不是很方便,毕竟有室友在搞基。所以就先拿图文教程来说事吧,虽说操作上没有视频直观,不过也能把思路讲清晰也是很好的事情,在此感谢大家对玉米的支持。


↓↓支持小站发展,点一点小广告,么么哒(づ ̄ 3 ̄)づ↓↓

BB点儿啥

访客
傲娇玉米站网友 傲娇玉米站网友2016-09-05 23:04:10 · 回复 不错,学到东西了
晒着太阳数星星 晒着太阳数星星2017-07-13 14:07:23 · 回复 毕竟有室友在搞基……
光仔 光仔2018-06-17 11:09:30 · 回复 感谢玉米,学到了新姿势
awa awa2020-03-09 11:34:49 · 回复 66666