Cocos2d-JS實現的打飛機

1、前言

今天咱們來說一個最最最多見的一個小遊戲——打飛機!是的,打飛機!還記得小時候在玩兒的雷電,應該是打飛機最先的樣子了吧。直到如今,也有微信打飛機,全民飛機大戰,全民打飛機等遊戲的出現,這些遊戲基本都是在咱們小時候玩兒的打飛機的原型上增長特效音效以及更多新的玩兒法,在這基礎上進行的創新。 其實做爲開發者來講,仔細看這款遊戲,也並非什麼高大上的遊戲,其餘不少元素、邏輯,都是咱們常見的,好比滾動背景,定時產生敵人等。真正開發一款打飛機遊戲,其實只要一天就夠了,但這個遊戲的經典玩法倒是很難超越的,在這裏,我不得不向這款遊戲的原創致敬。javascript

2、遊戲分析

遊戲中的元素基本能夠肯定,有玩家,三種敵機,兩種道具,子彈和滾動的背景。而遊戲的場景,也就是開始場景,遊戲場景,幫助場景(這個都是無關緊要的),暫停場景和結束場景。遊戲中的邏輯其實也很簡單,無非就是靈活運用定時器,定時產生敵人,定時產生子彈,定時產生道具,再而後就是玩家與道具,玩家與敵人,子彈與敵人的碰撞檢測。咱們能夠把以上提到的元素所有抽象成類,每一個類封裝本身的實現接口,在遊戲場景中調用相應類的對應的方法便可。java

3、遊戲實現

1.全局類

遊戲中全部用到的常量均可以放在一個全局類中,在實際開發中可根據需求動態修改,也方便管理維護。
代碼以下:git

/**
 * Created by Henry on 16/7/6.
 */
var Global = {
    // 子彈移動速度
    bulletSpeed:10,
    // 敵機移動速度
    enemySpeed:function(type){
        switch(type){
            case 1:
                return 5;
            break;
            case 2:
                return 3;
            break;
            case 3:
                return 2;
            break;
        };
    },
    // 敵機創造速度
    createEnemySpeed:function(type){
        switch(type){
            case 1:
                return 1;
            break;
            case 2:
                return 3;
            break;
            case 3:
                return 5;
            break;
        };
    },
    // 敵機生命
    enemyHp:function(type){
        switch(type){
            case 1:
                return 1;
            break;
            case 2:
                return 5;
            break;
            case 3:
                return 10;
            break;
        };
    },
    // 道具移動速度
    toolSpeed:function(type){
        switch(type){
            case 1:
                return 2;
            break;
            case 2:
                return 3;
            break;
        };
    },
    // 道具創造速度
    createToolSpeed:function(type){
        switch(type){
            case 1:
                return 30;
            break;
            case 2:
                return 50;
            break;
        };
    },
    // 射擊速度
    shootSpeed:0.2,
    // 雙倍射擊時長
    doubleShootTimes:100
};

2.背景類

遊戲中用到的背景也能夠提出來爲一個背景類,遊戲中的背景是滾動的,滾動背景的實現方式就是用兩張相同的圖片拼接起來,不斷往下移動,當下面那張徹底移出屏幕的時候,再將這張圖片移動到前一張的上面,這樣一直循環往復,就造成了不斷滾動的背景。實現代碼以下:github

/**
 * Created by Henry on 16/7/6.
 */
 var Background = cc.Sprite.extend({
     ctor: function (isOver) {
        if(isOver){
            this._super();
            var bg = new cc.Sprite(cc.spriteFrameCache.getSpriteFrame("gameover.png"));
            bg.setScale(750/480);
            this.addChild(bg);
        }else{
            this._super();
            this.setScale(750/480);
            // 滾動背景圖1
            var menuBg1 = new cc.Sprite(cc.spriteFrameCache.getSpriteFrame("background.png"));
            menuBg1.setPosition(cc.winSize.width / 2, cc.winSize.height / 2);
            menuBg1.setScale(750 / 480);
            menuBg1.setTag(1);
            this.addChild(menuBg1);
            // 滾動背景圖2
            var menuBg2 = new cc.Sprite(cc.spriteFrameCache.getSpriteFrame("background.png"));
            menuBg2.setPosition(cc.winSize.width / 2, cc.winSize.height / 2 + cc.winSize.height - 4);
            menuBg2.setScale(750 / 480);
            menuBg2.setTag(2);
            this.addChild(menuBg2);
            this.schedule(this.update);
        }
        return true;
     },
     stopMove:function(){
        this.unschedule(this.update);
     },
     update: function () {
         var menuBg1 = this.getChildByTag(1);
         var menuBg2 = this.getChildByTag(2);
         if (menuBg1.getPositionY() <= -cc.winSize.height / 2 + 20) {
             menuBg1.setPositionY(cc.winSize.height / 2 + cc.winSize.height);
         } else {
             menuBg1.setPositionY(menuBg1.getPositionY() - 1);
         }
         if (menuBg2.getPositionY() <= -cc.winSize.height / 2 + 20) {
             menuBg2.setPositionY(cc.winSize.height / 2 + cc.winSize.height);
         } else {
             menuBg2.setPositionY(menuBg2.getPositionY() - 1);
         }
     }
 });

