Cocos2d-JS连载之打飞机游戏学习基础概念

打飞机这个游戏并不是我原创,只是拿来学习使用。望原作者见谅。刚刚开始是强烈建议简书可以上传源代码的,但是后来一想,已经有了github了可以直接链接过去,都不用简书浪费资源了。哈哈哈哈。我讲要分析的项目上传到了我的github仓库:github AirBarons

游戏玩耍地址:http://112.126.90.18/airbarons/
  先看一下AirBarons目录结构。

AirBarons目录结构

  • project.json 项目的一些配置信息
"project_type": "javascript",
    "debugMode" : 1,
    "showFPS" : true,
    "frameRate" : 60,
    "id" : "gameCanvas",
    "renderMode" : 0,
    "engineDir":"frameworks/cocos2d-html5",
    "modules" : ["cocos2d"],

这些前一篇已经说过了。
看下AirBarons项目用到了哪些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数组中了

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中用到的配置相关参数js的引入
"src/config/GameConfig.js",
"src/config/EnemyType.js",
"src/config/Level.js",
  • 接着是到了游戏各个分类的相关js文件的引入,该游戏有 <b>设置</b>、<b>关于</b>、<b>游戏</b>和<b>游戏结束后</b>四个模块。下面看看这四个模块都用到了Cocos2d 的哪些基础概念。

首先说一下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(游戏前准备)、restart(游戏重新启动)
    resume(继续游戏)、run(启动游戏) setFrameRate(设置帧率)、step(一帧一帧运行游戏)这些方法。还有很多属性onStart就是其中的一个属性,这里将onStart定义成了一个function,在scripts引擎加载完毕后就会回调。
  2. <b>cc.view</b>
    在onStart回调的方法中设置了cc.view的相关属性,cc.view是个代表了游戏窗口的单例的对象
    这里使用setDesignResolutionSize(width, height, resolutionPolicy)设置了游戏窗口的大小和屏幕适配的策略。屏幕大小自适应的涉及到cc.ResolutionPolicy,其中有六种方式

EXACT_FIT会拉伸游戏,充满整个屏幕,最简单最粗暴
SHOW_ALL保持游戏原比例,让一边占满屏幕,另外一侧黑边
NO_BORDER跟SHOW_ALL类似,但让短边占满屏幕,另外一侧超出屏幕,不显示黑边,一部分画面在屏幕外,无法显示
FIXED_HEIGHTFIXED_WIDTH都是NO_BORDER的升级版,指定那一侧充满屏幕,另外一侧超出屏幕,
UNKNOWN 六种方案。该游戏选择的是SHOW_ALL
接着使用resizeWithBrowserSize(enabled)
这个方法只在web中起作用,canvas 跟着浏览器得大小变动而自适应。

  1. <b>cc.LoaderScene</b>和<b>cc.director</b>
Node子类的结构图

这里涉及到Node、Scence、Director。即节点、导演和场景 ,其实还有Layer(层)、Sprite(精灵)。节点是Node是上层的对象。在Cocos2d-x-3.x引擎中,采用节点树形结构来管理游戏对象,一个游戏可以划分为不同的场景,一个场景又可以分为不同的层,一个层又可以拥有任意个可见的游戏节点(即对象,游戏中基本上所有的类都派生于节点类Node)。可以执行Action来修改游戏节点的属性,使其移动、旋转、放大、缩小等等。
看下官网给出的图即可明白


官网给出的关系图

到眼控制着场景,层属于场景中的一个场景。然后精灵有事属于场景中的一个东东。精灵的移动,旋转,缩放,执行动画,并接受其他转换构成层的东西,各个层之间顺序执行然后构成一个场景。场景之间的切换最终构成了一个游戏,执行者就是导演。
cc.LoaderScene.preload了g_resources后传入回调函数
function () {
cc.director.runScene(new MainMenuScene());
}回调函数是导演执行了第一个场景。
再传入当前的对象 this);
好了看new出的第一个场景 MainMenuScene 看一下场景定义的规范

var MainMenuScene  = cc.Scene.extend({
 //this._super();重写后这个方法一定要记得写上,要不然导致后续不会执行
//这里是场景的各个函数的重写 场景是是继承自node的所以node中方法也可以被重写,其中这里有onEnter方法,这个方法是不需要主动调用的。
官方给出的解释Event callback that is invoked every time when CCNode enters the 'stage'. 可以看出 这个方法会每次都被调用 当进入到stage后.
var layer = new MainMenuLayer();
  this.addChild(layer);这个场景只有一个层,最后将这个层假如到这个场景,由于只有一个层所以都不需要指定执行的顺序。
})

下面看MainMenuLayer,层的规范定义是

var MainMenuLayer = cc.Layer.extend({
//    这里可以声明属性
    _backgroundLayer : null,
    _touchLayer      : null,
ctor方法相当于是构造方法,你在new这个层的时候就会被调用。
构造方法中又可以调用其他的方法
这里是调用了addBackgroundLayer和addTouchLayer可以看出是加了两个子层到该层中,这个是按加入的顺序执行的。
})

看一下假如的MMBackgroundLayer和MMTouchLayer层,
MMBackgroundLayer是创建了一个背景层,这个和上一个所讲的层基本一致。里面涉及到了精灵的创建

 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
        });

可以看出精灵的创建方式 可以指定属性一个其中有anchorPoint 锚点坐标,默认是 (0.5, 0.5) 这就说明这个精灵位于层的中央。x,y是宽和高的概念。
这里涉及到cocos2d的坐标概念
总之openGL的坐标和UI的坐标是不一致的,按openGL坐标来就可以了

ui坐标

openGL坐标

坐标中还有模型坐标和世界坐标的概念 其实就是精灵相对的坐标概念

