聲明:本文爲原創文章,如需轉載,請註明來源WAxes,謝謝!html
樓主記憶力很差,最近恰好用了一下createJs框架,怕之後一段時間沒用後會忘記,因此在此作個記錄,或許之後用得着。git
createJs網上的中文教程挺少的,之前UC有個Xcanvas的論壇有createJs的詳細教程,可是隨着XCanvas團隊的解散,那個網站也關閉了。。網上的大部分都是很是基礎的教程,有點千遍一概的感受。因此樓主就去把createJs下載下來,硬着頭皮看英文文檔了。憑着樓主這英語六級只考了三百多分的渣渣來講,看起來很費力啊,不過仍是勉強摸索出了大概的用法。因此如今就是學了多少就記錄多少,以後或許也會不按期更新一下該框架的新的學習心得。畢竟對本身之後仍是有幫助的。github
但願本文能幫到那些想學createJs的新手。由於樓主也是剛學的,因此本文或許有不正確之處,所以本文僅當參考,如有不正之處歡迎斧正。canvas
閒話說到這,直接進入主題。後端
樓主用createJs寫了個簡單的跑酷遊戲DEMO,就拿它作例子吧。 看DEMO戳我。數組
createJs的由來,基礎什麼的就不說了,就直接說createJs的用法吧。
框架
首先到createJs官網下載,createJs分紅easelJs(圖形動畫)、preloadJs(文件加載)、soundJs(音頻控制)以及tweenJs(補間動畫)四部分,你們下載的時候,建議下載兩個文件,一個是壓縮版文件,用於項目中的引用,再下載個源碼文件,用於查看用法、API、demo等。由於樓主目前只用了easelJs和preloadJs,因此暫時就只說這兩個,其實就這兩個已經很是夠用了。dom
接下來開始分析代碼:函數
首先引入js文件學習
<script src="easeljs-0.7.1.min.js"></script> <script src="preloadjs-0.4.1.min.js"></script>
而後進行舞臺初始化操做:
function init(){ stage = new createjs.Stage("cas"); C_W = stage.canvas.width; C_H = stage.canvas.height; var manifest = [ {src:"image/man.png" , id:"man"}, {src:"image/ground.png" , id:"ground"}, {src:"image/bg.png" , id:"bg"}, {src:"image/high.jpg" , id:"high"}, {src:"image/coins.png" , id:"coin"} ] loader = new createjs.LoadQueue(false); loader.addEventListener("complete" , handleComplete); loader.loadManifest(manifest); drawLoading(); }
上面就用到了preloadJs中的方法,實例化一個loader,把須要加載的圖片文件放在manifest裏面,進行加載,加載完成後調用回調handleCompelete函數:
function handleComplete(){ //當圖片素材load完後執行該方法 var manImage = loader.getResult("man"), lowground = loader.getResult("ground"), highground = loader.getResult("high"), bgImage = loader.getResult("bg"), coins = loader.getResult("coin"); sky = new createjs.Shape(); sky.graphics.bf(bgImage).drawRect(0,0,C_W,C_H); sky.setTransform(0, 0, 1 , C_H/bgImage.height); stage.addChild(sky); man = createMan(200,326,manImage); //該框爲斷定角色的斷定區域 kuang = new createjs.Shape(); kuang.graphics.beginStroke("rgba(255,0,0,0.5)").drawRect(0 , 0 , man.size().w , man.picsize().h*1.5); // stage.addChild(kuang); mapHandle(lowground , highground , coins); createjs.Ticker.timingMode = createjs.Ticker.RAF;//設置循環方法,能夠是requestAnimationFrame或者是setTimeout createjs.Ticker.setFPS(30);//舞臺幀率控制 createjs.Ticker.addEventListener("tick", tick);//綁定舞臺每一幀的邏輯發生函數 window.addEventListener("keydown" , function(event){ event = event||window.event; if(event.keyCode===32&&man.jumpNum<man.jumpMax){ man.jump(); } }) }
得到加載完成後端的圖片數據就直接用loader.getResult就能夠獲取了,跑酷遊戲須要一個背景,因此,咱們實例化一個sky,而後進行位圖繪製,bf方法是beginBitmapFill的縮寫,該方法就是開始繪製位圖,後面的drawRect是位圖的繪製區域,區域固然是整個畫布啦,因此就是drawRect(0,0,C_W,C_H)。實例化出來sky後就直接添加到舞臺stage裏面就好了。接下來是實例化一個角色,createMan方法後面有說,是本身封裝的。
而後進行舞臺循環設置,上面有註釋了,就不說了。
舞臺設置中,mapHandle是地圖數據的初始化:
var mapIndex = 0, //地圖序列 Mix = 0, //地圖數組的索引 allStones = [], //存放全部的石頭 allCoins = [], //全部金幣 showSt = []; //存放顯示出來的石頭 function mapHandle(lowground , highground , coins){ //初始化地圖 allStones.length = 0; var stoneImage = {"A":lowground , "B":highground},kind = null; for(var i=0;i<30;i++){ //把須要用到的石頭預先放入容器中準備好 switch(i){ case 0:kind="A";break; case 10:kind="B";break; case 20:kind="C";break; } var st = createStone(C_W , kind , stoneImage); allStones.push(st) } for(var i=0;i<10;i++){ //把須要用到的金幣預先放入容器中 var coin = createCoin(coins); allCoins.push(coin); } Mix = Math.floor(Math.random()*mapData.length); //隨機地圖序列 for(var i=0;i<8;i++){ setStone(false) } } function setStone(remove){ //添加陸地的石頭 var arg = mapData[Mix].charAt(mapIndex), coarg = coinCode[Mix].charAt(mapIndex), cc = null; if(coarg==="#"){ for(var i=0;i<allCoins.length;i++){ if(!allCoins[i].shape.visible){ cc = allCoins[i]; cc.shape.visible = true; break; } } } for(var z=0;z<allStones.length;z++){ if(!allStones[z].shape.visible&&allStones[z].kind===arg){ var st = allStones[z]; st.shape.visible = true; st.shape.x = showSt.length===0?0:showSt[showSt.length-1].shape.x+showSt[showSt.length-1].w; if(cc){ cc.shape.x = showSt.length===0?allStones[z].w/2-cc.size().w/2:showSt[showSt.length-1].shape.x+showSt[showSt.length-1].w+allStones[z].w/2-cc.size().w/2; cc.shape.y = arg==="C"? C_H-loader.getResult("high").height-50 : allStones[z].shape.y-cc.size().h/2-50; } if(remove) showSt.shift(); showSt.push(st); break; } } mapIndex++; if(mapIndex>=mapData[Mix].length){ Mix = Math.floor(Math.random()*mapData.length) mapIndex=0; } }
下面是人物模塊的封裝
(function(w){ var FRAME_RATE = 13, //精靈表播放速度 SCALE_X = 1.5, //X軸縮放 SCALE_Y = 1.5, //Y軸縮放 GRAVITY = 3, //重力加速度 JUMP_SPEED = 2.6, //垂直速度 WIDTH = 40, HEIGHT = 96, PICWIDTH = 64, PICHEIGHT = 64, PROPORTION = 150/1; //遊戲與實際的距離比例 var Man = function(x , y , img){ this.x = x; this.y = y; this.endy = y; this.vx = 0.5; this.vy = 0; this.ground = []; this.state = "run"; this.jumpNum = 0; this.jumpMax = 1; this.init(img); } Man.prototype = { constructors:Man, init:function(img){ var manSpriteSheet = new createjs.SpriteSheet({ //實例化精靈表繪製器 "images":[img], "frames":{"regX":0,"height":PICWIDTH,"count":45,"regY":1,"width":PICHEIGHT}, "animations":{ "run":{ frames:[21,20,19,18,17,16,15,14,13,12], //精靈表每一幀的位置 next:"run", //當精靈表循環完後的下一步動做 speed:1, //精靈表播放速度 }, "jump":{ frames:[34,35,36,37,38,39,40,41,42,43], next:"run", speed:1, }, "die":{ frames:[8,7,6,5,4,3,2,1,0], next:"die", speed:1, } } }); this.sprite = new createjs.Sprite(manSpriteSheet , this.state); //實例化精靈 this.sprite.framerate = FRAME_RATE; //精靈表繪製速率 this.sprite.setTransform(this.x, this.y, SCALE_X, SCALE_Y); //設置精靈的位置 stage.addChild(this.sprite); //添加到舞臺 }, update:function(){ var sprite = this.sprite; var time = createjs.Ticker.getInterval()/1000; //獲取當前幀與上一幀的時間間隔 if(this.state==="run"){ if(sprite.x<this.x){ sprite.x +=this.vx; }else { sprite.x = this.x } } if(this.endy>sprite.y||this.state==="jump"){ //角色的動做處理 var nexty = sprite.y+time*this.vy*PROPORTION; this.vy += time*GRAVITY; sprite.y += time*this.vy*PROPORTION; if(Math.abs(sprite.y-this.endy)<10&&this.vy>0){ this.state = "run"; sprite.y=this.endy; this.vy = 0; } } if(sprite.x+(PICWIDTH*SCALE_X-WIDTH)/2<0||sprite.y>C_H+200){ this.die(); createjs.Ticker.reset(); alert("you are Die!"); } switch(this.state){ case "run": this.jumpNum = 0; break; case "die": if(sprite.currentFrame===0){ sprite.paused = true; } break; } }, run:function(){ this.sprite.gotoAndPlay("run") }, jump:function(){ this.vy = -JUMP_SPEED; this.state = "jump"; this.sprite.gotoAndPlay("jump"); //讓精靈表播放特定的動畫 this.jumpNum++; }, die:function(){ this.state = "die"; this.sprite.gotoAndPlay("die") }, size:function(){ return { w:WIDTH, h:HEIGHT } }, picsize:function(){ return { w:PICWIDTH, h:PICHEIGHT } } } w.createMan = function(x , y , img){ return new Man(x , y , img) }; })(window)
人物模塊封裝就是簡單的在createJs的封裝之上進行進一步的封裝,封裝很簡單,就是用createJs實例化一個精靈類,再綁定精靈表,上面的代碼中也有註釋,基本上都說的很明白了。
下面貼出封裝的石頭以及金幣模塊,簡單說下背景的循環,預先實例化一堆石頭和金幣,而後移動響應的石頭,當石頭移動到超出舞臺區域時,把他的visible屬性置爲false,再從新添加一個石頭在最後的位置進行新的一次移動。金幣也同樣。地圖數據則是經過預先定義好的字符串來實現。
(function(w){ var SPEED = 4, COIN_STAY_X = 20, COIN_STAY_Y = 20, COIN_STAY_WIDTH = 30, COIN_STAY_HEIGHT = 30, COIN_SCALE_X = 0.08, COIN_SCALE_Y = 0.08; //地上的石頭類 var Stone = function(x,kind,allImage){ this.x = x; this.kind = kind; this.allImage = allImage; this.init(); } var sp = Stone.prototype; sp.init=function(){ this.shape = new createjs.Shape(); if(this.kind!=="C"){ this.h = this.allImage[this.kind].height; this.w = this.allImage[this.kind].width*2; this.y = C_H - this.h; this.shape.graphics.beginBitmapFill(this.allImage[this.kind]).drawRect(0, 0, this.w, this.h); this.shape.setTransform(this.x, this.y, 1, 1); }else { this.h = -1000; this.w = 170; this.y = C_H - this.h; this.shape.graphics.beginFill("#000").drawRect(0, 0, this.w, this.h); this.shape.setTransform(this.x, this.y, 1, 1); } this.shape.visible = false; this.shape.cache(0 , 0 , this.w , this.h); stage.addChild(this.shape); } sp.update=function(){ this.shape.x -= SPEED; } //金幣類 var Coin = function(image){ this.sizeX = COIN_SCALE_X; this.sizeY = COIN_SCALE_Y; this.isget = false; this.init = function(){ this.shape = new createjs.Shape(); this.shape.graphics.beginBitmapFill(image).drawRect(0, 0, image.width, image.height); this.shape.setTransform(0, 0, COIN_SCALE_X, COIN_SCALE_Y); this.shape.visible = false; stage.addChild(this.shape); } this.init(); this.update = function(){ if(this.isget){ this.sizeX = this.sizeX + ((COIN_STAY_WIDTH/image.width) - this.sizeX)*0.1; this.sizeY = this.sizeY + ((COIN_STAY_HEIGHT/image.height) - this.sizeY)*0.1; this.shape.setTransform( this.shape.x + (COIN_STAY_X - this.shape.x)*0.1, this.shape.y + (COIN_STAY_Y - this.shape.y)*0.1, this.sizeX, this.sizeY ); if(Math.abs(this.shape.x-COIN_STAY_X)<0.5&&Math.abs(this.shape.y-COIN_STAY_Y)<0.5){ this.shape.visible = false; this.isget = false; this.sizeX = COIN_SCALE_X; this.sizeY = COIN_SCALE_Y; this.shape.setTransform(0,0,this.sizeX,this.sizeY); } } else{ this.shape.x -= SPEED; if(this.shape.x<-image.width*COIN_SCALE_X){ this.shape.visible = false; } } } this.size = function(){ return { w:image.width*COIN_SCALE_X, h:image.height*COIN_SCALE_Y } } } w.createCoin = function(image){ return new Coin(image) } w.createStone = function(x,kind,allImage){ return new Stone(x,kind,allImage); } })(window)
封裝方法跟上面的人物模塊封裝差很少,不過人物是用精靈類,石頭金幣則是用形狀類了。就是經過位圖的繪製,來繪製位圖的圖片,原理都同樣。
最後是舞臺逐幀處理的tick方法:
function tick(event){ //舞臺逐幀邏輯處理函數 man.update(); kuang.x = man.sprite.x+(man.picsize().w*1.5-man.size().w)/2; //參考框 kuang.y = man.sprite.y; man.ground.length=0; var cg = stoneHandle(); if(man.ground[0]&&!cg) { man.ground.sort(function(a,b){return b.h-a.h}); man.endy = man.ground[0].y-man.picsize().h*1.5; } allCoins.forEach(function(cc , index){ if(cc.shape.visible){ if( Math.abs((kuang.x+man.size().w/2) - (cc.shape.x+cc.size().w/2)) <= (man.size().w+cc.size().w)/2&& Math.abs((kuang.y+man.size().h/2) - (cc.shape.y+cc.size().h/2)) <= (man.size().h+cc.size().h)/2&& !cc.isget ){ cc.isget = true; countCoin.innerHTML = parseInt(countCoin.innerHTML)+1 } cc.update(); } }) document.getElementById("showFPS").innerHTML = man.endy stage.update(event) }
在每一幀的處理,就像本身寫遊戲同樣啦,就是把舞臺裏的全部對象逐個進行邏輯運算,進行相應處理。
基本上createJs的用法仍是相對比較簡單而且強大的。比本身去造輪子能省不少功夫。
源碼地址:https://github.com/whxaxes/canvas-test/tree/gh-pages/src/Game-demo/runningMan