3.子彈類

玩家不斷髮出子彈,子彈類須要包含移動和移除兩個方法,子彈一旦被建立,就按照一個軌跡移動,直到碰撞到敵機或飛出屏幕才移除子彈,碰撞檢測在遊戲場景中實現,碰撞以後調用子彈類的移除方法。
代碼以下:數組

/**
 * Created by Henry on 16/7/6.
 */
var Bullet = cc.Sprite.extend({
    gameLayer:null,
    ctor: function (type,isUp,gameLayer) {
        this.gameLayer = gameLayer;
        this._super(cc.spriteFrameCache.getSpriteFrame("bullet"+type+".png"));
        if(isUp){
            this.schedule(this.moveUp);
        }else{
            this.schedule(this.moveDown);
        }
        return true;
    },
    moveUp:function(){
        this.setPositionY(this.getPositionY() + Global.bulletSpeed);
        if(this.getPositionY()>=cc.winSize.height+this.height/2){
            // 飛出屏幕刪除
            this.remove();
        }
    },
    moveDown:function(){
        this.setPositionY(this.getPositionY() - Global.bulletSpeed);
        if(this.getPositionY()<=-this.height/2){
            // 飛出屏幕刪除
            this.remove();
        }
    },
    remove:function(){
        var index = this.gameLayer.bullets.indexOf(this);  
        if (index > -1) {  
            this.gameLayer.bullets.splice(index, 1);  
        }
        this.removeFromParent();
    }
});

4.玩家類

遊戲中的主角就是玩家類,玩家控制玩家類在屏幕中移動飛行,並不斷射擊子彈,玩家類中包含單行子彈射擊和吃完道具以後的雙行子彈射擊兩個方法,玩家還有移動方法,飛入屏幕方法和爆炸方法。同時飛機重複播放飛行動畫。
代碼以下:微信

/**
 * Created by Henry on 16/7/6.
 */
var Player = cc.Sprite.extend({
    lock:true,
    gameLayer:null,
    doubleCount:0,
    ctor: function (gameLayer) {
        this._super(cc.spriteFrameCache.getSpriteFrame("hero1.png"));
        this.gameLayer = gameLayer;
        this.doubleCount = 0;
        this.lock = true;
        // 飛行動畫
        var heroHoldFrames = [
            cc.spriteFrameCache.getSpriteFrame("hero1.png"),
            cc.spriteFrameCache.getSpriteFrame("hero2.png")
        ];
        var holdAnimation = new cc.Animation(heroHoldFrames, 0.1);
        this.runAction(cc.repeatForever(cc.animate(holdAnimation)));
        this.moveIn();
        this.schedule(this.shootSingleBullet,Global.shootSpeed);
        return true;
    },
    shootSingleBullet:function(){
        if (!this.lock) {
            var bullet = new Bullet(2,true,this.gameLayer);
            bullet.setPosition(this.getPositionX(),this.getPositionY()+this.height/2);
            this.gameLayer.addChild(bullet);
            this.gameLayer.bullets.push(bullet);
            // 發射子彈音效
            cc.audioEngine.playEffect("res/sound/bullet.mp3");
        }
    },
    shootDoubleBegin:function(){
        this.unschedule(this.shootSingleBullet);
        this.schedule(this.shootDoubleBullet,Global.shootSpeed-0.1);
        this.doubleCount = 0;
    },
    shootDoubleBullet:function(){
        if (!this.lock) {
            if(this.doubleCount>=Global.doubleShootTimes){
                this.unschedule(this.shootDoubleBullet);
                this.schedule(this.shootSingleBullet,Global.shootSpeed);
            }else{
                this.doubleCount += 1;
                var bulletLeft = new Bullet(2,true,this.gameLayer);
                var bulletRight = new Bullet(2,true,this.gameLayer);
                bulletLeft.setPosition(this.getPositionX()-35,this.getPositionY()+5);
                bulletRight.setPosition(this.getPositionX()+35,this.getPositionY()+5);
                this.gameLayer.addChild(bulletLeft);
                this.gameLayer.addChild(bulletRight);
                this.gameLayer.bullets.push(bulletLeft);
                this.gameLayer.bullets.push(bulletRight);
                // 發射子彈音效
                cc.audioEngine.playEffect("res/sound/bullet.mp3");
            }
        }
    },
    moveIn:function(){
        this.runAction(cc.sequence(
            cc.moveTo(1, cc.p(cc.winSize.width/2, cc.winSize.height/2)),
            cc.moveBy(2, cc.p(0, -400)),
            cc.callFunc(function(player){
                // 播完動畫解鎖操做
                player.lock = false;
            },this,this)
        ));
    },
    moveBy:function(x,y) {
        if (!this.lock) {
            this.setPosition(this.getPositionX() + x, this.getPositionY() + y);
        }
    },
    blowUp:function(){
        this.lock = true;
        // 爆炸動畫
        var blowUpFrames = [
            cc.spriteFrameCache.getSpriteFrame("hero_blowup_n1.png"),
            cc.spriteFrameCache.getSpriteFrame("hero_blowup_n2.png"),
            cc.spriteFrameCache.getSpriteFrame("hero_blowup_n3.png"),
            cc.spriteFrameCache.getSpriteFrame("hero_blowup_n4.png")
        ];
        var blowUpAnimation = new cc.Animation(blowUpFrames, 0.1);
        this.stopAllActions();
        this.runAction(cc.sequence(cc.animate(blowUpAnimation),cc.callFunc(function(hero){
            hero.removeFromParent();
        },this,this)));
    }
});

