Cocos2d-JS連載之打飛機(jī)游戲?qū)W習(xí)基礎(chǔ)概念

打飛機(jī)這個游戲并不是我原創(chuàng),只是拿來學(xué)習(xí)使用。望原作者見諒。剛剛開始是強(qiáng)烈建議簡書可以上傳源代碼的,但是后來一想,已經(jīng)有了github了可以直接鏈接過去,都不用簡書浪費(fèi)資源了。哈哈哈哈。我講要分析的項(xiàng)目上傳到了我的github倉庫:github AirBarons

游戲玩耍地址:http://112.126.90.18/airbarons/
  先看一下AirBarons目錄結(jié)構(gòu)。

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)說過了。
看下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>
看到加載的第一個文件就是 "src/resource.js",典型的js文件。定義了res對象,里面存儲的是圖片和聲音的地址對象,最后將res對象的屬性都放到了全局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",
  • 接著是到了游戲各個分類的相關(guān)js文件的引入,該游戲有 <b>設(shè)置</b>、<b>關(guān)于</b>、<b>游戲</b>和<b>游戲結(jié)束后</b>四個模塊。下面看看這四個模塊都用到了Cocos2d 的哪些基礎(chǔ)概念。

首先說一下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();
  1. <b>cc.game</b>
    這個是An object to boot the game.(啟動游戲的對象)其中有 end(游戲停止)、isPaused(檢查游戲是否停止)、pause(停止游戲)、prepare(游戲前準(zhǔn)備)、restart(游戲重新啟動)
    resume(繼續(xù)游戲)、run(啟動游戲) setFrameRate(設(shè)置幀率)、step(一幀一幀運(yùn)行游戲)這些方法。還有很多屬性onStart就是其中的一個屬性,這里將onStart定義成了一個function,在scripts引擎加載完畢后就會回調(diào)。
  2. <b>cc.view</b>
    在onStart回調(diào)的方法中設(shè)置了cc.view的相關(guān)屬性,cc.view是個代表了游戲窗口的單例的對象
    這里使用setDesignResolutionSize(width, height, resolutionPolicy)設(shè)置了游戲窗口的大小和屏幕適配的策略。屏幕大小自適應(yīng)的涉及到cc.ResolutionPolicy,其中有六種方式

EXACT_FIT會拉伸游戲,充滿整個屏幕,最簡單最粗暴,
SHOW_ALL保持游戲原比例,讓一邊占滿屏幕,另外一側(cè)黑邊
NO_BORDER跟SHOW_ALL類似,但讓短邊占滿屏幕,另外一側(cè)超出屏幕,不顯示黑邊,一部分畫面在屏幕外,無法顯示,
FIXED_HEIGHTFIXED_WIDTH都是NO_BORDER的升級版,指定那一側(cè)充滿屏幕,另外一側(cè)超出屏幕,
UNKNOWN 六種方案。該游戲選擇的是SHOW_ALL、
接著使用resizeWithBrowserSize(enabled)
這個方法只在web中起作用,canvas 跟著瀏覽器得大小變動而自適應(yīng)。

  1. <b>cc.LoaderScene</b>和<b>cc.director</b>
Node子類的結(jié)構(gòu)圖

這里涉及到Node、Scence、Director。即節(jié)點(diǎn)、導(dǎo)演和場景 ,其實(shí)還有Layer(層)、Sprite(精靈)。節(jié)點(diǎn)是Node是上層的對象。在Cocos2d-x-3.x引擎中,采用節(jié)點(diǎn)樹形結(jié)構(gòu)來管理游戲?qū)ο?,一個游戲可以劃分為不同的場景,一個場景又可以分為不同的層,一個層又可以擁有任意個可見的游戲節(jié)點(diǎn)(即對象,游戲中基本上所有的類都派生于節(jié)點(diǎn)類Node)??梢詧?zhí)行Action來修改游戲節(jié)點(diǎn)的屬性,使其移動、旋轉(zhuǎn)、放大、縮小等等。
看下官網(wǎng)給出的圖即可明白


官網(wǎng)給出的關(guān)系圖

