HTML5引擎Construct2技术剖析(五)

来源:互联网 发布:memcmp linux 编辑:程序博客网 时间:2024/06/04 20:21

当完成初始化工作后,游戏就开始运行了。下面几节来介绍游戏的运行过程。游戏运行过程主要分为几个部分:

(1)资源加载

前一节介绍过初始化工作的最后就是调用go函数,go函数的工作就是加载资源,显示加载进度,等待资源加载完成后正式进入游戏界面。如果采用WebGL,则会新创建一个overlay_canvas来显示加载进度。

加载界面分为3种样式:显示加载带Logo图片进度条、显示进度条和显示加载进度百分比文字。

加载界面也支持Layout来实现更为复杂、效果更炫的加载背景。如果加载界面使用Layout,,则go函数中加载的资源仅仅是那个Layout所使用的资源,而游戏的其他资源会在加载Layout界面运行时才开始加载;否则go函数中会加载所有游戏使用的资源。

加载资源主要包括音频和纹理,纹理列表在前面构造基于Sprite插件的对象类型ObjectType时,会调用OnCreate函数。这个函数中会找到当前精灵对象使用的所有纹理文件,通过waitForImageLoad函数将其加入到runtime的wait_for_textures数组中,等待文件的后台自动加载。音频列表则调用getready函数,通过声音插件Audio的setPreloadList函数找出所有预加载的音频文件列表放入runtime的audio_to_preload数组中,等待文件的后台自动加载。
go函数通过SetTimeout不断回调自己,每次调用都会通过areAllTexturesAndSoundsLoaded函数检查音频和纹理资源是否加载完成,如果完成则执行go_loading_finished函数正式进入游戏状态。

setTimeout((function (self) { return function () { self.go(); }; })(this), (this.isCocoonJs ? 10 : 100));  

(2) 进入游戏

游戏资源加载完毕后,正式进入游戏流程,这是由go_loading_finished函数来完成,其主要流程:
1) 关闭加载界面。如果是WebGL,释放overlay canvas资源。
2) 如果设置使用Layout加载界面,则开始准备游戏资源列表(因为前面的资源加载阶段仅加载了Layout加载界面的资源),后台自动加载。
3) 遍历所有界面,创建其中包含的全局对象实例(但不在游戏世界)

    for (i = 0, len = this.layouts_by_index.length; i < len; i++)    {        this.layouts_by_index[i].createGlobalNonWorlds();    }    if (this.first_layout)          this.layouts[this.first_layout].startRunning();    else        this.layouts_by_index[0].startRunning();

Layout对象的startRunning函数主要流程:
(a) 如果Layout有EventSheet,则调用updateDeepIncludes函数更新其中包含的EventSheet数据。

     if (this.sheetname) {        this.event_sheet = this.runtime.eventsheets[this.sheetname];        this.event_sheet.updateDeepIncludes();  

}
(b) 遍历在数据解析阶段创建的对象实例,根据对象实例的layer属性,将对象实例放入相应图层的instances数组中。并将每个图层的对象实例按深度进行排序。

        for (i = 0, len = this.layers.length; i < len; ++i)        {              this.layers[i].instances.sort(sort_by_zindex);        }

(c) 为所有图层创建本图层的初始对象实例,并初始化图层的视口。updateViewport函数仅完成当前图层的旋转设置,获得旋转后的视口坐标。

    for (i = 0, len = this.layers.length; i < len; i++)    {        layer = this.layers[i];        layer.createInitialInstances();         layer.updateViewport(null);    }

createInitialInstances函数根据initial_instances中的对象实例数据(实例数据才前面解析时仅简单拷贝,没有进一步处理)创建实例对象,

    for (i = 0, k = 0, len = this.initial_instances.length; i < len; i++)    {        initial_inst = this.initial_instances[i];        type = this.runtime.types_by_index[initial_inst[1]]; …

如果对象实例具有Persist(持久化)行为,即实例的状态数据能够跨场景,当切换场景时则该实例不会被删除;如果不具备Persist行为,则会在图层初始时重新创建实例对象。layout.first_visit表示第一次进入该场景。inst.type.global为真,表示该实例为全局对象,不会在退出Layout时被删除回收。如果keep为真,说明该实例不是全局实例,将其加入到initial_instances数组中。

hasPersistBehavior = this.runtime.typeHasPersistBehavior(type);            keep = true;            if (!hasPersistBehavior || this.layout.first_visit)            {                inst = this.runtime.createInstanceFromInit(initial_inst, this, true);                created_instances.push(inst);                if (inst.type.global)                    keep = false;            }

前面如果调用了runtime.createInstanceFromInit 函数,该函数会将新创建的实例放入runtime.createRow数组中,这里新建的对象实例还没有真正加入实例管理列中。ClearDeathRow函数会将createRow中实例加入到管理列表中,并更新实例的IID。

this.runtime.ClearDeathRow();}

(d) 检查所有新创建的对象实例,如果该实例的类型属于某个容器,则创建该容器中的其他类型的实例(即兄弟实例),放到siblings数组中。

for (i = 0; i < created_instances.length; i++)        {            inst = created_instances[i];            if (!inst.type.is_contained)                continue;            …            for (k = 0, lenk = inst.type.container.length; k < lenk; k++)            {                …                s = this.runtime.createInstanceFromInit(t.default_instance, inst.layer, true, inst.x, inst.y, true);                this.runtime.ClearDeathRow();                t.updateIIDs();                inst.siblings.push(s);                created_instances.push(s);            }        }

(e) 遍历所有新建的实例,为每个实例触发OnCreated事件函数,如果EventSheet中定义了相应的Action,则会触发执行该动作。

    for (i = 0, len = created_instances.length; i < len; i++)    {        inst = created_instances[i];        this.runtime.trigger(Object.getPrototypeOf(inst.type.plugin).cnds.OnCreated, inst);    }

(f) 触发OnLayoutStart事件函数。

this.runtime.trigger(cr.system_object.prototype.cnds.OnLayoutStart, null);

5) 如果没有使用Layout加载界面,则直接标记加载完成,触发cr.system_object.prototype.cnds.OnLoadFinished事件。由于触发器必须在Layout中执行,因此前面必须先调用Layout的startRunning函数。

this.loadingprogress = 1;           this.trigger(cr.system_object.prototype.cnds.OnLoadFinished, null); 

6) 遍历所有对象类型,执行onAppBegin函数(如果有的话)。调用tick函数进入游戏循环。

for (i = 0, len = this.types_by_index.length; i < len; i++)        {            t = this.types_by_index[i];            if (t.onAppBegin)                t.onAppBegin();        }        this.tick(false);

下一节介绍游戏主循环

0 0
原创粉丝点击