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

egret的github地址是https://github.com/egret-labs...,你們本身git clone到本地。html

一.路口html文件

用ergetWing新建一個工程,打開根目錄下的index.html,這個就是項目的入口文件,咱們看下其中裝載遊戲內容的DIV容器html5

<div style="margin: auto;width: 100%;height: 100%;" class="egret-player"
         data-entry-class="Main"
         data-orientation="auto"
         data-scale-mode="exactFit"
         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"
         data-show-fps-style="x:0,y:0,size:12,textColor:0xffffff,bgAlpha:0.9">
    </div>
    <script>
        /**
         * {
         * "renderMode":, //引擎渲染模式,"canvas" 或者 "webgl"
         * "audioType": 0 //使用的音頻類型,0:默認,1:qq audio,2:web audio,3:audio
         * "antialias": //WebGL模式下是否開啓抗鋸齒,true:開啓,false:關閉,默認爲false
         * "retina": //是否基於devicePixelRatio縮放畫布
         * }
         **/
        egret.runEgret({renderMode:"webgl", audioType:0});
    </script>

這裏是官方的配置說明,由於咱們後面分析源碼會用到,你們能夠先粗略看一下android

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的運行流程。git

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部分的思路進行分析。github

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各項支持信息。web

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。canvas

/**
     * 設置渲染模式。"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的使用你們能夠看這裏初識 WebGLsegmentfault

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 = <HTMLDivElement>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):全局哈希基類和全局異步函數對象接口

相關文章
相關標籤/搜索