HTML5引擎Construct2技术剖析(五)

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

(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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值