资讯专栏INFORMATION COLUMN

erget源码分析(1):入口文件分析

Rainie / 1941人阅读

摘要:是一个静态类,调用方法来初始化各项支持信息。小结通过和这两个静态类初始化了项目运行的环境参数,然后创建了屏幕适配器根据不同的适配策略调整。下一篇源码分析全局哈希基类和全局异步函数对象接口

egret的github地址是https://github.com/egret-labs...,大家自己git clone到本地。

一.路口html文件

用ergetWing新建一个工程,打开根目录下的index.html,这个就是项目的入口文件,我们看下其中装载游戏内容的DIV容器

   

这里是官方的配置说明,因为我们后面分析源码会用到,大家可以先粗略看一下

data-entry-class=”Main” 设置项目的入口文件,表示项目的入口类,默认为Main,如果需要自定义的话需要在项目中先创建类,然后在这里配置类的名字。
data-orientation=”auto” 设置旋转模式。
data-scale-mode=”showAll” 设置缩放模式。
data-frame-rate=”30” 这里是运行的帧率。
data-content-width=”480” 和 data-content-height=”800” 用来设置舞台的设计宽和高
data-show-paint-rect=”false” 设置显示脏矩形的重绘区域。
data-multi-fingered=”2” 设置多指触摸
data-show-fps=”false” data-show-log=”false” 这里设置显示帧率和log,只有在调试时会显示,发布的版本会去掉。
data-log-filter=”” 设置一个正则表达式过滤条件,日志文本匹配这个正则表达式的时候才显示这条日志。如 data-log-filter="^egret" 表示仅显示以 egret 开头的日志。
data-show-fps-style=”x:0,y:0,size:30,textColor:0x00c200,bgAlpha:0.9” 这里设置fps面板的样式。目前支持默认的这几种设置,修改其值即可,比如修改面板位置可以设置x和y,改变大小可以设置size,改变文字颜色textColor,改变背景面板的透明度bgAlpha。

页面打开后会立即执行egret.runEgret(),这几个参数后面会详细讲,我们先理解erget的运行流程。

EgretWeb.ts入口脚本

我们在源码项目中搜索runEgret可以看到分别在
src/egret/native/EgretNative.ts
src/egret/player/EgretEntry.ts
src/egret/web/EgretWeb.ts中被定义。
这是egret的一个基本的结构,EgretEntry.ts定义了接口,而EgretNative.ts和EgretWeb.ts分别定义在原生平台和web平台上的实现。我们先按照web部分的思路进行分析。

1.初始化环境参数
    let isRunning: boolean = false;

    /**
     * @private
     * 网页加载完成,实例化页面中定义的Egret标签
     */
    function runEgret(options?: runEgretOptions): void {
        if (isRunning) {
            return;
        }
        isRunning = true;
        if (!options) {
            options = {};
        }
        Html5Capatibility._audioType = options.audioType;
        Html5Capatibility.$init();
        //......
   }

首先看runEntry函数的前面几行,这里利用一个闭包和isRunning,还有if语句来防止重复运行游戏。这里的isRunning这个变量明显只被runEntry函数使用,所以不把它定义为成员私有属性而是定义成一个变量。Html5Capatibility是一个静态类,调用$init()方法来初始化html5各项支持信息。

public static $init(): void {
    let ua: string = navigator.userAgent.toLowerCase();
    Html5Capatibility.ua = ua;
    egret.Capabilities.$isMobile = (ua.indexOf("mobile") != -1 || ua.indexOf("android") != -1);
    //......
}

我们稍微来看一下$init()方法,这里还调用了一个全局静态类Capabilities,这个类在src/egret/system/Capabilities.ts下,主要是存储当前运行的设备(PC/IOS/Android)信息、平台信息(web/native)、渲染模式、引擎版本和客户端尺寸。
我们可以看出,在web平台关于运行环境的各项信息从Capabilities获得,HTML5的接口支持从Html5Capatibility获得。

2.配置渲染
 // WebGL上下文参数自定义