5.敵機類

敵機分爲三種,小型飛機,中型飛機和大型飛機,三種飛機的血量,速度都不相同,其中,大型飛機還能在每三秒建立三個小型飛機。敵機一樣包含移動,擊中和爆炸方法。
代碼以下:dom

/**
 * Created by Henry on 16/7/6.
 */
var Enemy = cc.Sprite.extend({
    type:1,
    hp:0,
    gameLayer:null,
    ctor: function (type,gameLayer) {
        this.type = type;
        this.hp = Global.enemyHp(type);
        this.gameLayer = gameLayer;
        if(type==3){
            // 大型飛機的動畫
            this._super(cc.spriteFrameCache.getSpriteFrame("enemy"+type+"_n1.png"));
            var holdFrames = [
                cc.spriteFrameCache.getSpriteFrame("enemy"+type+"_n1.png"),
                cc.spriteFrameCache.getSpriteFrame("enemy"+type+"_n2.png")
            ];
            var holdAnimation = new cc.Animation(holdFrames, 0.1);
            this.runAction(cc.repeatForever(cc.animate(holdAnimation)));
        }else{
            this._super(cc.spriteFrameCache.getSpriteFrame("enemy"+type+".png"));
        }
        this.schedule(this.moveDown);
        if(this.type==3){
            // 大飛機每3s產生3個小飛機
            this.schedule(function(){
                this.createEnemy();
            },3);
        }
        return true;
    },
    // 創造三個小型飛機
    createEnemy:function(){
        var enemy1 = new Enemy(1,this.gameLayer);
        var enemy2 = new Enemy(1,this.gameLayer);
        var enemy3 = new Enemy(1,this.gameLayer);
        var x1 = this.getPositionX()-this.width/2+enemy1.width/2;
        var x2 = this.getPositionX();
        var x3 = this.getPositionX()+this.width/2-enemy3.width/2;
        enemy1.setPosition(x1,this.getPositionY());
        enemy2.setPosition(x2,this.getPositionY());
        enemy3.setPosition(x3,this.getPositionY());
        this.gameLayer.addChild(enemy1);
        this.gameLayer.addChild(enemy2);
        this.gameLayer.addChild(enemy3);
        this.gameLayer.enemies.push(enemy1);
        this.gameLayer.enemies.push(enemy2);
        this.gameLayer.enemies.push(enemy3);
    },
    moveDown:function(){
        this.setPositionY(this.getPositionY() - parseInt(Global.enemySpeed(this.type)));
        if(this.getPositionY()<=-this.height/2){
            // 飛出屏幕刪除
            this.remove();
        }
    },
    remove:function(){
        var index = this.gameLayer.enemies.indexOf(this);  
        if (index > -1) {  
            this.gameLayer.enemies.splice(index, 1);  
        }
        this.removeFromParent();
    },
    // 擊中
    hit:function(){
        // 擊中動畫
        this.hp -= 1;
        if(this.hp<=0){
            this.blowUp();
        }else{
            var holdFrame = this.type==3?"enemy"+this.type+"_n1.png":"enemy"+this.type+".png";
            var hitFrames = [
                cc.spriteFrameCache.getSpriteFrame(holdFrame),
                cc.spriteFrameCache.getSpriteFrame("enemy"+this.type+"_hit.png")
            ];
            var hitAnimation = new cc.Animation(hitFrames, 0.1);
            this.runAction(cc.sequence(cc.animate(hitAnimation),cc.callFunc(function(enemy){
                enemy.setSpriteFrame(cc.spriteFrameCache.getSpriteFrame(holdFrame));
            },this)));
        }
    },
    // 爆炸
    blowUp:function(){
        if(this.type==3){
            // 大飛機爆炸產生3個小飛機
            this.createEnemy();
        }
        this.unschedule(this.moveDown);
        var index = this.gameLayer.enemies.indexOf(this);  
        if (index > -1) {  
            this.gameLayer.enemies.splice(index, 1);  
        }
        // 爆炸動畫
        var blowUpFrames = [
            cc.spriteFrameCache.getSpriteFrame("enemy"+this.type+"_down1.png"),
            cc.spriteFrameCache.getSpriteFrame("enemy"+this.type+"_down2.png"),
            cc.spriteFrameCache.getSpriteFrame("enemy"+this.type+"_down3.png"),
            cc.spriteFrameCache.getSpriteFrame("enemy"+this.type+"_down4.png")
        ];
        if(this.type==3){
            blowUpFrames = blowUpFrames.concat([
                cc.spriteFrameCache.getSpriteFrame("enemy"+this.type+"_down5.png"),
                cc.spriteFrameCache.getSpriteFrame("enemy"+this.type+"_down6.png")
            ]);
        }
        // 播放爆炸音效
        if(this.type==3){
            cc.audioEngine.playEffect("res/sound/enemy3_down.mp3");
        }else{
            cc.audioEngine.playEffect("res/sound/enemy1_down.mp3");
        }
        var blowUpAnimation = new cc.Animation(blowUpFrames, 0.1);
        this.stopAllActions();
        this.runAction(cc.sequence(cc.animate(blowUpAnimation),cc.callFunc(function(enemy){
            enemy.removeFromParent();
        },this,this)));
        // 加分
        this.gameLayer.addScore(this.type);
    }
});

