Html5游戲框架createJs的簡單用法


  聲明:本文為原創文章,如需轉載,請注明來源WAxes,謝謝!

  樓主記憶力不好,最近剛好用了一下createJs框架,怕以后一段時間沒用后會忘記,所以在此做個記錄,或許以后用得着。

  createJs網上的中文教程挺少的,以前UC有個Xcanvas的論壇有createJs的詳細教程,但是隨着XCanvas團隊的解散,那個網站也關閉了。。網上的大部分都是非常基礎的教程,有點千遍一律的感覺。所以樓主就去把createJs下載下來,硬着頭皮看英文文檔了。憑着樓主這英語六級只考了三百多分的渣渣來說,看起來很費力啊,不過還是勉強摸索出了大概的用法。所以現在就是學了多少就記錄多少,之后或許也會不定期更新一下該框架的新的學習心得。畢竟對自己以后還是有幫助的。

  希望本文能幫到那些想學createJs的新手。因為樓主也是剛學的,所以本文或許有不正確之處,因此本文僅當參考,若有不正之處歡迎斧正。

  閑話說到這,直接進入主題。

  樓主用createJs寫了個簡單的跑酷游戲DEMO,就拿它做例子吧。 看DEMO戳我

  createJs的由來,基礎什么的就不說了,就直接說createJs的用法吧。

  首先到createJs官網下載,createJs分成easelJs(圖形動畫)、preloadJs(文件加載)、soundJs(音頻控制)以及tweenJs(補間動畫)四部分,大家下載的時候,建議下載兩個文件,一個是壓縮版文件,用於項目中的引用,再下載個源碼文件,用於查看用法、API、demo等。因為樓主目前只用了easelJs和preloadJs,所以暫時就只說這兩個,其實就這兩個已經非常夠用了。

  接下來開始分析代碼:

  首先引入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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM