egret的github地址是https://github.com/egret-labs...,你们本身git clone到本地。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
咱们在源码项目中搜索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
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得到。正则表达式
// 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
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; //...... }
若是用户配置了缩放比例就使用它,配置了计算缩放比例的方法就调用它,不然计算当前浏览器支持的最大精度的缩放比例。浏览器
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的坑。
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()方法进行全局的数据更新和视图渲染。那么整个游戏引擎大概的启用流程到这里就结束了。