6.道具類

遊戲中會定時掉落道具,道具類包含向下移動方法,移除方法和旋轉方法。道具與玩家的碰撞檢測在遊戲場景中實現。
代碼以下:學習

/**
 * Created by Henry on 16/7/6.
 */
var Tool = cc.Sprite.extend({
    gameLayer:null,
    type:0,
    ctor: function (type,gameLayer) {
        this._super(cc.spriteFrameCache.getSpriteFrame("ufo"+type+".png"));
        this.type = type;
        this.gameLayer = gameLayer;
        // 旋轉特效
        this.rotate();
        // 向下移動
        this.schedule(this.moveDown);
        return true;
    },
    moveDown:function(){
        this.setPositionY(this.getPositionY()-Global.toolSpeed(this.type));
        if(this.getPositionY()<=-this.height/2){
            // 飛出屏幕刪除
            this.remove();
        }
    },
    remove:function(){
        var index = this.gameLayer.tools.indexOf(this);  
        if (index > -1) {  
            this.gameLayer.tools.splice(index, 1);  
        }
        this.runAction(cc.sequence(cc.scaleTo(0.1,0),cc.callFunc(function(tool){
            tool.removeFromParent();
        },this,this)));
    },
    rotate:function(){
        var rotateAction = cc.repeatForever(cc.sequence(cc.rotateTo(1,30),cc.sequence(cc.rotateTo(1,-30))));
        this.runAction(rotateAction);
    }
});

7.載入類

載入類只是在開始場景中的一個載入的循環播放的動畫。
代碼以下:動畫

/**
 * Created by Henry on 16/7/6.
 */
var Loading = cc.Sprite.extend({
    ctor: function () {
        this._super(cc.spriteFrameCache.getSpriteFrame("game_loading1.png"));
        var loadingFrames = [
            cc.spriteFrameCache.getSpriteFrame("game_loading1.png"),
            cc.spriteFrameCache.getSpriteFrame("game_loading2.png"),
            cc.spriteFrameCache.getSpriteFrame("game_loading3.png"),
            cc.spriteFrameCache.getSpriteFrame("game_loading4.png")
        ];
        var loadingAnimation = new cc.Animation(loadingFrames, 0.5);
        this.runAction(cc.repeatForever(cc.animate(loadingAnimation)));
        return true;
    }
});

8.開始場景

開始場景是遊戲的入口場景,進入以後首先進入開始場景,開始場景包含開始遊戲按鈕和幫助按鈕,點擊幫助按鈕進入幫助場景,點擊開始按鈕進入遊戲場景。開始場景還包含一個滾動的背景圖和一個載入動畫。
開始場景的效果以下:
開始場景
代碼以下:this

/**
 * Created by Henry on 16/7/6.
 */
