1. 程式人生 > >CreateJS實現打飛機小遊戲

CreateJS實現打飛機小遊戲

之前對createJs體驗了一下,正好看到網上有一篇教程,試著也寫了一個打飛機的小遊戲,UI相對比較簡單,主要記錄下實現遊戲邏輯的思路。

window.onload = function() {
    canvas = document.getElementById("canvas");
    stage = new createjs.Stage(canvas);
    preload = new createjs.LoadQueue(false);
    //如果載入聲音,必須先註冊createjs.Sound
    preload.installPlugin(createjs.Sound);
    // 匯入資源建立舞臺
    game.init();
};
    init: function() {
        var progressText = new createjs.Text("", "20px Arial", "#dd4814");
        progressText.x = stage.canvas.width / 2;
        progressText.y = stage.canvas.height / 2;
        progressText.textAlign = 'center';
        stage.addChild(progressText);

        function startPreload() {
            var manifest = [{
                id: 'sprite',
                src: 'plane.png'
            }, {
                id: 'shot',
                src: 'shot.mp3'
            }, {
                id: "explosion",
                src: 'explosion.mp3'
            }];
            preload.setMaxConnections(3);
            preload.maintainScriptOrder = true;
            preload.on("progress", loadProgress);
            preload.on("error", loadError);
            preload.on("complete", loadComplete);
            preload.loadManifest(manifest);
        }
        startPreload();

        function loadProgress(e) {
            progressText.text = "已載入 " + (preload.progress * 100 | 0) + " %";
            console.log(progressText.text);
        }

        function loadError(e) {
            console.log(e);
        }

        function loadComplete(e) {
            stage.removeChild(progressText);
            game.handleComplete();
        }
    },

載入好需要用到的庫以後,先執行init方法載入遊戲資源;如果要播放聲音,需先preload.installPlugin(createjs.Sound)註冊createjs.Sound,不然後面直接呼叫方法播放音訊檔案會出錯。

    // 建立遊戲介面,設定按鍵
    handleComplete: function() {
        buildGame.init();
        buildGame.setContorl();
        game.startGame();
    },
    //開始遊戲
    startGame: function() {
        createjs.Ticker.setFPS(60);
        createjs.Ticker.addEventListener('tick', function() {
            updateGame.init();
            stage.update();
        });
    }

在startGame方法裡,設定幀頻,stage.update()遊戲更新場景。

buildGame.init();建立遊戲場景,飛機的精靈動畫物件。

init: function() {
        buildGame.buildMsg();
        buildGame.buildSpriteSheet();
        buildGame.buildPlayer();
        buildGame.buildEnemy();
        buildGame.buildSpace();
    },

