论GameMakerStudio对象实例的执行顺序(二)
一、继承
严格来说这部分内容并不属于对象实例执行顺序范畴内,但由于在后面章节会涉及到此功能,所以我们先做个铺垫。
说到继承,就要提到父与子这两种角色。当一个对象有一个父对象时,它可以继承其所有事件与代码内容。而这个对象就是这个父的子。当然子也可以有自己的子,但一个子不能有多个父。所以继承关系我们可以想象为树状结构即可。
正常情况下,一个对象(子)继承另一个对象(父),而子为空时(无任何事件),子将继承父的所有事件以及其事件内的代码(除sprite、visible、solid、persistent、physics、depth等)。
子可以设置父所没有的事件,相当于青出于蓝而胜于蓝。而当子创建了一个其父所拥有的事件时,子将不继承父在本事件里的代码。除非子在本事件的代码里写上event_inherited()函数,他将继承父的在本事件的所有内容。
关于继承的功能,这里举个简单的例子。
假设有对象obj_A、obj_B、obj_C三个对象。obj_B与obj_C都继承obj_A。
obj_A对象的代码如下:
Create事件:
n = 10;
Step事件:
n += 5;
obj_B对象的代码如下:
End Step事件:
n += 3;
obj_C对象的代码如下:
Create事件:
n = 5; //不继承obj_A在create事件里的代码。
Step事件:
event_inherited();//obj_A在step事件里的代码n += 5;
n += 2;
结果:
在本帧结束时,obj_A的n从10(create事件)到15(step事件)。
obj_B的n从10(继承obj_A的create事件)到15(继承obj_A的step事件),再到18(end step事件)。
obj_C的n从5(create事件的代码。它没有继承obj_A在create事件里的代码)到10(继承obj_A的step事件里的代码),同时再到12(step事件的代码)。
二、with函数
with函数的参数可以为对象(可为父),也可以为某特定实例id。其功能是在指定对象的所有实例或某一特定实例,即刻执行with里面的代码(对每个实例内生效)。
功能看似简单,但实际上可能会很复杂。我们来分析一位网友的with问题。
假设只有一个对象obj_A。我们事先在房间编辑器创建obj_A的三个实例a1、a2与a3。Id从小到大(即为执行顺序):a1 -> a2 -> a3。
obj_A对象的代码如下:
Create事件:
time = 0;
n = 0;
Step事件:
with obj_A
{
if keyboard_check_pressed(vk_space) && time = 0
time += 1;
}
if time = 1
{
n -= 5;
time = 0;
}
结果:
a1的n为-10;a2的n为-10;但a3的n为-5。
问题分析:
我们按照实例的执行顺序,来详细分析下。
从vk_space键按下的那一帧开始算起。由于vk_space键只按一下,所以第二帧不会执行keyboard_check_pressed(vk_space)。
第一帧a1的step事件:
a1的with代码,导致a1,a2,a3的time都为1。
a1执行with下面代码后,此时a1的time为0,n为-5。
第一帧a2的step事件:
a2的with代码,导致a1的time为1(此时a2,a3的time也为1)。
a2执行with下面代码后,此时a2的time为0,n为-5。
第一帧a3的step事件:
a3的with代码,导致a2的time为1。
注意,a1并不执行其with下面代码!原因是a1的time为1。
a3执行with下面代码后,a2的time为0,n为-5。
至此第一帧结束。此时a1的time为1,n为-5;a2的time为1,n为-5;a3的time为0,n为-5。
第二帧a1的step事件:
由于a1的time为1,所以执行了n -= 5;
a1最终time为0,n为-10。
第二帧a2的step事件:
由于a2的time为1,所以执行了n -= 5;
a2最终time为0,n为-10。
第二帧a3的step事件:
由于a3的time为0,所以什么都不执行;
a3最终time为0,n为-5。
以上结果是两帧后的结果。也就是说,当你在一帧里按下虚拟按键vk_space后,通过两帧才执行完成。
至此,网友的问题分析结束。当然,我们不推荐用with来玩嵌套。但如果你能分析清楚这个问题,那么with应该难不倒你了。
关于with还需要注意的是,如果with后面为父对象,那么父与其子都将执行with里面的代码。
举个简单例子。
假设有对象obj_A、obj_B、obj_C、obj_D四个对象。obj_B与obj_C都继承obj_A。
obj_D对象的代码如下:
Step事件:
with obj_A
{
n += 10;
}
结果:
所有obj_A、obj_B与obj_C的实例中,变量n都将加10。
如果我们只想让obj_A的实例中变量n增加10,可以将代码写成:
with obj_A
{
If object_index = obj_A
{
n += 10;
}
}
三、碰撞函数与事件
本质上碰撞事件与其他非draw事件在执行规则方面是相同的。但由于碰撞事件与碰撞函数的区别,以及碰撞多个实例的执行顺序问题,这部分内容我们还得拿出来单独讨论。
其实碰撞事件与碰撞函数还是有很大区别的。
1、碰撞函数以instance_place为例(与碰撞事件最为相像),在碰撞代码处只执行一次。也就是说,当本对象实例碰撞多个目标对象的实例时,在一帧里碰撞函数只会检测其中的一个实例(根据实例执行顺序决定)。并且根据实例的执行顺序逻辑,每帧都只会碰撞检测这个实例,除非改变被碰撞检测对象实例的执行顺序。
我们假设个场景。如果在本帧开始时,执行碰撞函数的实例,同时与多个被检测对象的实例碰撞时,碰撞函数只会检测id最小的(或在房间编辑器里Instance Order修改的实例顺序优先的)实例(与实例所属对象的object_index无关!)。然后再执行碰撞函数下面的其他代码。
举个简单例子。
假设有对象obj_A、obj_B两个对象。对象obj_A有一个实例id:100000;对象obj_B有两个实例:id:100001与id:100002。
obj_A对象的代码如下:
Create事件:
n = 0;
Step事件:
var temp;
temp = instance_place(x,y,obj_B); //只检测id小者
if temp
n = temp.n;
obj_B对象的代码如下:
Create事件:
n = id;
结果:
当实例id:100000(obj_A)同时碰撞id:100001(obj_B)与id:100002(obj_B)两个实例时,id:100000的n为100001。
2、而对于执行碰撞事件的实例,同时与多个被检测对象的实例碰撞时,碰撞事件将执行多次。其次数是被检测对象与之碰撞实例个数的总和。例如,当一个执行碰撞事件的实例,同时与被检测对象5个实例同时碰撞时,碰撞事件将执行5次。检测被撞对象实例的执行顺序,与对象实例执行顺序规则相同(遵循对象object_index与id规则)。
举个简单例子。
假设有对象obj_A、obj_B两个对象。对象obj_A有一个实例id:100000;对象obj_B有两个实例:id:100001与id:100002。
obj_A对象的代码如下:
Create事件:
n = 0;
与obj_B的collision事件:
n = other.n; //执行两次
obj_B对象的代码如下:
Create事件:
n = id;
结果:
当实例id:100000(obj_A)同时碰撞id:100001(obj_B)与id:100002(obj_B)两个实例时,id:100000的n先为100001,然后变为100002。
四、speed相关问题
原本这部分内容应该放到【论GameMakerStudio事件的执行顺序】来讨论。但由于需要理解两个实例的执行顺序,所以我认为放到里较为合适。
首先我们举例来了解与分析问题。
假设有对象obj_A、obj_B两个对象。每个对象各有一个实例。这两个实例的精灵图片为64x64的正方形图片(中心点居中)。我们在房间编辑器里预先创建两个实例,并且让两个实例左右(obj_A在左,obj_B在右)无缝连接起来(两者x坐标差值为64)。
obj_A对象的代码如下:
Step事件:
x = obj_B.x-64;
obj_B对象的代码如下:
Create事件:
hspeed = 10;
结果:
游戏在执行过程中,你会发现obj_A与obj_B的两个实例间有个10像素的缝隙。
分析:
其实原因是在于speed属性和step事件的特点。当对象以speed属性(包括hspeed和vspeed属性)进行移动时,只有在step事件结束时(所有实例都执行完step事件)才会改变其实例的坐标。上面例子中object_B的实例,只有当所有实例执行完step事件后,才会执行x += 10(hspeed = 10的结果)。
所以在object_A的step实例里的x = obj_B.x-64代码中的obj_B.x,其实并没有执行hspeed = 10的结果。也可以理解为object_A一直在跟随object_B上一步的坐标。
要想避免这个情况发生,我们可以将object_A中step事件变成end step事件。
obj_A对象的代码如下:
End step事件:
x = obj_B.x-64;
或者我们干脆删除object_A中所有事件,object_B中不应用hspeed属性,在object_B的step事件里用坐标移动自己与跟随者。
obj_B对象的代码如下:
Step事件:
x += 10;
obj_A.x = x;
至此,对象实例的执行顺序讨论完毕。由于对象实例的执行顺序远远比想象的复杂的多,所以这次讨论绝对不可能涵盖所有可能性。要完善这部分内容,还需要大家多多提出建议。如果可能,我们会再做出一次专题性讨论。