function runEgret(options?: runEgretOptions): void {
        if (options.renderMode == "webgl") {
            // WebGL抗锯齿默认关闭,提升PC及某些平台性能
            let antialias = options.antialias;
            WebGLRenderContext.antialias = !!antialias;
            // WebGLRenderContext.antialias = (typeof antialias == undefined) ? true : antialias;
        }
    sys.CanvasRenderBuffer = web.CanvasRenderBuffer;
    setRenderMode(options.renderMode);
    ......
}

这里有个小技巧就是利用两个!来转型,因为options.antialias可能是false、true、undefined中的一个,如果是false或者true,两个!!相当于没有作用,如果是undefined就被转换成false。

    /**
     * 设置渲染模式。"auto","webgl","canvas"
     * @param renderMode
     */
    function setRenderMode(renderMode: string): void {
        //......
        if (renderMode == "webgl" && WebGLUtils.checkCanUseWebGL()) {
            sys.RenderBuffer = web.WebGLRenderBuffer;
            sys.systemRenderer = new WebGLRenderer();
            sys.canvasRenderer = new CanvasRenderer();
            sys.customHitTestBuffer = new WebGLRenderBuffer(3, 3);
            sys.canvasHitTestBuffer = new CanvasRenderBuffer(3, 3);
            Capabilities.$renderMode = "webgl";
        }
        else {
            sys.RenderBuffer = web.CanvasRenderBuffer;
            sys.systemRenderer = new CanvasRenderer();
            sys.canvasRenderer = sys.systemRenderer;
            sys.customHitTestBuffer = new CanvasRenderBuffer(3, 3);
            sys.canvasHitTestBuffer = sys.customHitTestBuffer;
            Capabilities.$renderMode = "canvas";
        }
        //......
    }

我们简单来看一下setRenderMode方法,如果用户把renderMode设置为webGL并且浏览器支持webGL就使用webGL否则使用canvas,WebGLUtils.checkCanUseWebGL()这个方法大家可以自己去看一下,同样使用了两个!!的技巧,关于webGL的使用大家可以看这里初识 WebGL

3.分辨率配置
function runEgret(options?: runEgretOptions): void {
        //......
        let canvasScaleFactor;
        if (options.canvasScaleFactor) {
            canvasScaleFactor = options.canvasScaleFactor;
        }
        else if(options.calculateCanvasScaleFactor) {
            canvasScaleFactor = options.calculateCanvasScaleFactor(sys.canvasHitTestBuffer.context);
        }
        else {
            //based on : https://github.com/jondavidjohn/hidpi-canvas-polyfill
            let context = sys.canvasHitTestBuffer.context;
            let backingStore = context.backingStorePixelRatio ||
                context.webkitBackingStorePixelRatio ||
                context.mozBackingStorePixelRatio ||
                context.msBackingStorePixelRatio ||
                context.oBackingStorePixelRatio ||
                context.backingStorePixelRatio || 1;
            canvasScaleFactor = (window.devicePixelRatio || 1) / backingStore;
        }
        sys.DisplayList.$canvasScaleFactor = canvasScaleFactor;
        //......
}

如果用户配置了缩放比例就使用它,配置了计算缩放比例的方法就调用它,否则计算当前浏览器支持的最大精度的缩放比例。

4.执行系统定时器
function runEgret(options?: runEgretOptions): void {
    //......
    let ticker = egret.ticker;
    startTicker(ticker);
    //......
}
        

egret.sys.$ticker是egret.SystemTicker类的单例对象,首先对它调用了startTicker方法:

function startTicker(ticker:egret.sys.SystemTicker):void {
    var requestAnimationFrame =
        window["requestAnimationFrame"] ||
        window["webkitRequestAnimationFrame"] ||
        window["mozRequestAnimationFrame"] ||
        window["oRequestAnimationFrame"] ||
        window["msRequestAnimationFrame"];
    if (!requestAnimationFrame) {
        requestAnimationFrame = function (callback) {
            return window.setTimeout(callback, 1000 / 60);
        };
    }
    requestAnimationFrame.call(window, onTick);
    function onTick():void {
        ticker.update();
        requestAnimationFrame.call(window, onTick)
    }
}