到眼控制著場景,層屬于場景中的一個場景。然后精靈有事屬于場景中的一個東東。精靈的移動,旋轉(zhuǎn),縮放,執(zhí)行動畫,并接受其他轉(zhuǎn)換構(gòu)成層的東西,各個層之間順序執(zhí)行然后構(gòu)成一個場景。場景之間的切換最終構(gòu)成了一個游戲,執(zhí)行者就是導(dǎo)演。
cc.LoaderScene.preload了g_resources后傳入回調(diào)函數(shù)
function () {
cc.director.runScene(new MainMenuScene());
}回調(diào)函數(shù)是導(dǎo)演執(zhí)行了第一個場景。
再傳入當(dāng)前的對象 this);
好了看new出的第一個場景 MainMenuScene 看一下場景定義的規(guī)范

var MainMenuScene  = cc.Scene.extend({
 //this._super();重寫后這個方法一定要記得寫上,要不然導(dǎo)致后續(xù)不會執(zhí)行
//這里是場景的各個函數(shù)的重寫 場景是是繼承自node的所以node中方法也可以被重寫,其中這里有onEnter方法,這個方法是不需要主動調(diào)用的。
官方給出的解釋Event callback that is invoked every time when CCNode enters the 'stage'. 可以看出 這個方法會每次都被調(diào)用 當(dāng)進(jìn)入到stage后.
var layer = new MainMenuLayer();
  this.addChild(layer);這個場景只有一個層,最后將這個層假如到這個場景,由于只有一個層所以都不需要指定執(zhí)行的順序。
})

下面看MainMenuLayer,層的規(guī)范定義是

var MainMenuLayer = cc.Layer.extend({
//    這里可以聲明屬性
    _backgroundLayer : null,
    _touchLayer      : null,
ctor方法相當(dāng)于是構(gòu)造方法,你在new這個層的時(shí)候就會被調(diào)用。
構(gòu)造方法中又可以調(diào)用其他的方法
這里是調(diào)用了addBackgroundLayer和addTouchLayer可以看出是加了兩個子層到該層中,這個是按加入的順序執(zhí)行的。
})

看一下假如的MMBackgroundLayer和MMTouchLayer層,
MMBackgroundLayer是創(chuàng)建了一個背景層,這個和上一個所講的層基本一致。里面涉及到了精靈的創(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)建方式 可以指定屬性一個其中有anchorPoint 錨點(diǎn)坐標(biāo),默認(rèn)是 (0.5, 0.5) 這就說明這個精靈位于層的中央。x,y是寬和高的概念。
這里涉及到cocos2d的坐標(biāo)概念
總之openGL的坐標(biāo)和UI的坐標(biāo)是不一致的,按openGL坐標(biāo)來就可以了

ui坐標(biāo)

openGL坐標(biāo)

坐標(biāo)中還有模型坐標(biāo)和世界坐標(biāo)的概念 其實(shí)就是精靈相對的坐標(biāo)概念

![Upload Paste_Image.png failed. Please try again.]
在游戲場景中有兩個Node對象,其中Node1的坐標(biāo)是(400, 500),大小是300 x 100像素。Node2是放置在Node1中的,它對于Node1的模型坐標(biāo)是(0, 0),大小是150 x 50像素。

好了繼續(xù)到MMTouchLayer了,在該層中 用到了cc.audioEngine去播放音樂。

/        播放背景音樂,true代表循環(huán)無限次播放,false表示只播放一次。
        if (GC.SOUND_ON){
            if (cc.audioEngine.isMusicPlaying()){
                return;
            }
            cc.audioEngine.playMusic(res.mm_bgMusic_mp3, true);
        }

然后又去加載了cc.Menu

//        菜單。 對應(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 看其中的一個MenuItemSprite 定義

var newGame = new cc.MenuItemSprite(
            newGameNormal,
            newGameSelected,
            newGameDisabled,
            function(){
                this.onButtonEffect();
                this.flareEffect(flare, this, this.onNewGame);
            }.bind(this)
        );

里面構(gòu)造方法傳入的也是各個精靈,精靈的創(chuàng)建中用到了cc.rect,看其中的一個。

//        根據(jù)rect區(qū)域去創(chuàng)建一個精靈,作為下面menuItemSprite顯示的圖片。
//        因?yàn)閙enuItem有Normal、Selected、Disabled三個狀態(tài),所以一個菜單項(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)。
這樣的話就渲染出來了兩個層,一個背景層,一個菜單層。在菜單切換的過程中還涉及到了 動作 ,函數(shù)的回調(diào) 、按順序執(zhí)行一組動作、 同時(shí)執(zhí)行一組動作

//        定義動作
        var opacityAnim = cc.fadeIn(0.5, 255);
        var opacDim = cc.fadeIn(1, 0);

