一、前言
今天我們來講一個最最最常見的一個小游戲——打飛機!是的,打飛機!還記得小時候在玩兒的雷電,應該是打飛機最早的樣子了吧。直到現在,也有微信打飛機,全民飛機大戰,全民打飛機等游戲的出現,這些游戲基本都是在我們小時候玩兒的打飛機的原型上增加特效音效以及更多新的玩兒法,在這基礎上進行的創新。 其實作為開發者來說,仔細看這款游戲,也並不是什么高大上的游戲,其他很多元素、邏輯,都是我們常見的,比如滾動背景,定時產生敵人等。真正開發一款打飛機游戲,其實只要一天就夠了,但這個游戲的經典玩法卻是很難超越的,在這里,我不得不向這款游戲的原創致敬。
二、游戲分析
游戲中的元素基本可以確定,有玩家,三種敵機,兩種道具,子彈和滾動的背景。而游戲的場景,也就是開始場景,游戲場景,幫助場景(這個都是可有可無的),暫停場景和結束場景。游戲中的邏輯其實也很簡單,無非就是靈活運用定時器,定時產生敵人,定時產生子彈,定時產生道具,再然后就是玩家與道具,玩家與敵人,子彈與敵人的碰撞檢測。我們可以把以上提到的元素全部抽象成類,每個類封裝自己的實現接口,在游戲場景中調用相應類的對應的方法即可。
三、游戲實現
1.全局類
游戲中所有用到的常量都可以放在一個全局類中,在實際開發中可根據需求動態修改,也方便管理維護。
代碼如下:
/**
* 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.背景類
游戲中用到的背景也可以提出來為一個背景類,游戲中的背景是滾動的,滾動背景的實現方式就是用兩張相同的圖片拼接起來,不斷往下移動,當下面那張完全移出屏幕的時候,再將這張圖片移動到前一張的上面,這樣一直循環往復,就形成了不斷滾動的背景。實現代碼如下:
/**
* 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.敵機類
敵機分為三種,小型飛機,中型飛機和大型飛機,三種飛機的血量,速度都不相同,其中,大型飛機還能在每三秒創建三個小型飛機。敵機同樣包含移動,擊中和爆炸方法。
代碼如下:
/**
* 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.開始場景
開始場景是游戲的入口場景,進入之后首先進入開始場景,開始場景包含開始游戲按鈕和幫助按鈕,點擊幫助按鈕進入幫助場景,點擊開始按鈕進入游戲場景。開始場景還包含一個滾動的背景圖和一個載入動畫。
開始場景的效果如下:
代碼如下:
/**
* 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);
}
});
四、運行效果
最后的運行效果如下
通過CVP平台的項目托管可看到實際運行效果,地址如下:
http://www.cocoscvp.com/usercode/3d1775ea3aea1f12e986b7d8ebbb079fca12c064/
五、源代碼
所有源代碼均上傳到github,歡迎交流學習,地址:
https://github.com/hjcenry/plane