var MenuLayer = cc.Layer.extend({
    loadCount: 1,
    ctor: function () {
        this._super();
        this.loadCount = 1;
        // 加載plist
        cc.spriteFrameCache.addSpriteFrames(res.shoot_background_plist);
        // 添加背景圖
        var bg = new Background(false);
        bg.setPosition(0,0);
        this.addChild(bg);
        // logo
        var copyright = new cc.Sprite(cc.spriteFrameCache.getSpriteFrame("shoot_copyright.png"));
        copyright.setPosition(cc.winSize.width / 2, cc.winSize.height / 2 + 270);
        copyright.runAction(cc.repeatForever(cc.sequence(cc.scaleTo(1, 1.5), cc.scaleTo(1, 1))));
        this.addChild(copyright);
        // 遊戲按鈕
        var startBtn = new cc.MenuItemSprite(
            new cc.Sprite("res/game_start.png"),
            new cc.Sprite("res/game_start_selected.png"),
            function () {
                cc.audioEngine.playEffect("res/sound/button.mp3");
                cc.director.runScene(new cc.TransitionFade(1, new GameScene()));
            }, this);
        var helpBtn = new cc.MenuItemSprite(
            new cc.Sprite("res/game_help.png"),
            new cc.Sprite("res/game_help_selected.png"),
            function () {
                cc.audioEngine.playEffect("res/sound/button.mp3");
                cc.director.runScene(new cc.TransitionFade(1, new HelpScene()));
            }, this);
        helpBtn.setPositionY(startBtn.getPositionY() - startBtn.height / 2 - 100);
        var menu = new cc.Menu(startBtn, helpBtn);
        this.addChild(menu);
        // loading動畫
        var loading = new Loading();
        loading.setPosition(cc.winSize.width/2,200);
        this.addChild(loading);
        return true;
    }
});

var MenuScene = cc.Scene.extend({
    onEnter: function () {
        this._super();
        var layer = new MenuLayer();
        this.addChild(layer);
    }
});

9.遊戲場景

從開始場景點擊開始遊戲按鈕以後進入遊戲場景,遊戲場景包含全部的遊戲邏輯,包括滾動背景,玩家,定時產生的敵機,定時產生的道具,玩家與敵機、敵機與子彈、以及玩家與道具的碰撞檢測,玩家的移動操做,使用爆炸道具等。
遊戲開始以前有一個玩家飛入屏幕的動畫,效果圖以下:
玩家飛入
玩家飛入以後,就能夠定時建立子彈、道具與敵機,遊戲場景的效果圖以下:
遊戲場景
掉落的道具備發射雙排子彈和炸彈兩種,雙排子彈效果圖以下:
雙排子彈
拾取到炸彈道具會累加到左下角的計數中,點擊炸彈就可使用,炸彈可以讓屏幕中全部飛機所有爆炸,效果圖以下:
炸彈
遊戲中的敵機,子彈以及道具,都用數組來存儲,每一幀都要遍歷數組來進行碰撞檢測,但玩家與子彈碰撞,玩家與敵機碰撞,以及敵機與子彈碰撞時,調用相應的類的邏輯,代碼以下:

/**
 * Created by Henry on 16/7/6.
 */