//        為動作加上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)動作
        var onComplete = cc.callFunc(callback, target);
        var killflare = cc.callFunc(function () {
            this.getParent().removeChild(this,true);
        }, flare);

//        按順序執(zhí)行一組動作
        var seqAction = cc.sequence(opacityAnim, biggerEase, opacDim, killflare, onComplete);

//        同時(shí)執(zhí)行一組動作
        var action = cc.spawn(seqAction, easeMove, rotateEase, bigger);
        flare.runAction(action);
    }

動作比較簡單的。 就是精靈的旋轉(zhuǎn)放大縮小等。
cc.callFunc是去執(zhí)行一個函數(shù),這里有this.getParent().removeChild方法,其實(shí)就是去移除創(chuàng)建的層。
cc.sequence是按順序執(zhí)行一組動作,傳給 cc.spawn 是組合一組動作,然后精靈去執(zhí)行 flare.runAction(action);
主要看一下GamePlayScene 游戲玩耍這個層,這個層里面涉及到了基礎(chǔ)概念cc.spriteFrameCache緩存

 cc.spriteFrameCache.addSpriteFrames(res.gp_TextureOpaquePack_plist);

直接這樣調(diào)用就可以了。這個層假如了兩個層GPTouchLayer和GPBackgroundLayer
GPBackgroundLayer比較簡單就是放了一張背景圖片而已。主要的碰撞動作在GPTouchLayer中
這個里面涉及到比較多和重要的幾個概念。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);

//            碰撞檢測
            this.checkIsCollide();

//            檢測我們的飛船重生
            this.checkIsReborn();

//            這個部分被我直接干掉了。。。因?yàn)?,重?fù)代碼,沒什么內(nèi)容
//            this._movingBackground(dt);
        }
    },

schedule是一致去執(zhí)行一個方法這里一直在執(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
        });

這個里面還有一個比較重要的就是碰撞檢測
利用坐標(biāo)是否落在區(qū)域去檢測
看代碼

//   碰撞堅(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);
    },

這里面分了不同種類的精靈BulletSprite、EnemySprite、ExplosionSprite、ShipSprite、SparkEffectSprite看名字就可以看出來各個代表什么精靈,既然有碰撞檢測那么坐標(biāo)移動在哪里呢,其實(shí)就是在各個精靈中自己定義的action
看BulletSprite這個精靈吧,

update:function (dt) {

//        cc.log("這里。。。");
        var y = this.y;
        this.y = y - this.yVelocity * dt; //不斷的移動坐標(biāo)去達(dá)到移動的目的
        if (y < 0 || y > GC.h + 10 || this.HP <= 0) {
            this.destroy();
        }
    },

其中還有一些別的方法 死亡啊,新產(chǎn)生啊之類的,這個就是碰撞檢測后的邏輯代碼,不涉及到基礎(chǔ)知識了,這里就過了。

真不容易寫完了。這里貼出來試玩地址
http://112.126.90.18/airbarons/
算了還是放頂部吧
go home。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. Scene 場景 創(chuàng)建一個Scene 2.節(jié)點(diǎn) Node 節(jié)點(diǎn)即構(gòu)成cocos2d世界的基本單位節(jié)點(diǎn)封裝了基...
    寫java的逗比叫z1閱讀 1,084評論 0 2
  • 前言 我選擇開發(fā)一個游戲有很多原因。我覺得自己是“核心”玩家,過去的大部分時(shí)間我都花在玩游戲,自己制作、閱讀和游戲...
    月影檀香閱讀 12,489評論 1 27
  • 經(jīng)過昨天的一輪面試,小萌新們是否對黨建的各部門有了更多了解呢~ 先要恭喜進(jìn)入二輪筆試的同學(xué)噢~ 在此之前,請看小宣...
    Joyceyee閱讀 231評論 0 0
  • 以前不喜歡寒冷的冬天,但今年我心態(tài)完全不一樣,現(xiàn)在深冬才是我要的感覺,最好是北風(fēng)蕭蕭,天氣要有四季才知冷暖,生活不...
    孤心詣閱讀 348評論 4 2
  • 科學(xué)背后有一個邏輯,萬物背后有規(guī)則支配著,這規(guī)則并不以人的意志為轉(zhuǎn)移??茖W(xué)就在尋找各種各樣背后的規(guī)律?;蛟S有人就說...
    tomcat_wu閱讀 636評論 0 2

友情鏈接更多精彩內(nèi)容