GameMaker Studio 2

创建于:2017-04-19

创建人: dougen

192 信息 1115 成员
游戏开发工具 GameMaker Studio 2 的讨论小组

论GameMakerStudio对象实例的执行顺序(二)

BloveStorm 2017-10-04

一、继承

严格来说这部分内容并不属于对象实例执行顺序范畴内,但由于在后面章节会涉及到此功能,所以我们先做个铺垫。

说到继承,就要提到父与子这两种角色。当一个对象有一个父对象时,它可以继承其所有事件与代码内容。而这个对象就是这个父的子。当然子也可以有自己的子,但一个子不能有多个父。所以继承关系我们可以想象为树状结构即可。

Image title

正常情况下,一个对象(子)继承另一个对象(父),而子为空时(无任何事件),子将继承父的所有事件以及其事件内的代码(除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;

 

至此,对象实例的执行顺序讨论完毕。由于对象实例的执行顺序远远比想象的复杂的多,所以这次讨论绝对不可能涵盖所有可能性。要完善这部分内容,还需要大家多多提出建议。如果可能,我们会再做出一次专题性讨论。

(转发自:原日志地址)
 

加入 indienova

  • 建立个人/工作室档案
  • 建立开发中的游戏档案
  • 关注个人/工作室动态
  • 寻找合作伙伴共同开发
  • 寻求线上发行
  • 更多服务……
登录/注册