var GameLayer = cc.Layer.extend({
    touchStartX:0,
    touchStartY:0,
    bullets:[],
    enemies:[],
    tools:[],
    ctor: function () {
        this._super();
        this.touchStartX = 0;
        this.touchStartY = 0;
        this.bullets = [];
        this.enemies = [];
        this.tools = [];
        // 播放背景音樂
        cc.audioEngine.playMusic("res/sound/game_music.mp3",true);
        // 加載plist
        cc.spriteFrameCache.addSpriteFrames(res.shoot_background_plist);
        cc.spriteFrameCache.addSpriteFrames(res.shoot_plist);
        // 添加背景圖
        var bg = new Background(false);
        bg.setPosition(0,0);
        this.addChild(bg);
        // 添加飛機
        var player = new Player(this);
        player.setPosition(cc.winSize.width / 2, -player.height / 2);
        this.addChild(player);
        player.setTag(1);
        // 產生敵機
        this.schedule(function(){
            this.createEnemy(1);
        },Global.createEnemySpeed(1));
        this.schedule(function(){
            this.createEnemy(2);
        },Global.createEnemySpeed(2));
        this.schedule(function(){
            this.createEnemy(3);
        },Global.createEnemySpeed(3));
        // 產生道具
        this.schedule(function(){
            this.createTool(1);
        },Global.createToolSpeed(1));
        this.schedule(function(){
            this.createTool(2);
        },Global.createToolSpeed(2));
        // 添加爆炸道具
        var bombNor = new cc.Sprite(cc.spriteFrameCache.getSpriteFrame("bomb.png"));
        var bombSelected = new cc.Sprite(cc.spriteFrameCache.getSpriteFrame("bomb.png"));
        bombSelected.setPosition(-bombSelected.width/4,-bombSelected.height/4);
        bombSelected.setScale(1.5);
        var bombBtn = new cc.MenuItemSprite(
            bombNor,
            bombSelected,
            function () {
                var bombNum = this.getChildByTag(3);
                if(parseInt(bombNum.getString().slice(1))==0){
                    return;
                }
                // 全屏爆炸
                var blowEnemy = [];
                for(var i in this.enemies){
                    var enemy = this.enemies[i];
                    blowEnemy.push(enemy);
                }
                for(var j in blowEnemy){
                    blowEnemy[j].blowUp();
                }
                // 數量減一
                bombNum.setString("X"+(parseInt(bombNum.getString().slice(1))-1));
            }, this);
        bombBtn.setPosition(50+bombBtn.width/2,50+bombBtn.height/2);
        var bombMenu = new cc.Menu(bombBtn);
        bombMenu.setPosition(0,0);
        bombMenu.setAnchorPoint(0,0);
        this.addChild(bombMenu);
        // 爆炸道具數量
        var bombNum = new cc.LabelBMFont("X2",res.font);
        bombNum.setAnchorPoint(0,0.5);
        bombNum.setPosition(bombBtn.getPositionX()+bombBtn.width/2+50,bombBtn.getPositionY());
        bombNum.setTag(3);
        this.addChild(bombNum);
        // 暫停開始按鈕
        var pauseBtn = new cc.MenuItemSprite(
            new cc.Sprite(cc.spriteFrameCache.getSpriteFrame("game_pause_nor.png")),
            new cc.Sprite(cc.spriteFrameCache.getSpriteFrame("game_pause_pressed.png")),
            function () {
                // 暫停音樂音效
                cc.audioEngine.pauseAllEffects();
                cc.audioEngine.pauseMusic();
                pauseBtn.setEnabled(false);
                cc.director.pause();
                this.addChild(new PauseLayer(pauseBtn),10);
            }, this);
        var pauseMenu = new cc.Menu(pauseBtn);
        pauseMenu.setPosition(20+pauseBtn.width/2,cc.winSize.height-pauseBtn.height/2-20);
        pauseMenu.setAnchorPoint(0,0);
        this.addChild(pauseMenu);
        // 分數
        var score = new cc.LabelBMFont("0",res.font);
        score.setAnchorPoint(0,0.5);
        score.setPosition(pauseMenu.getPositionX()+pauseBtn.width/2+50,pauseMenu.getPositionY());
        score.setTag(2);
        this.addChild(score);
        // 碰撞檢測
        this.schedule(this.collision);
        return true;
    },
    collision:function(){
        var bullets = this.bullets;
        var enemies = this.enemies;
        var tools = this.tools;
        var score = parseInt(this.getChildByTag(2).getString());
        for(var i in enemies){
            var enemy = enemies[i];
            // 檢測是否與玩家碰撞
            var player = this.getChildByTag(1);
            if(cc.rectIntersectsRect(enemy.getBoundingBox(),player.getBoundingBox())){
                // 遊戲結束
                this.unschedule(this.collision);
                player.blowUp();
                // 中止背景音樂
                cc.audioEngine.stopMusic("res/sound/game_music.mp3");
                cc.audioEngine.playEffect("res/sound/game_over.mp3");
                this.scheduleOnce(function() {
                    cc.director.runScene(new cc.TransitionFade(1,new OverScene(score)));
                },2);
            }
            // 檢測是否吃到道具
            for(var m in tools){
                var tool = tools[m];
                if(cc.rectIntersectsRect(tool.getBoundingBox(),player.getBoundingBox())){
                    switch(tool.type){
                        case 1:
                            // 雙排子彈道具
                            cc.audioEngine.playEffect("res/sound/get_double_laser.mp3");
                            player.shootDoubleBegin();
                            break;
                        case 2:
                            // 清屏道具
                            cc.audioEngine.playEffect("res/sound/get_bomb.mp3");
                            var bomb = this.getChildByTag(3);
                            bomb.setString("X"+(parseInt(bomb.getString().slice(1))+1));
                            bomb.runAction(cc.sequence(cc.scaleTo(0.1,1.2),cc.scaleTo(0.1,1)));
                            break;
                    }                  
                    tool.remove();
                }
            }
            for(var j in bullets){
                var bullet = bullets[j];
                // 檢測是否與子彈碰撞
                if(cc.rectIntersectsRect(enemy.getBoundingBox(),bullet.getBoundingBox())){
                    enemy.hit();
                    bullet.remove();
                }
            }
        }
    },
    addScore:function(type){
        var score = this.getChildByTag(2);
        var addScore = 0;
        var curScore = parseInt(score.getString());
        switch(type){
            case 1:
                addScore = 100 + Math.ceil(Math.random()*(curScore/1000));
            break;
            case 2:
                addScore = 200 + Math.ceil(Math.random()*(curScore/1000));
            break;
            case 3:
                addScore = 500 + Math.ceil(Math.random()*(curScore/1000));
            break;
        }
        score.setString(curScore+addScore);
    },
    createEnemy:function(type){
        var enemy = new Enemy(type,this);
        var randomX = Math.random()*(cc.winSize.width-enemy.width/2-enemy.width/2)+enemy.width/2;
        enemy.setPosition(randomX,cc.winSize.height+enemy.height/2);
        this.addChild(enemy);
        this.enemies.push(enemy);
    },
    createTool:function(type){
        var tool = new Tool(type,this);
        var randomX = Math.random()*(cc.winSize.width-tool.width/2-tool.width/2)+tool.width/2;
        tool.setPosition(randomX,cc.winSize.height+tool.height/2);
        this.addChild(tool);
        this.tools.push(tool);
    },
    onEnter:function(){
        this._super();
        // 添加觸摸事件
        cc.eventManager.addListener({
            event:cc.EventListener.TOUCH_ONE_BY_ONE,
            swallowTouches:true,
            onTouchBegan:this.touchbegan,
            onTouchMoved:this.touchmoved,
            onTouchEnded:this.touchended
        },this);
        return true;
    },
    touchbegan:function(touch,event){
        event.getCurrentTarget().touchStartX = touch.getLocation().x;
        event.getCurrentTarget().touchStartY = touch.getLocation().y;
        return true;
    },
    touchmoved:function(touch,event){
        var touchX = touch.getLocation().x;
        var touchY = touch.getLocation().y;
        var touchStartX = event.getCurrentTarget().touchStartX;
        var touchStartY = event.getCurrentTarget().touchStartY;
        var player = event.getCurrentTarget().getChildByTag(1);
        if(player!=null){
            player.moveBy(touchX-touchStartX,touchY-touchStartY);
            event.getCurrentTarget().touchStartX = touchX;
            event.getCurrentTarget().touchStartY = touchY;
        }
        return true;
    },
    touchended:function(touch,event){
        return true;
    }
});

