打飛機(jī)這個(gè)游戲并不是我原創(chuàng)兽泣,只是拿來(lái)學(xué)習(xí)使用怀读。望原作者見(jiàn)諒诉位。剛剛開(kāi)始是強(qiáng)烈建議簡(jiǎn)書(shū)可以上傳源代碼的,但是后來(lái)一想菜枷,已經(jīng)有了github了可以直接鏈接過(guò)去苍糠,都不用簡(jiǎn)書(shū)浪費(fèi)資源了。哈哈哈哈啤誊。我講要分析的項(xiàng)目上傳到了我的github倉(cāng)庫(kù):github AirBarons
游戲玩耍地址:http://112.126.90.18/airbarons/
先看一下AirBarons目錄結(jié)構(gòu)岳瞭。
- project.json 項(xiàng)目的一些配置信息
"project_type": "javascript",
"debugMode" : 1,
"showFPS" : true,
"frameRate" : 60,
"id" : "gameCanvas",
"renderMode" : 0,
"engineDir":"frameworks/cocos2d-html5",
"modules" : ["cocos2d"],
這些前一篇已經(jīng)說(shuō)過(guò)了。
看下AirBarons項(xiàng)目用到了哪些js文件
"jsList" : [
"src/resource.js",
"src/config/GameConfig.js",
"src/config/EnemyType.js",
"src/config/Level.js",
"src/mainMenu/scene/MainMenu.js",
"src/mainMenu/layer/MMBackgroundLayer.js",
"src/mainMenu/layer/MMMainMenuLayer.js",
"src/mainMenu/layer/MMTouchLayer.js",
"src/setting/scene/Setting.js",
"src/setting/layer/STBackgroundLayer.js",
"src/setting/layer/STTouchLayer.js",
"src/about/scene/About.js",
"src/about/layer/ABBackgroundLayer.js",
"src/about/layer/ABTouchLayer.js",
"src/gamePlay/classes/LevelManager.js",
"src/gamePlay/scene/GamePlay.js",
"src/gamePlay/layer/GPBackgroundLayer.js",
"src/gamePlay/layer/GPTouchLayer.js",
"src/gamePlay/sprite/ShipSprite.js",
"src/gamePlay/sprite/BulletSprite.js",
"src/gamePlay/sprite/EnemySprite.js",
"src/gamePlay/sprite/ExplosionSprite.js",
"src/gamePlay/sprite/SparkEffectSprite.js",
"src/gameOver/scene/GameOver.js",
"src/gameOver/layer/GOBackgroundLayer.js",
"src/gameOver/layer/GOTouchLayer.js"
]
基本概念之<b>resource</b>
看到加載的第一個(gè)文件就是 "src/resource.js",典型的js文件蚊锹。定義了res對(duì)象瞳筏,里面存儲(chǔ)的是圖片和聲音的地址對(duì)象,最后將res對(duì)象的屬性都放到了全局g_resources數(shù)組中了
var res = {
HelloWorld_png : "res/HelloWorld.png",
CloseNormal_png : "res/CloseNormal.png",
CloseSelected_png : "res/CloseSelected.png",
// shared
sh_arial_14_fnt : 'res/shared/arial-14.fnt',
sh_arial_14_png : 'res/shared/arial-14.png',
// mainMenu
TextureTransparentPack_plist : "res/mainMenu/textureTransparentPack.plist",
TextureTransparentPack_png : "res/mainMenu/textureTransparentPack.png",
mm_bg_png : "res/mainMenu/bg.png",
mm_logo_png : "res/mainMenu/logo.png",
mm_mune_png : "res/mainMenu/menu.png",
mm_flare_jpg : "res/mainMenu/flare.jpg",
mm_btnEffect : "res/sound/effect/buttonEffect.mp3",
mm_bgMusic_mp3 : "res/sound/music/mainMainMusic.mp3",
// gamePlay
gp_TextureOpaquePack_plist : "res/gamePlay/textureOpaquePack.plist",
gp_TextureOpaquePack_png : "res/gamePlay/textureOpaquePack.png",
gp_b01_plist : "res/gamePlay/b01.plist",
gp_b01_png : "res/gamePlay/b01.png",
gp_Explosion_plist : "res/gamePlay/explosion.plist",
gp_Explosion_png : "res/gamePlay/explosion.png",
gp_explodeEffect_mp3 : 'res/sound/effect/explodeEffect.mp3',
gp_bgMusic_mp3 : "res/sound/music/bgMusic.mp3",
gp_shipDestroyEffect_mp3 : 'res/sound/effect/shipDestroyEffect.mp3',
// setting
st_menuTitle_png : "res/setting/menuTitle.png",
// gameOver
go_gameOver_png : "res/gameOver/gameOver.png",
go_cocos2d_html5_png : "res/gameOver/cocos2d-html5.png"
};
var g_resources = [];
for (var i in res) {
g_resources.push(res[i]);
}
- 接著是游戲js中用到的配置相關(guān)參數(shù)js的引入
"src/config/GameConfig.js",
"src/config/EnemyType.js",
"src/config/Level.js",
- 接著是到了游戲各個(gè)分類(lèi)的相關(guān)js文件的引入牡昆,該游戲有 <b>設(shè)置</b>姚炕、<b>關(guān)于</b>、<b>游戲</b>和<b>游戲結(jié)束后</b>四個(gè)模塊丢烘。下面看看這四個(gè)模塊都用到了Cocos2d 的哪些基礎(chǔ)概念柱宦。
首先說(shuō)一下js的地址是:api-ref/js/V3.12/
看main.js
cc.game.onStart = function(){
cc.view.adjustViewPort(true);
cc.view.setDesignResolutionSize(320, 480, cc.ResolutionPolicy.SHOW_ALL);
cc.view.resizeWithBrowserSize(true);
cc.LoaderScene.preload(g_resources, function () {
cc.director.runScene(new MainMenuScene());
}, this);
};
cc.game.run();
- <b>cc.game</b>
這個(gè)是An object to boot the game.(啟動(dòng)游戲的對(duì)象)其中有 end(游戲停止)、isPaused(檢查游戲是否停止)播瞳、pause(停止游戲)掸刊、prepare(游戲前準(zhǔn)備)、restart(游戲重新啟動(dòng))
resume(繼續(xù)游戲)赢乓、run(啟動(dòng)游戲) setFrameRate(設(shè)置幀率)忧侧、step(一幀一幀運(yùn)行游戲)這些方法。還有很多屬性onStart就是其中的一個(gè)屬性牌芋,這里將onStart定義成了一個(gè)function蚓炬,在scripts引擎加載完畢后就會(huì)回調(diào)。 - <b>cc.view</b>
在onStart回調(diào)的方法中設(shè)置了cc.view的相關(guān)屬性,cc.view是個(gè)代表了游戲窗口的單例的對(duì)象
這里使用setDesignResolutionSize(width, height, resolutionPolicy)設(shè)置了游戲窗口的大小和屏幕適配的策略躺屁。屏幕大小自適應(yīng)的涉及到cc.ResolutionPolicy试吁,其中有六種方式
EXACT_FIT會(huì)拉伸游戲,充滿(mǎn)整個(gè)屏幕,最簡(jiǎn)單最粗暴熄捍,
SHOW_ALL保持游戲原比例烛恤,讓一邊占滿(mǎn)屏幕,另外一側(cè)黑邊
NO_BORDER跟SHOW_ALL類(lèi)似余耽,但讓短邊占滿(mǎn)屏幕缚柏,另外一側(cè)超出屏幕,不顯示黑邊碟贾,一部分畫(huà)面在屏幕外币喧,無(wú)法顯示,
FIXED_HEIGHT和FIXED_WIDTH都是NO_BORDER的升級(jí)版袱耽,指定那一側(cè)充滿(mǎn)屏幕杀餐,另外一側(cè)超出屏幕,
UNKNOWN 六種方案朱巨。該游戲選擇的是SHOW_ALL史翘、
接著使用resizeWithBrowserSize(enabled)
這個(gè)方法只在web中起作用,canvas 跟著瀏覽器得大小變動(dòng)而自適應(yīng)冀续。
- <b>cc.LoaderScene</b>和<b>cc.director</b>
這里涉及到Node琼讽、Scence、Director洪唐。即節(jié)點(diǎn)钻蹬、導(dǎo)演和場(chǎng)景 ,其實(shí)還有Layer(層)凭需、Sprite(精靈)问欠。節(jié)點(diǎn)是Node是上層的對(duì)象。在Cocos2d-x-3.x引擎中粒蜈,采用節(jié)點(diǎn)樹(shù)形結(jié)構(gòu)來(lái)管理游戲?qū)ο笏诚祝粋€(gè)游戲可以劃分為不同的場(chǎng)景,一個(gè)場(chǎng)景又可以分為不同的層薪伏,一個(gè)層又可以擁有任意個(gè)可見(jiàn)的游戲節(jié)點(diǎn)(即對(duì)象,游戲中基本上所有的類(lèi)都派生于節(jié)點(diǎn)類(lèi)Node)粗仓〖藁常可以執(zhí)行Action來(lái)修改游戲節(jié)點(diǎn)的屬性,使其移動(dòng)借浊、旋轉(zhuǎn)塘淑、放大、縮小等等蚂斤。
看下官網(wǎng)給出的圖即可明白
到眼控制著場(chǎng)景存捺,層屬于場(chǎng)景中的一個(gè)場(chǎng)景。然后精靈有事屬于場(chǎng)景中的一個(gè)東東。精靈的移動(dòng)捌治,旋轉(zhuǎn)岗钩,縮放,執(zhí)行動(dòng)畫(huà)肖油,并接受其他轉(zhuǎn)換構(gòu)成層的東西兼吓,各個(gè)層之間順序執(zhí)行然后構(gòu)成一個(gè)場(chǎng)景。場(chǎng)景之間的切換最終構(gòu)成了一個(gè)游戲森枪,執(zhí)行者就是導(dǎo)演视搏。
cc.LoaderScene.preload了g_resources后傳入回調(diào)函數(shù)
function () {
cc.director.runScene(new MainMenuScene());
}回調(diào)函數(shù)是導(dǎo)演執(zhí)行了第一個(gè)場(chǎng)景。
再傳入當(dāng)前的對(duì)象 this);
好了看new出的第一個(gè)場(chǎng)景 MainMenuScene 看一下場(chǎng)景定義的規(guī)范
var MainMenuScene = cc.Scene.extend({
//this._super();重寫(xiě)后這個(gè)方法一定要記得寫(xiě)上县袱,要不然導(dǎo)致后續(xù)不會(huì)執(zhí)行
//這里是場(chǎng)景的各個(gè)函數(shù)的重寫(xiě) 場(chǎng)景是是繼承自node的所以node中方法也可以被重寫(xiě)浑娜,其中這里有onEnter方法,這個(gè)方法是不需要主動(dòng)調(diào)用的式散。
官方給出的解釋Event callback that is invoked every time when CCNode enters the 'stage'. 可以看出 這個(gè)方法會(huì)每次都被調(diào)用 當(dāng)進(jìn)入到stage后.
var layer = new MainMenuLayer();
this.addChild(layer);這個(gè)場(chǎng)景只有一個(gè)層筋遭,最后將這個(gè)層假如到這個(gè)場(chǎng)景,由于只有一個(gè)層所以都不需要指定執(zhí)行的順序杂数。
})
下面看MainMenuLayer宛畦,層的規(guī)范定義是
var MainMenuLayer = cc.Layer.extend({
// 這里可以聲明屬性
_backgroundLayer : null,
_touchLayer : null,
ctor方法相當(dāng)于是構(gòu)造方法,你在new這個(gè)層的時(shí)候就會(huì)被調(diào)用揍移。
構(gòu)造方法中又可以調(diào)用其他的方法
這里是調(diào)用了addBackgroundLayer和addTouchLayer可以看出是加了兩個(gè)子層到該層中次和,這個(gè)是按加入的順序執(zhí)行的。
})
看一下假如的MMBackgroundLayer和MMTouchLayer層那伐,
MMBackgroundLayer是創(chuàng)建了一個(gè)背景層踏施,這個(gè)和上一個(gè)所講的層基本一致。里面涉及到了精靈的創(chuàng)建
this._sptBg = new cc.Sprite(res.mm_bg_png);
this._sptBg.attr({
anchorX : 0.5,
anchorY : 0.5,
x: GC.w_2,
y: GC.h_2
});
可以看出精靈的創(chuàng)建方式 可以指定屬性一個(gè)其中有anchorPoint 錨點(diǎn)坐標(biāo)罕邀,默認(rèn)是 (0.5, 0.5) 這就說(shuō)明這個(gè)精靈位于層的中央畅形。x,y是寬和高的概念。
這里涉及到cocos2d的坐標(biāo)概念
總之openGL的坐標(biāo)和UI的坐標(biāo)是不一致的诉探,按openGL坐標(biāo)來(lái)就可以了
坐標(biāo)中還有模型坐標(biāo)和世界坐標(biāo)的概念 其實(shí)就是精靈相對(duì)的坐標(biāo)概念
![Upload Paste_Image.png failed. Please try again.]
在游戲場(chǎng)景中有兩個(gè)Node對(duì)象日熬,其中Node1的坐標(biāo)是(400, 500),大小是300 x 100像素肾胯。Node2是放置在Node1中的竖席,它對(duì)于Node1的模型坐標(biāo)是(0, 0),大小是150 x 50像素敬肚。
好了繼續(xù)到MMTouchLayer了毕荐,在該層中 用到了cc.audioEngine去播放音樂(lè)。
/ 播放背景音樂(lè)艳馒,true代表循環(huán)無(wú)限次播放憎亚,false表示只播放一次。
if (GC.SOUND_ON){
if (cc.audioEngine.isMusicPlaying()){
return;
}
cc.audioEngine.playMusic(res.mm_bgMusic_mp3, true);
}
然后又去加載了cc.Menu
// 菜單。 對(duì)應(yīng)三者關(guān)系:菜單里面有菜單項(xiàng)第美,菜單項(xiàng)中綁定要執(zhí)行的方法蝶锋,并且需要圖片去顯示。圖片就是精靈
var menu = new cc.Menu(newGame, gameSettings, about);
menu.alignItemsVerticallyWithPadding(10);
menu.x = GC.w_2;
menu.y = GC.h_2 - 80;
this.addChild(menu, 1, 2);
構(gòu)造方法傳入的是cc.MenuItemSprite 看其中的一個(gè)MenuItemSprite 定義
var newGame = new cc.MenuItemSprite(
newGameNormal,
newGameSelected,
newGameDisabled,
function(){
this.onButtonEffect();
this.flareEffect(flare, this, this.onNewGame);
}.bind(this)
);
里面構(gòu)造方法傳入的也是各個(gè)精靈斋日,精靈的創(chuàng)建中用到了cc.rect牲览,看其中的一個(gè)。
// 根據(jù)rect區(qū)域去創(chuàng)建一個(gè)精靈恶守,作為下面menuItemSprite顯示的圖片第献。
// 因?yàn)閙enuItem有Normal、Selected兔港、Disabled三個(gè)狀態(tài)庸毫,所以一個(gè)菜單項(xiàng)需要三張紋理圖片
var newGameNormal = new cc.Sprite(res.mm_mune_png, cc.rect(0, 0, 126, 33));
cc.rect(0, 0, 126, 33));是指定cc.Rect(x, y, width, height)。
這樣的話就渲染出來(lái)了兩個(gè)層衫樊,一個(gè)背景層飒赃,一個(gè)菜單層。在菜單切換的過(guò)程中還涉及到了 動(dòng)作 科侈,函數(shù)的回調(diào) 载佳、按順序執(zhí)行一組動(dòng)作、 同時(shí)執(zhí)行一組動(dòng)作
// 定義動(dòng)作
var opacityAnim = cc.fadeIn(0.5, 255);
var opacDim = cc.fadeIn(1, 0);
// 為動(dòng)作加上easing效果臀栈,具體參考tests里面的示例
var biggerEase = cc.scaleBy(0.7, 1.2, 1.2).easing(cc.easeSineOut());
var easeMove = cc.moveBy(0.5, cc.p(328, 0)).easing(cc.easeSineOut());
var rotateEase = cc.rotateBy(2.5, 90).easing(cc.easeExponentialOut());
var bigger = cc.scaleTo(0.5, 1);
// 函數(shù)回調(diào)動(dòng)作
var onComplete = cc.callFunc(callback, target);
var killflare = cc.callFunc(function () {
this.getParent().removeChild(this,true);
}, flare);
// 按順序執(zhí)行一組動(dòng)作
var seqAction = cc.sequence(opacityAnim, biggerEase, opacDim, killflare, onComplete);
// 同時(shí)執(zhí)行一組動(dòng)作
var action = cc.spawn(seqAction, easeMove, rotateEase, bigger);
flare.runAction(action);
}
動(dòng)作比較簡(jiǎn)單的蔫慧。 就是精靈的旋轉(zhuǎn)放大縮小等。
cc.callFunc是去執(zhí)行一個(gè)函數(shù)权薯,這里有this.getParent().removeChild方法姑躲,其實(shí)就是去移除創(chuàng)建的層。
cc.sequence是按順序執(zhí)行一組動(dòng)作盟蚣,傳給 cc.spawn 是組合一組動(dòng)作黍析,然后精靈去執(zhí)行 flare.runAction(action);
主要看一下GamePlayScene 游戲玩耍這個(gè)層,這個(gè)層里面涉及到了基礎(chǔ)概念cc.spriteFrameCache緩存
cc.spriteFrameCache.addSpriteFrames(res.gp_TextureOpaquePack_plist);
直接這樣調(diào)用就可以了屎开。這個(gè)層假如了兩個(gè)層GPTouchLayer和GPBackgroundLayer
GPBackgroundLayer比較簡(jiǎn)單就是放了一張背景圖片而已阐枣。主要的碰撞動(dòng)作在GPTouchLayer中
這個(gè)里面涉及到比較多和重要的幾個(gè)概念。scheduleUpdate和schedule
scheduleUpdate相當(dāng)于是調(diào)用層的update方法
// 游戲時(shí)時(shí)刷新
update:function (dt) {
if (this._state == STATE_PLAYING) {
// UI在這邊更新
this.updateUI();
// 敵人在這里面產(chǎn)生奄抽,以及界面上
this.moveActiveUnit(dt);
// 碰撞檢測(cè)
this.checkIsCollide();
// 檢測(cè)我們的飛船重生
this.checkIsReborn();
// 這個(gè)部分被我直接干掉了蔼两。。如孝。因?yàn)橄芰ǎ貜?fù)代碼娩贷,沒(méi)什么內(nèi)容
// this._movingBackground(dt);
}
},
schedule是一致去執(zhí)行一個(gè)方法這里一直在執(zhí)行分?jǐn)?shù)計(jì)數(shù)方法第晰。
// 分?jǐn)?shù)在這里面加
scoreCounter:function () {
if (this._state == STATE_PLAYING) {
this._time++;
this._levelManager.loadLevelResource(this._time);
}
},
存放分?jǐn)?shù)信息的是cc.LabelBMFont
this._lbScore = new cc.LabelBMFont("Score: 0", res.sh_arial_14_fnt);
this._lbScore.attr({
anchorX: 1,
anchorY: 0,
x: GC.w - 5,
y: GC.h - 30
});
這個(gè)里面還有一個(gè)比較重要的就是碰撞檢測(cè)
利用坐標(biāo)是否落在區(qū)域去檢測(cè)
看代碼
// 碰撞堅(jiān)持
collide:function (a, b) {
var ax = a.x;
var ay = a.y;
var bx = b.x;
var by = b.y;
if (Math.abs(ax - bx) > MAX_CONTAINT_WIDTH || Math.abs(ay - by) > MAX_CONTAINT_HEIGHT)
return false;
var aRect = a.collideRect(ax, ay);
var bRect = b.collideRect(bx, by);
return cc.rectIntersectsRect(aRect, bRect);
},
這里面分了不同種類(lèi)的精靈BulletSprite、EnemySprite、ExplosionSprite茁瘦、ShipSprite品抽、SparkEffectSprite看名字就可以看出來(lái)各個(gè)代表什么精靈,既然有碰撞檢測(cè)那么坐標(biāo)移動(dòng)在哪里呢甜熔,其實(shí)就是在各個(gè)精靈中自己定義的action
看BulletSprite這個(gè)精靈吧圆恤,
update:function (dt) {
// cc.log("這里。腔稀。盆昙。");
var y = this.y;
this.y = y - this.yVelocity * dt; //不斷的移動(dòng)坐標(biāo)去達(dá)到移動(dòng)的目的
if (y < 0 || y > GC.h + 10 || this.HP <= 0) {
this.destroy();
}
},
其中還有一些別的方法 死亡啊,新產(chǎn)生啊之類(lèi)的焊虏,這個(gè)就是碰撞檢測(cè)后的邏輯代碼淡喜,不涉及到基礎(chǔ)知識(shí)了,這里就過(guò)了诵闭。
真不容易寫(xiě)完了炼团。這里貼出來(lái)試玩地址
http://112.126.90.18/airbarons/
算了還是放頂部吧
go home。