用到的圖片素材以及相應的幀資料:


    //建立精靈動畫表
    buildSpriteSheet: function() {
        var data = {
            images: [preload.getResult('sprite')],
            frames: [
                [0, 0, 38, 42],
                [37, 0, 42, 42],
                [79, 0, 37, 42],
                [116, 0, 42, 42],
                [158, 0, 37, 42],
                [0, 70, 64, 64],
                [64, 70, 64, 64],
                [128, 70, 64, 64],
                [192, 70, 64, 64],
                [256, 70, 64, 64],
                [320, 70, 64, 64],
                [384, 70, 64, 64],
                [448, 70, 64, 64],
                [512, 70, 64, 64],
                [576, 70, 64, 64],
                [640, 70, 64, 64],
                [704, 70, 64, 64],
                [768, 70, 64, 64]
            ],
            animations: {
                ship: 0,
                enemy1: 1,
                enemy2: 2,
                enemy3: 3,
                enemy4: 4,
                exp: {
                    frames: [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
                    speed: .5
                }
            }
        };
        buildGame.spriteSheet = new createjs.SpriteSheet(data);
    },

這裡要吐槽一下,createjs沒有合適的動畫轉換工具可以直接生成圖片素材與幀資料,美中不足啊。

素材中有5種飛機,選1個為我們可操作的主角後,剩下4個插入敵機種類的陣列中。

//建立玩家的飛機
    buildPlayer: function() {
        Plane.player = new createjs.Sprite(buildGame.spriteSheet, 'ship');
        //getBounds方法返回當前物件寬高
        console.log(Plane.player.getBounds());
        Plane.width = Plane.player.getBounds().width;
        Plane.height = Plane.player.getBounds().height;
        Plane.player.x = (stage.canvas.width - Plane.width) / 2;
        Plane.player.y = stage.canvas.height - Plane.height;
        stage.addChildAt(Plane.player, 0);
    },
    //建立敵機
    buildEnemy: function() {
        var e1, e2, e3, e4;
        e1 = new createjs.Sprite(buildGame.spriteSheet, "enemy1");
        e2 = new createjs.Sprite(buildGame.spriteSheet, "enemy2");
        e3 = new createjs.Sprite(buildGame.spriteSheet, "enemy3");
        e4 = new createjs.Sprite(buildGame.spriteSheet, "enemy4");
        enemyPlane.enemyClip.push(e1, e2, e3, e4);
    },
遊戲背景就更簡單了,隨機生成200個小圓點散落在場景中。
//星空背景
    buildSpace: function() {
        var i, star, starSky, w, h, alpha;
        for (i = 0; i < 200; i++) {
            starSky = new createjs.Container();
            star = new createjs.Shape();
            w = Math.floor(Math.random() * stage.canvas.width);
            h = Math.floor(Math.random() * stage.canvas.height);
            alpha = Math.random();
            star.graphics.beginFill("#45D0DE").drawCircle(0, 0, 2);
            star.x = w;
            star.y = h;
            star.alpha = alpha;
            starSky.addChild(star);
            updateGame.starSpace.push(star);
            stage.addChild(starSky);
        }
    },

敵機從種類陣列中隨機克隆種類放入編隊陣列,以編隊的形式進入場景。

//敵機物件
var enemyPlane = {
    //敵機種類陣列
    enemyClip: [],
    //敵機佇列陣列
    enemysQueue: [],
    //敵機增加時間(速度)
    addSpeed: 1000,
    //敵機移動時間(速度)
    moveSpeed: 5000,
    //敵機佇列數量
    enemynNum: 15,
    //隨機新增一類敵機進入戰場
    addEnemy: function() {
        var random = Math.floor((Math.random() * enemyPlane.enemyClip.length));
        //克隆隨機種類敵機放入佇列陣列
        var randomPlane = enemyPlane.enemyClip[random].clone();
        var randomPlaneWidth = randomPlane.getBounds().width;
        randomPlane.x = Math.floor((Math.random() * (stage.canvas.width - randomPlaneWidth * 2)));
        randomPlane.y = -100;

        enemyPlane.enemysQueue.push(randomPlane);

        stage.addChild(randomPlane);
    }
};

玩家飛機通過按鍵發射子彈,同時將發射的子彈插入一個數組。

//飛機物件
var Plane = {
    lives: 3,
    score: 0,
    speed: 9,
    player: {},
    width: 0,
    height: 0,
    fires: [],
    //發射子彈
    playFire: function() {
        if (updateGame.run) {
            var fire = new createjs.Shape();
            fire.graphics.beginFill('#FF0').drawRect(0, 0, 6, 6).endFill();
            fire.x = Plane.player.x - 3 + Plane.width / 2;
            fire.y = Plane.player.y;
            createjs.Sound.play('shot');
            Plane.fires.push(fire);
            stage.addChild(fire);
        }
    }
};
遊戲執行時迴圈子彈陣列和敵機編隊陣列就可以實現碰撞檢測的邏輯了,把發生碰撞的子彈與敵機從場景清除並播放爆炸動畫與音效,同時計分等。
    //子彈與敵機的碰撞檢測
    checkCollision: function() {
        for (var i = 0; i < Plane.fires.length; i++) {
            var fire = Plane.fires[i];
            for (var j = 0; j < enemyPlane.enemysQueue.length; j++) {
                var enemy = enemyPlane.enemysQueue[j];
                var fx = fire.x;
                var fy = fire.y;
                var ex = enemy.x;
                var ey = enemy.y;
                var ew = enemy.getBounds().width;
                var eh = enemy.getBounds().height;
                //判斷子彈有沒有進入敵機矩形區域內
                var pt = fy < ey + eh && fy > ey && fx > ex && fx < ex + ew && ey > 0;
                if (pt) {
                    Plane.score += 10;
                    Plane.fires.splice(i, 1);
                    enemyPlane.enemysQueue.splice(j, 1);
                    stage.removeChild(fire);
                    stage.removeChild(enemy);
                    createjs.Sound.play('explosion');
                    var exp1 = new createjs.Sprite(buildGame.spriteSheet, 'exp');
                    exp1.x = enemy.x;
                    exp1.y = enemy.y;
                    stage.addChild(exp1);
                    //爆炸動畫結束時從舞臺清除
                    exp1.addEventListener('animationend', function(e) {
                        stage.removeChild(e.target);
                    });
                };
            };
        };
    },
由於createjs提供的碰撞檢測方法並不是特別好用,所以這裡實現的並不算太好。還剩下的一些邏輯程式碼就隨便貼一下吧。
    //子彈飛行
    updateFire: function() {
        for (var i = 0; i < Plane.fires.length; i++) {
            var nextY = Plane.fires[i].y - 10;
            if (nextY <= 0) { //如果子彈飛出場景,在子彈陣列中去掉,並在stage中刪除元素
                console.log(Plane.fires[i]);
                stage.removeChild(Plane.fires[i]);
                Plane.fires.splice(i, 1);
                continue;
            }
            Plane.fires[i].y = nextY;
        };
    },
    //敵機飛行
    updateEnemy: function() {
        var pl = Plane.player;
        var plx = Plane.player.x;
        var ply = Plane.player.y;
        var plw = Plane.player.getBounds().width;
        var plh = Plane.player.getBounds().height;

        //佇列內的敵機依次出現
        for (var i = 0; i < enemyPlane.enemysQueue.length; i++) {
            var ep = enemyPlane.enemysQueue[i];
            var randomPlaneWidth = ep.getBounds().width;
            var randomPlaneHeight = ep.getBounds().height;

            createjs.Tween.get(ep).wait(enemyPlane.addSpeed * i).to({
                x: Math.floor((Math.random() * (stage.canvas.width - randomPlaneWidth * 2))),
                y: stage.canvas.height
            }, enemyPlane.moveSpeed, createjs.Ease.sineInOut(-2));

            // 敵機飛出場景移除
            if (ep.x >= stage.canvas.width || ep.y >= stage.canvas.height) {
                enemyPlane.enemysQueue.splice(ep, 1);
                stage.removeChild(ep);
                console.log(enemyPlane.enemysQueue);
                continue;
            }
            //玩家飛機與敵機碰撞處理
            var pz_x = Math.abs(ep.x - plx) <= randomPlaneWidth / 2 + plw / 2;
            var pz_y = Math.abs(ep.y - ply) <= randomPlaneHeight / 2 + plh / 2;
            if (pz_x && pz_y && updateGame.run) {
                Plane.lives--;
                createjs.Sound.play('explosion');
                var exp1 = new createjs.Sprite(buildGame.spriteSheet, 'exp');
                exp1.x = plx;
                exp1.y = ply;
                stage.addChild(exp1);
                //爆炸動畫結束時從舞臺清除
                exp1.addEventListener('animationend', function(e) {
                    stage.removeChild(e.target);

                    if (Plane.lives == 0) {
                        createjs.Ticker.setPaused(true); //暫停遊戲
                        alert('遊戲結束');
                    } else {
                        var setTime = setTimeout(function() {
                            buildGame.buildPlayer();
                            updateGame.run = true;
                            clearTimeout(setTime);
                        }, 1000);
                    };

                });
                stage.removeChild(Plane.player);
                updateGame.run = false;
                break;
            };
        };
        //如果佇列內沒有敵機新增敵機
        if (enemyPlane.enemysQueue.length == 0) {
            for (var i = 0; i < enemyPlane.enemynNum; i++) {
                enemyPlane.addEnemy();
            };
        };
    },
最終執行效果,還算勉強能玩。