var GameScene = cc.Scene.extend({
    onEnter: function () {
        this._super();
        var layer = new GameLayer();
        this.addChild(layer);
    }
});

10.暫停場景

在遊戲場景點擊暫停按鈕進入暫停場景,暫停場景實際上只是一個半透明的層蓋在遊戲場景上,進入暫停場景後遊戲暫停,暫停場景包含繼續按鈕,結束按鈕和從新開始按鈕。
暫停場景效果圖以下:
暫停場景
代碼以下:

/**
 * Created by Henry on 16/7/6.
 */
var PauseLayer=cc.LayerColor.extend({
    ctor:function (pauseBtn) {
        // 初始化爲黑色
        this._super(cc.color(0,0,0,100));
        this.width = cc.winSize.width;
        this.height = cc.winSize.height;
        // 繼續按鈕
        var resumeBtn = new cc.MenuItemSprite(
            new cc.Sprite("res/game_continue.png"),
            new cc.Sprite("res/game_continue_selected.png"),
            function () {
                cc.audioEngine.playEffect("res/sound/button.mp3");
                cc.audioEngine.resumeMusic();
                cc.director.resume();
                pauseBtn.setEnabled(true);
                this.removeFromParent();
            }, this);
        resumeBtn.setPosition(0,100);
        // 結束遊戲按鈕
        var overBtn = new cc.MenuItemSprite(
            new cc.Sprite("res/game_over.png"),
            new cc.Sprite("res/game_over_selected.png"),
            function () {
                cc.audioEngine.playEffect("res/sound/button.mp3");
                cc.audioEngine.stopMusic("res/sound/game_music.mp3");
                cc.director.resume();
                cc.director.runScene(new cc.TransitionFade(1, new MenuScene()));
            }, this);
        overBtn.setPosition(0,0);
        // 從新開始按鈕
        var reagainBtn = new cc.MenuItemSprite(
            new cc.Sprite("res/game_Reagain.png"),
            new cc.Sprite("res/game_Reagain_selected.png"),
            function () {
                cc.audioEngine.playEffect("res/sound/button.mp3");
                cc.audioEngine.stopMusic("res/sound/game_music.mp3");
                cc.director.resume();
                cc.director.runScene(new cc.TransitionFade(1, new GameScene()));
            }, this);
        reagainBtn.setPosition(0,-100);
        var menu = new cc.Menu(resumeBtn,overBtn,reagainBtn);
        this.addChild(menu);
        return true;
    }
});

11.結束場景

在遊戲場景中,若是在碰撞檢測中檢測到玩家與敵機碰撞,則遊戲結束,遊戲結束進入結束場景,結束場景包含玩家的最終分數,以及展現最高歷史分數和保存最高歷史分數。
結束場景的效果以下:
結束場景
代碼以下:

/**
 * Created by Henry on 16/7/6.
 */