![Upload Paste_Image.png failed. Please try again.]
在游戏场景中有两个Node对象,其中Node1的坐标是(400, 500),大小是300 x 100像素。Node2是放置在Node1中的,它对于Node1的模型坐标是(0, 0),大小是150 x 50像素。

好了继续到MMTouchLayer了,在该层中 用到了cc.audioEngine去播放音乐。

/        播放背景音乐,true代表循环无限次播放,false表示只播放一次。
        if (GC.SOUND_ON){
            if (cc.audioEngine.isMusicPlaying()){
                return;
            }
            cc.audioEngine.playMusic(res.mm_bgMusic_mp3, true);
        }

然后又去加载了cc.Menu

//        菜单。 对应三者关系:菜单里面有菜单项,菜单项中绑定要执行的方法,并且需要图片去显示。图片就是精灵
        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);

构造方法传入的是cc.MenuItemSprite 看其中的一个MenuItemSprite 定义

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

里面构造方法传入的也是各个精灵,精灵的创建中用到了cc.rect,看其中的一个。

//        根据rect区域去创建一个精灵,作为下面menuItemSprite显示的图片。
//        因为menuItem有Normal、Selected、Disabled三个状态,所以一个菜单项需要三张纹理图片
        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)。
这样的话就渲染出来了两个层,一个背景层,一个菜单层。在菜单切换的过程中还涉及到了 动作 ,函数的回调 、按顺序执行一组动作、 同时执行一组动作

//        定义动作
        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);

//        函数回调动作
        var onComplete = cc.callFunc(callback, target);
        var killflare = cc.callFunc(function () {
            this.getParent().removeChild(this,true);
        }, flare);

//        按顺序执行一组动作
        var seqAction = cc.sequence(opacityAnim, biggerEase, opacDim, killflare, onComplete);

//        同时执行一组动作
        var action = cc.spawn(seqAction, easeMove, rotateEase, bigger);
        flare.runAction(action);
    }

动作比较简单的。 就是精灵的旋转放大缩小等。
cc.callFunc是去执行一个函数,这里有this.getParent().removeChild方法,其实就是去移除创建的层。
cc.sequence是按顺序执行一组动作,传给 cc.spawn 是组合一组动作,然后精灵去执行 flare.runAction(action);
主要看一下GamePlayScene 游戏玩耍这个层,这个层里面涉及到了基础概念cc.spriteFrameCache缓存

 cc.spriteFrameCache.addSpriteFrames(res.gp_TextureOpaquePack_plist);

直接这样调用就可以了。这个层假如了两个层GPTouchLayer和GPBackgroundLayer
GPBackgroundLayer比较简单就是放了一张背景图片而已。主要的碰撞动作在GPTouchLayer中
这个里面涉及到比较多和重要的几个概念。scheduleUpdate和schedule
scheduleUpdate相当于是调用层的update方法

//    游戏时时刷新
    update:function (dt) {
        if (this._state == STATE_PLAYING) {

//            UI在这边更新
            this.updateUI();

//            敌人在这里面产生,以及界面上
            this.moveActiveUnit(dt);

//            碰撞检测
            this.checkIsCollide();

//            检测我们的飞船重生
            this.checkIsReborn();

//            这个部分被我直接干掉了。。。因为,重复代码,没什么内容
//            this._movingBackground(dt);
        }
    },

schedule是一致去执行一个方法这里一直在执行分数计数方法。

//    分数在这里面加
    scoreCounter:function () {
        if (this._state == STATE_PLAYING) {
            this._time++;
            this._levelManager.loadLevelResource(this._time);
        }
    },

存放分数信息的是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
        });

这个里面还有一个比较重要的就是碰撞检测
利用坐标是否落在区域去检测
看代码

//   碰撞坚持
    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看名字就可以看出来各个代表什么精灵,既然有碰撞检测那么坐标移动在哪里呢,其实就是在各个精灵中自己定义的action
看BulletSprite这个精灵吧,

update:function (dt) {

//        cc.log("这里。。。");
        var y = this.y;
        this.y = y - this.yVelocity * dt; //不断的移动坐标去达到移动的目的
        if (y < 0 || y > GC.h + 10 || this.HP <= 0) {
            this.destroy();
        }
    },

其中还有一些别的方法 死亡啊,新产生啊之类的,这个就是碰撞检测后的逻辑代码,不涉及到基础知识了,这里就过了。

真不容易写完了。这里贴出来试玩地址
http://112.126.90.18/airbarons/
算了还是放顶部吧
go home。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,454评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,553评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,921评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,648评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,770评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,950评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,090评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,817评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,275评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,592评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,724评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,409评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,052评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,815评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,043评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,503评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,627评论 2 350

推荐阅读更多精彩内容

  • 1. Scene 场景 创建一个Scene 2.节点 Node 节点即构成cocos2d世界的基本单位节点封装了基...
    写java的逗比叫z1阅读 1,021评论 0 2
  • 前言 我选择开发一个游戏有很多原因。我觉得自己是“核心”玩家,过去的大部分时间我都花在玩游戏,自己制作、阅读和游戏...
    月影檀香阅读 11,853评论 1 27
  • 经过昨天的一轮面试,小萌新们是否对党建的各部门有了更多了解呢~ 先要恭喜进入二轮笔试的同学噢~ 在此之前,请看小宣...
    Joyceyee阅读 174评论 0 0
  • 以前不喜欢寒冷的冬天,但今年我心态完全不一样,现在深冬才是我要的感觉,最好是北风萧萧,天气要有四季才知冷暖,生活不...
    孤心诣阅读 311评论 4 2
  • 科学背后有一个逻辑,万物背后有规则支配着,这规则并不以人的意志为转移。科学就在寻找各种各样背后的规律。或许有人就说...
    tomcat_wu阅读 573评论 0 2