createjs高级教程:createjs的位图渲染(非常重要)
今天给大家讲的东西,大家请画个重点,要“考的”,当然不是真的要考试,只是说他对于createjs编程非常重要。
今天给大家讲的是位图渲染,开讲之前先给大家普及一下,什么叫做位图渲染。位图渲染,从flash时代就是很有名的增加游戏同屏数量的方法。从名字上来说,就是把素材渲染成位图。大家都知道mc也就是movieclip的性能非常差,矢量动画的性能也非常差,但是图片的性能却还不错,所以之前我都推荐大家使用sprite。但是有些地方却不得不使用mc,或者矢量,其根本原因就是sprite也就是雪碧图真的太大了,他比一个同等时间的视频还要大。那么有没有,又能像矢量这么小,又能如sprite一样快的办法呢?有!位图渲染就是为此诞生的!他的基本原理就是把任何的素材都可以用copy像素的方式转化为位图,从而提升性能。这时候,做过as3的童鞋可能会说了:“不就是cacheAsBitmap吗?”。确实cacheAsBitmap就是渲染成位图而createjs的cache也是同样的道理(虽说道理一样但是实际原理还是不一样的,createjs的cache并没有变成位图),但是!!!cache的过程中也是非常消耗性能的,当你使用cache的时候可能性能不但没提升可能还会下降。所以我们的位图渲染并不是简单的cache,而是在它使用前,提前渲染成位图,也就是,俗称的用时间换效率的办法。具体的做法是,让动画自己运行一遍,然后不停的copy它每一帧的像素,使它成为一张sprite表,等到要用的时候他已经成为了sprite,也就不怎么耗性能了。这时候也许有人会问,虽然矢量的加载时间不多,但是算上渲染时间是不是和直接加载sprite相同了?答案是完全不同!我们计算机手机的性能非常强,渲染的速度是非常快的,并且渲染的过程是不耗流量的,尤其适合web和移动端开发。
讲完了位图渲染的原理,那我再讲讲怎么在createjs上做位图渲染。
之前我一直想自己封装一个位图渲染类,毕竟as3也就是flash他也没有自己的位图渲染类,靠自己封装的。但是就在我准备写的时候,惊奇的发现,createjs其实已经实现的这个类。这个类名就叫做SpriteSheetBuilder。期初我没有重视这个类,是因为我一直认为他是sprite类的一个底层分类,毕竟官方例子也没有着重写他,api也写的及其晦涩。后来看源代码的时候发现了这个类,正所谓是金子总会发光的真的没错。
来,新鲜的代码来了
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style> *{ margin: 0; padding: 0; } </style> </head> <body> <canvas id="canvas" width="1920" height="1000" style="position: absolute"></canvas> <script src="createjs-1.0.0.min.js" type="text/javascript"></script> <script src="FPS.js" type="text/javascript"></script> <script src="assets.js" type="text/javascript"></script> <script> var canvas = document.getElementById("canvas") var stage = new createjs.StageGL(canvas); var huo = new lib.huo();//矢量影片剪辑 var spritesheetBuilder = new createjs.SpriteSheetBuilder(); spritesheetBuilder.addMovieClip(huo,new createjs.Rectangle(0,0,180,160),0.5); spritesheetBuilder.addEventListener("progress",function (event){ console.log(event,"<<<<<<<<<<<<<<<<")//渲染进度 }) spritesheetBuilder.addEventListener("complete",function(event){ console.log("complete",spritesheetBuilder.spriteSheet) spritesheetBuilder.stopAsync(); renderCompleteHandler(spritesheetBuilder.spriteSheet) }) spritesheetBuilder.buildAsync(); createjs.Ticker.framerate = 65;//用RAF_SYNCHED模式帧频要多放几侦,不然掉一帧帧频就自动降级 createjs.Ticker.timingMode = createjs.Ticker.RAF_SYNCHED; createjs.Ticker.addEventListener("tick",function (){ stage.update(); }) var spritesheet,sprite function renderCompleteHandler(sheet) { spritesheet = sheet; sprite = new createjs.Sprite(spritesheet); sprite.play(); stage.addChild(sprite);//在这里矢量已经变成位图 sprite.x = 500; sprite.y = 0; moreHandler(); } function moreHandler() { for(var i = 0;i < 15000;i++) { var s = new createjs.Sprite(spritesheet); stage.addChild(s); s.x = Math.random()*1920-50; s.y = Math.random()*900+100; s.gotoAndPlay(parseInt(Math.random()*20)); } FPS.startFPS(stage); FPS.showText = "COUNT:15000" } </script> </body> </html>
代码非常简单,就是我创建了一个movieclip对象“huo”,然后用SpriteSheetBuilder类做了位图渲染,最后创建渲染后的sprite对象,并创建15000个对象来测试性能。
那性能怎么样呢?下面的例子点击看看。
http://www.ajexoop.com/test/SpriteSheetBuilder/index.html
15000个矢量火,太多了全屏都是橙色,我特意放了一个在最上面,让童鞋们看出来这是火……
可以看到帧频再15000个矢量对象并且还在有滤镜的情况下(从火的素材可以看出,加了发光滤镜),跑到了60帧,当然这是在我的机子上,不同的机子不同的性能结果也不同。这里还有一点值得注意的,在使用animateCC给movieclip加滤镜并进行位图渲染时,滤镜要在每个帧的对象上加,而不能直接在父mc上加,除非你用代码加滤镜。
除了可以让movieclip做位图渲染外,还可以让代码生成的矢量对象做位图渲染,我下面再放一段代码:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style> *{ margin: 0; padding: 0; } </style> </head> <body> <canvas id="canvas" width="1920" height="1000" style="position: absolute"></canvas> <script src="createjs-1.0.0.min.js" type="text/javascript"></script> <script> var canvas = document.getElementById("canvas") var stage = new createjs.StageGL(canvas); var spritesheetBuilder = new createjs.SpriteSheetBuilder(); var text = new createjs.Text() text.font = "bold 36px Arial"; text.color = "#000000"; var rect = new createjs.Rectangle(0,0,60,60); var str = "你今天真的好么,我今天真的非常好!" var txtFrameFunction = function (source, data) { source.text = str.charAt(data.i).toString(); } for(var i= 0;i < str.length;i++) { // text.text = str.charAt(i).toString();//这样来加帧是没有的,调用的都是最后一个字母 spritesheetBuilder.addFrame(text,rect,1,txtFrameFunction,{"i":i}); } // spritesheetBuilder.addEventListener("progress",function (event){ // }) spritesheetBuilder.addEventListener("complete",function(event){ console.log("complete",spritesheetBuilder.spriteSheet) spritesheetBuilder.stopAsync(); renderCompleteHandler(spritesheetBuilder.spriteSheet) }) spritesheetBuilder.buildAsync(); var spritesheet,sprite function renderCompleteHandler(sheet) { spritesheet = sheet; sprite = new createjs.Sprite(spritesheet); sprite.play(); stage.addChild(sprite);//在这里矢量已经变成位图 sprite.x = 500; sprite.y = 0; } createjs.Ticker.framerate = 65;//用RAF_SYNCHED模式帧频要多放几侦,不然掉一帧帧频就自动降级 createjs.Ticker.timingMode = createjs.Ticker.RAF_SYNCHED; createjs.Ticker.addEventListener("tick",function (){ stage.update(); }) </script> </body> </html>
http://www.ajexoop.com/test/SpriteSheetBuilder/index2.html
可以点击链接,看一下效果,我把文字(text,shape都是矢量)做了位图渲染,这样就不需要bitmapText了。不过方法有些特别,重点是看那个引入的方法,有了这个方法txtFrameFunction,才能边播放边渲染,才能最后把整个动画渲染出来(坑爹的官方居然一点例子都没有,不是看源代码我也不知道怎么用)。
那么位图渲染的范围只限于矢量和滤镜么?答案是不止的,还有复杂对象动画。比如:带了很多装备武器的人物骨骼动画。说到这里我提一下,egret,也就是白鹭,有个渲染龙骨的例子,同屏可以渲染很多对象,但是我们自己用龙骨却不可以,原因就是,他做了位图渲染。用了位图渲染,带了再多装备,做再复杂的动作对它来说也只是一张图。那怎么看出来有没有做位图渲染呢?看有没有做位图渲染,只需要看他第二次加载的时候是不是瞬间加载完毕的就可以了,如果不是瞬间加载完毕的,说明他这不是在加载,而是在渲染。因为第二次肯定是加载缓存的,缓存可以很快的加载完成。
接下来,还要说一个重要的事:“不是用了位图渲染,性能就一定能变好了”。一台设备如果连只渲染sprite也卡的话,位图渲染也是没用的,程序员做些不适宜位图渲染的事,位图渲染也不会使程序变快,这点换别的引擎也是一样的。下面我例举一下:
1:位图渲染中,把本身不需要渲染的背景部分一起渲染进去。
我这篇文章一出,有些童鞋可能会把自己舞台上所有的对象全部放进movieclip里,一整个做个位图渲染,这样子并没有用的,只会增加重绘区域,并不会使程序变快。只需要渲染矢量的部分,滤镜的部分,复杂对象的部分(比如带了装备的骨骼)渲染就够了。
2:矢量对象形状是随机的。
矢量对象形状如果是随机的,就变得经常要渲染,这时候不仅不会使程序变快,还会有内存泄漏的隐患。这样还不如不停的cache。
3:本身就可以做进位图里。
本身可以做进位图里,还用位图渲染,除非你为了省加载时间。不然就是没事找事。
4:渲染后,源对象还在舞台上的。
这个就不解释了,低级错误。
5:其他原因造成的卡顿。
比如算法啊,内存泄漏啊,嵌套太多啊,死循环啊等等。
最后……大家都学会了么。
支雅
好厉害,学到了。
匿名
最开始没关注api的时候 一直是用双缓冲来操作 生成位图渲染 一个隐藏的复杂mc 画到一个显示的容器里面
ajex
@匿名 我刚开始的时候也是准备这么封装,结果发现了这个api