这里同样是判读浏览器是否存在requestAnimationFrame的API,存在则使用之,否则使用setTimeout方法,这里onTicker使用了延迟递归调用,实现每隔一段时间就调用一次ticker.update()方法,这里使用call方法确保调用该方法对象是全局window对象,避开js中this的坑。

5.屏幕适配和播放器的创建
function runEgret(options?: runEgretOptions): void {
    //......
    if (options.screenAdapter) {
            egret.sys.screenAdapter = options.screenAdapter;
        }
        else if (!egret.sys.screenAdapter) {
            egret.sys.screenAdapter = new egret.sys.DefaultScreenAdapter();
        }

        let list = document.querySelectorAll(".egret-player");
        let length = list.length;
        for (let i = 0; i < length; i++) {
            let container = list[i];
            let player = new WebPlayer(container, options);
            container["egret-player"] = player;
            //webgl模式关闭脏矩形
            if (Capabilities.$renderMode == "webgl") {
                player.stage.dirtyRegionPolicy = DirtyRegionPolicy.OFF;
            }
        }
        if (Capabilities.$renderMode == "webgl") {
            egret.sys.DisplayList.prototype.setDirtyRegionPolicy = function () { };
        }

        window.addEventListener("resize", function () {
            if (isNaN(resizeTimer)) {
                resizeTimer = window.setTimeout(doResize, 300);
            }
        });
    //......
   
}
//......
    let resizeTimer: number = NaN;

    function doResize() {
        resizeTimer = NaN;

        egret.updateAllScreens();

        if (customContext) {
            customContext.onResize(context);
        }
    }

接下来使用document.querySelectorAll()方法取得所有拥有"egret-player"的CSS class的DOM对象。就是我们一开始在index.html的body里看到的那个div标签。
遍历这些DOM对象,为每一个创建一个egret.WebPlayer对象,并赋值给DOM的"egret-player"属性(这是个自定义属性)。

这里还有一个值得注意的对方是resizeTimer,每当浏览器尺寸变化先进行一个判断,如果不存在重绘定时器(也就是resizeTimer为NaN),就启动一个定时器,在300毫秒后重新获取浏览器尺寸重新绘制,并把resizeTimer赋值为NaN表示这个定时器关闭了。这么做,是因为在PC端,我们修改浏览器尺寸是一个延续动作,也就是鼠标持续移动改变窗口尺寸,定义一个300毫秒的定时器延时重绘是防止过多的重绘请求占用资源。

小结

runEgret通过Html5Capatibility和Capatibilities这两个静态类初始化了项目运行的环境参数,然后创建了屏幕适配器egret.sys.screenAdapter根据不同的适配策略调整。然后通过监听winodw对象的resize事件监听客户端尺寸变化(包括旋转设备,改变浏览器窗口尺寸等)。最主要的事情是调用创建一个定时器无限地调用egret.sys.$ticker的update()方法进行全局的数据更新和视图渲染。那么整个游戏引擎大概的启用流程到这里就结束了。

下一篇:erget源码分析(2):全局哈希基类和全局异步函数对象接口

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/89710.html

相关文章

  • erget源码分析(2):全局哈希基类和全局异步函数对象接口

    摘要:异步函数对象接口,包含和两个成员方法。哈希计数在整个的源码中都没有找到和方法的调用,这两个方法的具体作用是在原生中实现类式继承和私有属性一类的功能。 文件结构 utils/HashObject.ts文件:showImg(https://segmentfault.com/img/bVZpuq?w=642&h=472); 首先解释一下文件结构图 __extends方法 通过原型对象模拟类...

    godlong_X 评论0 收藏0
  • erget源码分析(3):生命周期

    摘要:概述下面我们来分析一下中的生命周期。定义了监听生命周期的类,这个三个成员方法分别执行暂停继续和刷新。方法作为桥梁,接收到具体的类型的函数,创建的一个实例作为参数调用它。函数做了两件事情暂停应用程序,暂停背景音乐和音效的播放。 概述 下面我们来分析一下erget中的生命周期。 src/egret/player/SystemTicker.ts: export namespace li...

    Hydrogen 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<