var OverLayer=cc.Layer.extend({
    ctor:function (score) {
        this._super();
        var bg = new Background(true);
        bg.setPosition(cc.winSize.width/2,cc.winSize.height/2);
        this.addChild(bg);
        var highest = cc.sys.localStorage.getItem("highest");
        highest = highest==null?0:highest;
        // 分數存儲
        if(parseInt(score)>parseInt(highest)){
            cc.sys.localStorage.setItem("highest" ,score);
            highest = score;
        }
        // 歷史最高分數
        var highestFnt = new cc.LabelBMFont(highest.toString(),res.font);
        highestFnt.setPosition(250,cc.winSize.height-highestFnt.height/2-75);
        highestFnt.setAnchorPoint(0,0.5);
        this.addChild(highestFnt);
        // 分數顯示
        var scoreFnt = new cc.LabelBMFont(score,res.font);
        scoreFnt.setPosition(cc.winSize.width/2,cc.winSize.height/2);
        this.addChild(scoreFnt);
        this.scheduleOnce(function(){
            cc.audioEngine.playEffect("res/sound/out_porp.mp3");
            scoreFnt.runAction(cc.sequence(cc.scaleTo(0.2,1.5),cc.scaleTo(0.2,1)));
        },1);
        // 菜單按鈕
        var restartBtn = new cc.MenuItemSprite(
            new cc.Sprite("res/btn_finish.png"),
            new cc.Sprite("res/btn_finish_selected.png"),
            function () {
                cc.audioEngine.playEffect("res/sound/button.mp3");
                cc.director.runScene(new cc.TransitionFade(1, new MenuScene()));
            }, this);
        restartBtn.setPosition(scoreFnt.getPositionX(),scoreFnt.getPositionY()-scoreFnt.height/2-100);
        var menu = new cc.Menu(restartBtn);
        menu.setPosition(0,0);
        menu.setAnchorPoint(0,0);
        this.addChild(menu);
        return true;
    }
});

var OverScene=cc.Scene.extend({
    ctor:function(score){
        this._super();
        var layer=new OverLayer(score);
        this.addChild(layer);
    }
});

12.幫助場景

幫助場景就是一個展現遊戲幫助提示的場景,其實只有文字和返回遊戲按鈕。
幫助場景的效果以下:
幫助場景
代碼以下:

/**
 * Created by Henry on 16/7/6.
 */
var OverLayer=cc.Layer.extend({
    ctor:function (score) {
        this._super();
        var bg = new Background(true);
        bg.setPosition(cc.winSize.width/2,cc.winSize.height/2);
        this.addChild(bg);
        var highest = cc.sys.localStorage.getItem("highest");
        highest = highest==null?0:highest;
        // 分數存儲
        if(parseInt(score)>parseInt(highest)){
            cc.sys.localStorage.setItem("highest" ,score);
            highest = score;
        }
        // 歷史最高分數
        var highestFnt = new cc.LabelBMFont(highest.toString(),res.font);
        highestFnt.setPosition(250,cc.winSize.height-highestFnt.height/2-75);
        highestFnt.setAnchorPoint(0,0.5);
        this.addChild(highestFnt);
        // 分數顯示
        var scoreFnt = new cc.LabelBMFont(score,res.font);
        scoreFnt.setPosition(cc.winSize.width/2,cc.winSize.height/2);
        this.addChild(scoreFnt);
        this.scheduleOnce(function(){
            cc.audioEngine.playEffect("res/sound/out_porp.mp3");
            scoreFnt.runAction(cc.sequence(cc.scaleTo(0.2,1.5),cc.scaleTo(0.2,1)));
        },1);
        // 菜單按鈕
        var restartBtn = new cc.MenuItemSprite(
            new cc.Sprite("res/btn_finish.png"),
            new cc.Sprite("res/btn_finish_selected.png"),
            function () {
                cc.audioEngine.playEffect("res/sound/button.mp3");
                cc.director.runScene(new cc.TransitionFade(1, new MenuScene()));
            }, this);
        restartBtn.setPosition(scoreFnt.getPositionX(),scoreFnt.getPositionY()-scoreFnt.height/2-100);
        var menu = new cc.Menu(restartBtn);
        menu.setPosition(0,0);install
        menu.setAnchorPoint(0,0);
        this.addChild(menu);
        return true;
    }
});

var OverScene=cc.Scene.extend({
    ctor:function(score){
        this._super();
        var layer=new OverLayer(score);
        this.addChild(layer);
    }
});

4、運行效果

最後的運行效果以下
打飛機效果圖
經過CVP平臺的項目託管可看到實際運行效果,地址以下:
http://www.cocoscvp.com/usercode/3d1775ea3aea1f12e986b7d8ebbb079fca12c064/

5、源代碼

全部源代碼均上傳到github,歡迎交流學習,地址:
https://github.com/hjcenry/plane

相關文章
相關標籤/搜索