背景:
最近火爆全球的游戏flappy bird让笔者叹为观止,于是花了一天的时间山寨了一个一模一样的游戏,现在把游戏的思路和源码分享出来,代码是基于javascript语言,cocos2d-x游戏引擎,cocos2d-x editor手游开发工具完成的,请读者轻砸;
android apk下载:(演示效果)
暂时只移植打包到android系统,可下载运行看看效果;
代码下载:
代码如何移植到各平台:
效果图:
cocos2d-x跨平台游戏引擎
cocos2d-x是全球知名的游戏引擎 ,引擎在全球范围内拥有众多开发者,涵盖国内外各知名游戏开发商。目前cocos2d-x引擎已经实现横跨ios、android、bada、meego、blackberry、marmalade、windows、linux等平台。编写一次,到处运行,分为两个版本 cocos2d-c++和cocos2d-html5 本文使用了后者;
cocos2d-x editor开发工具:
cocos2dx editor,它是开发跨平台的手机游戏工具,运行window/mac系统上,javascript脚本语言,基于cocos2d-x跨平台游戏引擎, 集合代码编辑,场景设计,动画制作,字体设计,还有粒子,物理系统,地图等等的,而且调试方便,和实时模拟;
思路和源码:
1 场景设计mainlayer.ccbx,如下图;主要分三层,开始场景、主场景、游戏结束场景,通过显示隐藏控制三个场景的切换。
mainlayer.ccbx代码
<?xml version="1.0" encoding="utf-8"?>
<document
jscontrolled="true"
jscontroller="mainlayer"
resolution="default"
>
<resolutions>
<resolution centeredorigin="false" ext="iphone" height="1280" width="720" name="default" scale="1"/>
<resolution centeredorigin="false" ext="iphone" height="720" width="1280" name="default1" scale="1"/>
</resolutions>
<animations>
<animation autoplay="true"
id="0"
name="default timeline"
length="10"
chainedid="0"
offset="0.0"
position="0.0"
resolution="30"
scale="128">
<callbackchannel>
</callbackchannel>
<soundchannel>
</soundchannel>
</animation>
</animations>
<layer
positionx="0" positiony="0.0"
sizetype="percent"
width="100" height="100"
anchorpointx="0.5" anchorpointy="0.5" ignoreanchorpoint="true"
scalex="1" scaley="1"
>
<sprite positiontype="leftbottom" width="720.0" height="1280.0" positionx="0" positiony="0" anchorpointx="0"
anchorpointy="0" src="resources/bg.png" name="" var="" target="none" scalex="1" scaley="1" visible="true"/>
<layercolor positiontype="leftbottom" width="720" height="1280" positionx="0" positiony="0" anchorpointx="0"
anchorpointy="0" color="#fff2e8ff" visible="false"/>
<menu positiontype="leftbottom" width="40" height="40" positionx="356.0" positiony="237.0" anchorpointx="0.5"
anchorpointy="0.5" scalex="2.4" scaley="1.725">
</menu>
<sprite positiontype="leftbottom" width="840.0" height="281.0" positionx="0" positiony="0" anchorpointx="0"
anchorpointy="0" src="resources/ground.png" var="ground" target="doc"/>
<node positiontype="leftbottom" width="40" height="40" positionx="800" positiony="250" anchorpointx="0"
anchorpointy="0" var="hosenode" target="doc">
<sprite positiontype="leftbottom" width="86.0" height="60.0" positionx="-500" positiony="400" anchorpointx="0.5"
anchorpointy="0.5" src="resources/flappy_packer.plist/bird3.png" var="test" target="doc" visible="false"/>
<sprite positiontype="leftbottom" width="86.0" height="60.0" positionx="-550" positiony="500" anchorpointx="0.5"
anchorpointy="0.5" src="resources/flappy_packer.plist/bird1.png" var="bird" target="doc" scalex="1" scaley="1" rotation="0" visible="true"/>
</node>
<node positiontype="leftbottom" width="40" height="40" positionx="303.0" positiony="500" anchorpointx="0.5"
anchorpointy="0.5" var="readynode" target="doc" visible="true">
<sprite positiontype="leftbottom" width="508.0" height="158.0" positionx="95.0" positiony="584.0" anchorpointx="0.5"
anchorpointy="0.5" src="resources/flappy_packer.plist/getready.png"/>
<sprite positiontype="leftbottom" width="286.0" height="246.0" positionx="73.0" positiony="236.0" anchorpointx="0.5"
anchorpointy="0.5" src="resources/flappy_packer.plist/click.png"/>
<node positiontype="leftbottom" width="40" height="40" positionx="300" positiony="500" anchorpointx="0.5"
anchorpointy="0.5" var="overnode" target="doc" visible="true">
<sprite positiontype="leftbottom" width="590.0" height="298.0" positionx="72.0" positiony="219.0" anchorpointx="0.5"
anchorpointy="0.5" src="resources/flappy_packer.plist/base.png">
<sprite positiontype="leftbottom" width="508.0" height="158.0" positionx="286.0" positiony="458.0" anchorpointx="0.5"
anchorpointy="0.5" src="resources/flappy_packer.plist/gameover.png"/>
</sprite>
<menu positiontype="leftbottom" width="40" height="40" positionx="0" positiony="0" anchorpointx="0.5"
anchorpointy="0.5">
<menuitem positiontype="leftbottom" width="290" height="176" positionx="-65.0" positiony="-92.0" anchorpointx="0.5"
anchorpointy="0.5" normalimage="resources/flappy_packer.plist/start.png" target="doc" onclick="onstartclicked"/>
<menuitem positiontype="leftbottom" width="290" height="176" positionx="230.0" positiony="-92.0" anchorpointx="0.5"
anchorpointy="0.5" target="doc" normalimage="resources/flappy_packer.plist/grade.png" onclick="ongradeclicked"/>
</menu>
</layer>
</document>
2 代码编写mainlayer.js
首先,小鸟在向前飞,其实是底部的路和水管在向左移动,相对的你就感觉小鸟在向右飞了;路循环移动代码:
mainlayer.prototype.groundrun = function ()
{
var action1 = cc.moveto.create(0.5, cc.p(-120, 0));
var action2 = cc.moveto.create(0, cc.p(0, 0));
var action = cc.sequence.create(action1, action2);
this.ground.runaction(cc.repeatforever.create(action));
}
初始化高低不同的水管,每一关卡都由上下两水管和空隙组成。总长度相同,空隙也一定,随机取下面水管的长度,就形成错落有致的水管关卡;
mainlayer.prototype.newhose = function (num)
var hoseheight = 830;
var acrossheight = 300;
var downheight = 100 + getrandom(400);
var upheight = 1100 - downheight - acrossheight;
var hosex = 400 * num;
var hosename = fp_main_texture.hose;
var ccspritedown = cc.sprite.createwithspriteframename(hosename[0]);
ccspritedown.setzorder(1);
ccspritedown.setanchorpoint(cc.p(0, 0));
ccspritedown.setposition(cc.p(hosex, 0));
ccspritedown.setscaley(downheight / hoseheight);
var ccspriteup = cc.sprite.createwithspriteframename(hosename[1]);
ccspriteup.setzorder(1);
ccspriteup.setanchorpoint(cc.p(0, 0));
ccspriteup.setposition(cc.p(hosex, downheight + acrossheight));
ccspriteup.setscaley(upheight / hoseheight);
this.hosenode.addchild(ccspritedown);
this.hosenode.addchild(ccspriteup);
this.hosespritelist.push(ccspritedown);
this.hosespritelist.push(ccspriteup);
return null;
一开始进入游戏让底部路不断移动,初始化水管,显示准备游戏场景;
mainlayer.prototype.onenter = function ()
cc.animationcache.getinstance().addanimations("resources/flappy_frame.plist");
this.groundrun();
this.ground.setzorder(10);
this.birdreadyaction();
this.bird.setzorder(20);
this.readynode.setvisible(true);
this.overnode.setvisible(false);
for (var i = 0; i < 30; i++) {
this.newhose(i);
}
点击屏幕,小鸟向上飞60dp,然后更快的速度下落(移动动画),同时闪动翅膀(帧动画);
mainlayer.prototype.birdriseaction = function ()
var riseheight = 60;
var birdx = this.bird.getpositionx();
var birdy = this.bird.getpositiony();
var time = birdy / 600;
var actionframe = cc.animate.create(cc.animationcache.getinstance().getanimation("fly"));
var flyaction = cc.repeat.create(actionframe, 90000);
var riseaction1 = cc.moveto.create(0.2, cc.p(birdx, birdy + riseheight));
var riseaction2 = cc.rotateto.create(0, -30);
var riseaction = cc.spawn.create(riseaction1, riseaction2);
var fallaction1 = cc.moveto.create(time, cc.p(birdx, 50));
var fallaction2 = cc.sequence.create(cc.delaytime.create(time / 6), cc.rotateto.create(0, 30));
var fallaction = cc.spawn.create(fallaction1, fallaction2);
this.bird.stopallactions();
this.bird.runaction(cc.spawn.create(
cc.sequence.create(riseaction, cc.delaytime.create(0.1), fallaction),
flyaction)
);
检测碰撞,如果小鸟碰到地面和水管,发生碰撞,这里碰撞直接用cocos2d-x 里面的图片和图片交叉函数 cc.rectintersectsrect;
mainlayer.prototype.checkcollision = function ()
if (this.bird.getpositiony() < 60) {
cc.log("floor");
this.birdfallaction();
return;
for (var i = 0; i < this.hosespritelist.length; i++) {
var hose = this.hosespritelist[i];
if (!this.isinscreen(hose)) {
// continue;
}
if (cc.rectintersectsrect(hose.getboundingbox(), this.bird.getboundingbox())) {
cc.log("hose positionx==" + hose.getboundingbox().x);
cc.log("this.bird positionx==" + this.bird.getboundingbox().x);
cc.log("i==" + i);
cc.log("birdfallaction");
this.birdfallaction();
return;
碰撞后,小鸟先下落,游戏结束场景显示;
mainlayer.prototype.birdfallaction = function ()
this.gamemode = over;
this.ground.stopallactions();
var time = birdy / 2000;
this.bird.runaction(cc.sequence.create(
cc.delaytime.create(0.1),
cc.spawn.create(cc.rotateto.create(time, 90), cc.moveto.create(time, cc.p(birdx, 50))))
this.overnode.setvisible(true);
游戏的难度主要在于多个水管的移动,小鸟触摸动画,检测碰撞,我把mainlayer.js所有代码贴出来;
fp_main_texture = {
frame_anims: "beanstalk/resources/bs_main_anims.plist",
hose: ["holdback1.png", "holdback2.png"]
ready = 1;
start = 2;
over = 3;
var mainlayer = function ()
cc.log("mainlayer");
this.bird = this.bird || {};
this.ground = this.ground || {};
this.hosenode = this.hosenode || {};
this.readynode = this.readynode || {};
this.overnode = this.overnode || {};
this.passtime = 0;
this.hosespritelist = [];
this.isstart = false;
this.gamemode = ready;
};
mainlayer.prototype.ondidloadfromccb = function ()
if (sys.platform == 'browser') {
this.onenter();
else {
this.rootnode.onenter = function ()
{
this.controller.onenter();
};
this.rootnode.schedule(function (dt)
{
this.controller.onupdate(dt);
});
this.rootnode.onexit = function ()
this.controller.onexit();
};
this.rootnode.ontouchesbegan = function (touches, event)
this.controller.ontouchesbegan(touches, event);
return true;
this.rootnode.ontouchesmoved = function (touches, event)
this.controller.ontouchesmoved(touches, event);
this.rootnode.ontouchesended = function (touches, event)
this.controller.ontouchesended(touches, event);
this.rootnode.settouchenabled(true);
mainlayer.prototype.birdreadyaction = function ()
mainlayer.prototype.onupdate = function (dt)
if (this.gamemode != start) {
this.passtime += dt;
this.hosenode.setpositionx(800 - 200 * this.passtime);
this.bird.setpositionx(-500 + 200 * this.passtime);
this.checkcollision();
mainlayer.prototype.isinscreen = function (sprite)
return (sprite.getpositionx() > 0 && sprite.getpositionx() < 720);
mainlayer.prototype.onexitclicked = function ()
mainlayer.prototype.onstartclicked = function ()
cc.director.getinstance().resume();
cc.builderreader.runscene("", "mainlayer");
mainlayer.prototype.onexit = function ()
cc.log("onexit");
mainlayer.prototype.ontouchesbegan = function (touches, event)
var loc = touches[0].getlocation();
mainlayer.prototype.ontouchesmoved = function (touches, event)
mainlayer.prototype.ontouchesended = function (touches, event)
if (this.gamemode == over) {
if (this.gamemode == ready) {
this.gamemode = start;
this.readynode.setvisible(false);
this.birdriseaction();
function isinrect(ccrect, cctouchbeganpos)
if (cctouchbeganpos.x > ccrect.x && cctouchbeganpos.x < (ccrect.x + ccrect.width)) {
if (cctouchbeganpos.y > ccrect.y && cctouchbeganpos.y < (ccrect.y + ccrect.height)) {
return true;
return false;
function getrandom(maxsize)
return math.floor(math.random() * maxsize) % maxsize;
再次提示代码下载地址:
flappy博文系列:
<a target="_blank" href="http://blog.csdn.net/touchsnow/article/details/19071961">flappy bird游戏源代码揭秘和下载</a>
<a target="_blank" href="http://blog.csdn.net/touchsnow/article/details/19176091">flappy bird游戏源代码揭秘和下载后续---移植到android真机上</a>
<a target="_blank" href="http://blog.csdn.net/touchsnow/article/details/19269757">flappy bird游戏源代码揭秘和下载后续---移植到html5网页浏览器</a>
<a target="_blank" href="http://blog.csdn.net/touchsnow/article/details/20258869">flappy bird游戏源代码揭秘和下载后续---日进5万美元的秘诀admob广告 </a>
flappy bird游戏源代码揭秘和下载后续---移植到苹果ios上 (未写)
笔者语:
联系笔者:[email protected](邮箱)qq群:232361142
后言:
为了鼓励更多手游爱好者加入cocos2d-x editor阵营,也为了读者坚定对该工具的信心,笔者基于以上的flappy bird代码和新的图片资源,往google play和中国应用市场发布了一个相似的游戏----腾飞的小鸟(fly bird)。该游戏还加入了广告模块,可以获得些许的广告费。也希望读者通过cocos2d-x editor可以在市场上发布更多的好游戏;