cocos2dx-html5 实现网页版flappy bird游戏

我也是第一次使用cocos2d_html5,对js和html5也不熟,看引擎自带的例子和引擎源码,边学边做,如果使用过cocos2d-x的话,完成这个游戏还是十分简单的。游戏体验地址:

http://zhoujianghai.github.io/games/flappybird/

1.
首先去cocos2d-x官网下载Cocos2d-html5-v2.2.2(目前最新版本)压缩包

2.
下载安装WampServer
(http://www.wampserver.com/en/),后期在浏览器运行程序的时候,需要用到wampserver。WampServer是一款由法国人开发的Apache
Web服务器、PHP解释器以及MySQL数据库的整合软件包,本身也不大,才30多M。我这里安装到e盘,安装到最后会出现选择explorer的提示,定位到WINDOWS目录下explorer.exe或者其他自己安装的浏览器目录下的explorer.exe文件。安装好后启动,在桌面的右下角会有一个绿色图标。

3.
解压Cocos2d-html5-v2.2.2到E:\wamp\www目录下
,此时打开浏览器,输入localhost,就可以看到下面的界面了,点击cocos2d_html5,就可以看到项目列表了:

4.
复制cocos2d_html5目录下的HelloHTML5World例子,命名为flappybird
,这是引擎自带的Hello
World
demo。在引擎目录下创建projects目录,然后把flappybird放到projects目录下。修改引擎根目录下的index.html文件,在MoonWarriors下添加





1

<li><a
href="projects/flappybird/index.html">flappybird </a> <span> - Game</span></li>

刷新浏览器,会看到刚添加的flappybird了:

flappybird目录结构如下:

res目录存放的是图片等资源文件,src存放自定义的js源码,build.xml是打包文件,使用ant打包。cocos2d.js文件是配置一些属性,比如:是否显示fps,是否使用物理引擎,指定引擎目录,指定自定义的js文件等,类似于android的Android.mk文件。index.html是游戏显示的界面,在这里指定canvas尺寸。main.js里定义了Application,加载游戏的入口Scene,类似于c++的AppDelegate.cpp。

5. 开始编写游戏

src目录下有两个文件,myApp.js(游戏主要代码在这个文件里)和resource.js(定义游戏所使用的资源),由于这个游戏代码量比较少,就直接在这两个文件添加代码。

游戏有三个状态:READY、START、OVER。

READY表示游戏刚开始时显示logo,然后提示玩家点击开始游戏;

START表示游戏进行中;

OVER表示游戏结束,显示游戏结算界面。

游戏中出现的精灵有:小鸟、底部不停滚动的地面、背景图片、一直向左滚动的水管。其实小鸟在水平方向是一直不动的,只有水管和地面在滚动。

游戏中需要完成的主要功能点:小鸟自身的动画、点击屏幕时小鸟上升和下降的动画、小鸟死亡动画、地面滚动动画、水管滚动动画、添加水管、小鸟和地面及小鸟和水管的碰撞检测、游戏分数存储。

首先加载资源和添加游戏背景:





1

2

3

4

5

this.winSize = cc.Director.getInstance().getWinSize();

cc.SpriteFrameCache.getInstance().addSpriteFrames(res.flappy_packer);

this.bgSprite = cc.Sprite.create(res.bg);

this.bgSprite.setPosition(this.winSize.width / 2, this.winSize.height / 2);

this.addChild(this.bgSprite, 0);

游戏资源使用TexturePacker打包在flappy_packer.plist文件中,函数名跟cocos2d-x c++版本是一样的。
初始化地面:





1

2

3

4

5

6

7

8

9

10

11

12

13

Helloworld.prototype.initGround = function() {

    //cc.log("initGround");

    this.groundSprite = cc.Sprite.create(res.ground);

    var
halfGroundW = this.groundSprite.getContentSize().width;

    var
halfGroundH = this.groundSprite.getContentSize().height;

    this.groundSprite.setAnchorPoint(0.5, 0.5);

    this.groundSprite.setPosition(halfGroundW / 2, halfGroundH / 2);

    this.addChild(this.groundSprite, GROUND_Z);

    var
action1 = cc.MoveTo.create(0.5, cc.p(halfGroundW / 2 - 120, this.groundSprite.getPositionY()));

    var
action2 = cc.MoveTo.create(0, cc.p(halfGroundW / 2, this.groundSprite.getPositionY()));

    var
action = cc.Sequence.create(action1, action2);

    this.groundSprite.runAction(cc.RepeatForever.create(action));

};

js可以使用proptotype来为类型添加行为,不理解的可以google一下。当然也可以跟init函数一样写在里面,像这样:





1

2

3

4

5

6

7

var Helloworld = cc.Layer.extend({

init:function
() {

},

 

initGround::function() {

}

);

这里为地面定义两个动作,因为地面图片宽度是840px,而游戏屏幕分辨率指定是720×1280,所以先让地面向左移动120px,再迅速回到原位置。

初始化小鸟动画:





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

Helloworld.prototype.initBird = function() {

    //cc.log("initBird");

    var
animation = cc.AnimationCache.getInstance().getAnimation("FlyBirdAnimation")

    if(!animation) {

        var
animFrames = [];

        var
str = "";

        var
birdFrameCount = 4;

        for
(var i = 1; i < birdFrameCount; ++ i) {

            str = "bird"
+ i + ".png";

            var
frame = cc.SpriteFrameCache.getInstance().getSpriteFrame(str);

            animFrames.push(frame);

        }

        var
animation = cc.Animation.create(animFrames, 0.05);

        cc.AnimationCache.getInstance().addAnimation(animation, "FlyBirdAnimation");

    }

 

    this.flyBird = cc.Sprite.createWithSpriteFrameName(res.fly_bird);

    this.flyBird.setAnchorPoint(cc.p(0.5, 0.5));

    this.flyBird.setPosition(this.winSize.width / 2, this.winSize.height / 2);

    this.addChild(this.flyBird, BIRD_Z);

    var
actionFrame = cc.Animate.create(animation);

    var
flyAction = cc.RepeatForever.create(actionFrame);

    this.flyBird.runAction(cc.RepeatForever.create(flyAction));

};

小鸟自身动画是一个帧动画,创建成功后添加到缓存中。

初始化ready界面,就是游戏开始的时候提示用户点击的画面:





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

Helloworld.prototype.initReady = function() {

    this.readyLayer = cc.Layer.create();

    var
logo = cc.Sprite.createWithSpriteFrameName(res.logo);

    logo.setAnchorPoint(cc.p(0.5, 0.5));

    logo.setPosition(this.winSize.width / 2, this.winSize.height - logo.getContentSize().height - 50);

    this.readyLayer.addChild(logo);

 

    var
getReady = cc.Sprite.createWithSpriteFrameName(res.getReady);

    getReady.setAnchorPoint(cc.p(0.5, 0.5));

    getReady.setPosition(this.winSize.width / 2, this.winSize.height / 2 + getReady.getContentSize().height);

    this.readyLayer.addChild(getReady);

 

    var
click = cc.Sprite.createWithSpriteFrameName(res.click);

    click.setAnchorPoint(cc.p(0.5, 0.5));

    click.setPosition(this.winSize.width / 2, getReady.getPositionY() - getReady.getContentSize().height / 2 - click.getContentSize().height / 2);

    this.readyLayer.addChild(click);

 

    this.addChild(this.readyLayer);

};

效果如下:

添加点击屏幕时小鸟上升和下降自由落体的动画:





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

Helloworld.prototype.runBirdAction = function
() {

    var
riseHeight = 50;

    var
birdX = this.flyBird.getPositionX();

    var
birdY = this.flyBird.getPositionY();

    var
bottomY = this.groundSprite.getContentSize().height - this.flyBird.getContentSize().height / 2;

 

    var
actionFrame = cc.Animate.create(cc.AnimationCache.getInstance().getAnimation("FlyBirdAnimation"));

    var
flyAction = cc.RepeatForever.create(actionFrame);

//上升动画

    var
riseMoveAction = cc.MoveTo.create(0.2, cc.p(birdX, birdY + riseHeight));

    var
riseRotateAction = cc.RotateTo.create(0, -30);

    var
riseAction = cc.Spawn.create(riseMoveAction, riseRotateAction);

//下落动画

//模拟自由落体运动

    var
fallMoveAction = FreeFall.create(birdY - bottomY);

    var
fallRotateAction =cc.RotateTo.create(0, 30);

    var
fallAction = cc.Spawn.create(fallMoveAction, fallRotateAction);

    this.flyBird.stopAllActions();

    this.flyBird.runAction(flyAction);

    this.flyBird.runAction(cc.Spawn.create(

        cc.Sequence.create(riseAction, fallAction) )

    );

};

这里自定义了一个自由落体的Action:FreeFall:





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

var FreeFall = cc.ActionInterval.extend( {

     timeElasped:0,

     m_positionDeltaY:null,

     m_startPosition:null,

     m_targetPosition:null,

 

    ctor:function() {

        cc.ActionInterval.prototype.ctor.call(this);

        this.yOffsetElasped = 0;

        this.timeElasped = 0;

        this.m_positionDeltaY = 0;

        this.m_startPosition = cc.p(0, 0);

        this.m_targetPosition = cc.p(0, 0);

     },

 

    initWithDuration:function
(duration) {

        if
(cc.ActionInterval.prototype.initWithDuration.call(this, duration)) {

            return
true;

        }

        return
false;

    },

 

    initWithOffset:function(deltaPosition) {

        var
dropTime = Math.sqrt(2.0*Math.abs(deltaPosition)/k_Acceleration) * 0.1;

        //cc.log("dropTime=" + dropTime);

        if
(this.initWithDuration(dropTime))

        {

            this.m_positionDeltaY = deltaPosition;

            return
true;

        }

         //cc.log("dropTime =" + dropTime + "; deltaPosition=" + deltaPosition);

        return
false;

    },

 

    isDone:function() {

        if
(this.m_targetPosition.y >= this._target.getPositionY()) {

            return
true;

        }

        return
false;

    },

 

    //Node的runAction函数会调用ActionManager的addAction函数,在ActionManager的addAction函数中会调用Action的startWithTarget,然后在Action类的startWithTarget函数中设置_target的值。

    startWithTarget:function(target) {

        //cc.log("startWithTarget target=" + target);

        cc.ActionInterval.prototype.startWithTarget.call(this, target);

        this.m_startPosition = target.getPosition();

        this.m_targetPosition = cc.p(this.m_startPosition.x, this.m_startPosition.y - this.m_positionDeltaY);

    },

 

    update:function(dt) {

        this.timeElasped += dt;

        //cc.log("isdone=" + this.timeElasped);

        if
(this._target && !(this.m_targetPosition.y >= this._target.getPositionY())) {

            var
yMoveOffset = 0.5 * k_Acceleration * this.timeElasped * this.timeElasped * 0.3;

            if
(cc.ENABLE_STACKABLE_ACTIONS) {

                var
newPos = cc.p(this.m_startPosition.x, this.m_startPosition.y - yMoveOffset);

                if
(this.m_targetPosition.y > newPos.y) {

                    newPos.y = this.m_targetPosition.y;

                    this._target.stopAction(this);

                }

 

                this._target.setPosition(newPos);

 

            } else
{

                this._target.setPosition(cc.p(this.m_startPosition.x, this.m_startPosition.y + this.m_positionDeltaY * dt));

            }

        }

    }

 

});

 

FreeFall.create = function(deltaPosition) {

        var
ff = new FreeFall();

        ff.initWithOffset(deltaPosition);

        return
ff;

    };

模仿了CCActionInterval.js中的其他内置的Action,如MoveBy,主要重写了initWithDuration,startWithTarget,update,isDone函数。
initWithDuration是设置该action运行的时间,时间的长短决定下降的速度。
startWithTarget函数由ActionManager调用,设置_target的值。
update函数在ActionInterval的step函数中会调用,在这个函数中不断更新精灵的坐标,使用了自由落体计算位移的公式。
isDone函数设置了动作是否运行结束。 重力加速度和action运行的时间需要不断调试。

添加水管:





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

function getRandom(maxSize) {

    return
Math.floor(Math.random() * maxSize) % maxSize;

}

 

Helloworld.prototype.addPipe = function
() { 

    cc.log("addPipe");

    var
ccSpriteDown = cc.Sprite.createWithSpriteFrameName(res.holdback1);

    var
pipeHeight = ccSpriteDown.getContentSize().height;

    var
pipeWidth = ccSpriteDown.getContentSize().width;

    var
groundHeight = this.groundSprite.getContentSize().height;

        //小鸟飞行区间高度

    var
acrossHeight = 300;

    var
downPipeHeight = 100 + getRandom(400);

   // cc.log("downPipeHeight=" + downPipeHeight);

 

    var
upPipeHeight = this.winSize.height - downPipeHeight - acrossHeight - groundHeight;

    var
PipeX = this.winSize.width + pipeWidth / 2;

    ccSpriteDown.setZOrder(1);

    ccSpriteDown.setAnchorPoint(cc.p(0.5, 0.5));

    ccSpriteDown.setPosition(cc.p(PipeX + pipeWidth / 2, groundHeight + pipeHeight / 2 - (pipeHeight - downPipeHeight)));

 

var ccSpriteUp = cc.Sprite.createWithSpriteFrameName(res.holdback2);

    ccSpriteUp.setZOrder(1);

    ccSpriteUp.setAnchorPoint(cc.p(0.5, 0.5));

    ccSpriteUp.setPosition(cc.p(PipeX + pipeWidth / 2, this.winSize.height + (pipeHeight- upPipeHeight) - pipeHeight / 2));

 

 this.addChild(ccSpriteDown, PIPE_Z);

    this.addChild(ccSpriteUp, PIPE_Z);

 

  this.PipeSpriteList.push(ccSpriteDown);

    this.PipeSpriteList.push(ccSpriteUp);

 

 this.score += 1;

};

分为上下两根水管,随机设置上下水管的高度,固定小鸟飞行区间的高度为300。然后把创建的水管放到数组中,同时每添加一排水管就增加一分。

添加碰撞检测函数:





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

Helloworld.prototype.getRect = function(a) {

     var
pos = a.getPosition();

     var
content = a.getContentSize();

     return
cc.rect(pos.x - content.width / 2, pos.y - content.height / 2, content.width, content.height);

};

 

Helloworld.prototype.collide = function
(a, b) {

    var
aRect = this.getRect(a);

    var
bRect = this.getRect(b);

    return
cc.rectIntersectsRect(aRect, bRect);

};

 

Helloworld.prototype.checkCollision = function
() {

    if
(this.collide(this.flyBird, this.groundSprite)) {

        //cc.log("hit floor");

        this.birdFallAction();

        return;

    }

    for
(var i = 0; i < this.PipeSpriteList.length; i++) {

        var
pipe = this.PipeSpriteList[i];

        if
(this.collide(this.flyBird, pipe)) {

            cc.log("hit pipe i="
+ i);

            this.birdFallAction();

            break;

        }

    }

}

采用最简单的方式:判断矩形是否相交。把小鸟分别跟地面和数组中的水管进行检测,如果发生碰撞,则执行小鸟死亡动画:





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

Helloworld.prototype.birdFallAction = function
() {

    this.gameMode = OVER;

    this.flyBird.stopAllActions();

    this.groundSprite.stopAllActions();

    var
birdX = this.flyBird.getPositionX();

    var
birdY = this.flyBird.getPositionY();

 

    var
bottomY = this.groundSprite.getContentSize().height + this.flyBird.getContentSize().width / 2;

    var
fallMoveAction = FreeFall.create(birdY - bottomY);

    var
fallRotateAction =cc.RotateTo.create(0, 90);

    var
fallAction = cc.Spawn.create(fallMoveAction, fallRotateAction);

    this.flyBird.runAction(cc.Sequence.create(cc.DelayTime.create(0.1),

        fallAction)

    );

 

    this.runAction(cc.Sequence.create(cc.DelayTime.create(1.0),

        cc.CallFunc.create(this.showGameOver, this))

    );

}

让小鸟旋转90度,然后垂直下落,然后显示game over画面:





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

Helloworld.prototype.showGameOver = function
() {

    var
userDefault = cc.UserDefault.getInstance();

    var
oldScore = userDefault.getIntegerForKey("score");

    var
maxScore = 0;

    if(this.score > oldScore) {

        maxScore = this.score;

        userDefault.setIntegerForKey("score", maxScore);

    }else
{

        maxScore = oldScore;

    }

 

    var
gameOverLayer = cc.Layer.create();

    cc.log("gameover="
+ res.gameover);

    var
gameOver = cc.Sprite.createWithSpriteFrameName(res.gameover);

    gameOver.setAnchorPoint(cc.p(0.5, 0.5));

    gameOver.setPosition(this.winSize.width / 2, this.winSize.height - gameOver.getContentSize().height / 2 - 150);

    gameOverLayer.addChild(gameOver);

 

    var
scorePanel = cc.Sprite.createWithSpriteFrameName(res.scorePanel);

    scorePanel.setAnchorPoint(cc.p(0.5, 0.5));

    scorePanel.setPosition(gameOver.getPositionX(), gameOver.getPositionY() - gameOver.getContentSize().height / 2 - scorePanel.getContentSize().height / 2 - 60);

    gameOverLayer.addChild(scorePanel);

 

    if(this.score > oldScore) {

        var
gold = cc.Sprite.createWithSpriteFrameName(res.gold);

        gold.setAnchorPoint(cc.p(0.5, 0.5));

        gold.setPosition(68 + gold.getContentSize().width / 2, 72 + gold.getContentSize().height / 2);

        scorePanel.addChild(gold);

    }else
{

        var
gray = cc.Sprite.createWithSpriteFrameName(res.gray);

        gray.setAnchorPoint(cc.p(0.5, 0.5));

        gray.setPosition(68 + gray.getContentSize().width / 2, 72 + gray.getContentSize().height / 2);

        scorePanel.addChild(gray);

    }

 

    var
newScoreLabel = cc.LabelAtlas.create(this.score, res.number, 22, 28, ‘0‘);

    newScoreLabel.setAnchorPoint(cc.p(0.5, 0.5));

    newScoreLabel.setScale(1.2);

    newScoreLabel.setPosition(scorePanel.getContentSize().width - newScoreLabel.getContentSize().width - 90, newScoreLabel.getContentSize().height / 2 + 180);

    scorePanel.addChild(newScoreLabel);

 

    var
maxScoreLabel = cc.LabelAtlas.create(maxScore, res.number, 22, 28, ‘0‘);

    maxScoreLabel.setAnchorPoint(cc.p(0.5, 0.5));

    maxScoreLabel.setScale(1.2);

    maxScoreLabel.setPosition(newScoreLabel.getPositionX(), maxScoreLabel.getContentSize().height / 2 + 75);

    scorePanel.addChild(maxScoreLabel);

 

    var
start = cc.Sprite.createWithSpriteFrameName(res.start);

    var
startMenuItem = cc.MenuItemSprite.create(start, null, null, this.restartGame, this);

    var
startMenu = cc.Menu.create(startMenuItem);

    startMenu.setAnchorPoint(cc.p(0.5, 0.5));

    startMenu.setPosition(this.winSize.width / 2 , scorePanel.getPositionY() - scorePanel.getContentSize().height / 2 - start.getContentSize().height / 2 - 60);

    gameOverLayer.addChild(startMenu);

 

    this.addChild(gameOverLayer, GAMEOVER_Z);

};

显示game over时保存游戏数据,显示这局游戏的分数和历史最高分。

点击开始游戏按钮,就可以重新开始游戏:





1

2

3

4

5

Helloworld.prototype.restartGame = function() {

    var
scene = cc.Scene.create();

    scene.addChild(Helloworld.create());

    cc.Director.getInstance().replaceScene(cc.TransitionFade.create(1.2, scene));

};

记得在init函数中清空水管数组:





1

this.PipeSpriteList = [];

下面是Helloworld类的代码:





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

var Helloworld = cc.Layer.extend({

    gameMode:null,

    bgSprite:null,

    groundSprite:null,

    flyBird:null,

    PipeSpriteList:[],

    passTime: 0,

    winSize: 0,

    screenRect:null,

    readyLayer:null,

    score: 0,

    scoreLabel:null,

 

    init:function
() {

        cc.log("helloworld init");

        this._super();

        this.PipeSpriteList = [];

        this.winSize = cc.Director.getInstance().getWinSize();

        cc.SpriteFrameCache.getInstance().addSpriteFrames(res.flappy_packer);

        this.bgSprite = cc.Sprite.create(res.bg);

        this.bgSprite.setPosition(this.winSize.width / 2, this.winSize.height / 2);

        this.addChild(this.bgSprite, 0);

        this.initGround();

        this.initReady();

        this.screenRect = cc.rect(0, 0, this.winSize.width, this.winSize.height);

        this.gameMode = READY;

        this.score = 0;

        this.scheduleUpdate();

        this.setTouchEnabled(true);

        return
true;

    },

 

    onTouchesBegan:function
(touches, event) {

    },

 

    onTouchesMoved:function
(touches, event) {

    },

 

    onTouchesEnded:function
(touches, event) {

        if
(this.gameMode == OVER) {

            return;

        }

        if
(this.gameMode == READY) {

            this.gameMode = START;

            this.readyLayer.setVisible(false);

            this.initBird();;

        }

        this.runBirdAction();

    },

 

    onTouchesCancelled:function
(touches, event) {

    },

 

    update:function(dt) {

        if
(this.gameMode != START) {

            return;

        }

        for(var
i = 0; i < this.PipeSpriteList.length; ++ i) {

            var
pipe = this.PipeSpriteList[i];

            pipe.setPositionX(pipe.getPositionX() - 3);

            if
(pipe.getPositionX() < -pipe.getContentSize().width / 2) {

                this.PipeSpriteList.splice(i, 1);

                //cc.log("delete pipe i=" + i);

            }

        }

        this.passTime += 1;

        if(this.passTime >= this.winSize.width / 6) {

            this.addPipe();

            this.passTime = 0;

        }

        this.checkCollision();

    }

});

在update函数中更新水管的位置,如果水管出了左边的屏幕就从数组中移除,每经过一定的时间就添加一排水管。
现在看看index.html的内容,在浏览器中访问的就是它:





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

<!DOCTYPE HTML>

<html>

<head>

    <meta
charset="utf-8">

    <title>Flappy Bird-codingnow.cn</title>

    <link
rel="icon"
type="image/png"
href="http://codingnow.cn/favicon.ico">

    <meta
name="viewport"
content="user-scalable=no"/>

    <meta
name="screen-orientation"
content="portrait"/>

    <meta
name="apple-mobile-web-app-capable"
content="yes"/>

    <meta
name="full-screen"
content="yes"/>

    <meta
name="x5-fullscreen"
content="true"/>

    <style>

        body, canvas, div {

            -moz-user-select: none;

            -webkit-user-select: none;

            -ms-user-select: none;

            -khtml-user-select: none;

            -webkit-tap-highlight-color: rgba(0, 0, 0, 0);

        }

    </style>

</head>

<body
style="padding:0; margin: 0;text-align: center;background: #f2f6f8;">

    <canvas
id="gameCanvas"
width="720"
height="1280"></canvas>

<script
src="cocos2d.js"></script>

<img
style="position:absolute;left:-9999px"
src="http://zhoujianghai.github.io/games/flappybird/res/icon_wechat.png"
onerror="this.parentNode.removeChild(this)">

</body>

</html>

在这个文件中指定了canvas的尺寸。html的内容从cocos2d-html5自带的例子中copy过来的,这里在底部添加了一个img,这个图片是分享到微信朋友圈时显示在左边的图片。
当浏览器窗口大小改变时,为了能自动调整显示游戏完整画面,需要在main.js的applicationDidFinishLaunching函数中添加:





1

2

3

cc.EGLView.getInstance().adjustViewPort(true);

cc.EGLView.getInstance().setDesignResolutionSize(720, 1280, cc.RESOLUTION_POLICY.SHOW_ALL);

cc.EGLView.getInstance().resizeWithBrowserSize(true);

还记得那个build.xml文件么,可以使用ant打包工具,把src目录下的js代码跟引擎代码打包成一个myApp-HelloWorld.js文件,这样我们只需要把res资源、cocos2d.js、index.html、myApp-HelloWorld.js放到网站上就可以了,也就更安全了。切换到项目build.xml所在目录,在命令符窗口执行:ant。很快就会生成myApp-HelloWorld.js文件,然后还需要修改cocos2d.js的window.addEventListener函数,修改s.src的值为myApp-HelloWorld.js,cocos2d.js文件里有详细注释的。

ok,flappy bird游戏的主要代码就完成了,感觉cocos2d-html5还是非常强大的,开发效率很高。
现在还只能在本地运行游戏,为了能在外网访问,可以把项目传到github上,创建github pages,可以参考:http://pages.github.com/

时间: 2024-08-06 16:04:41

cocos2dx-html5 实现网页版flappy bird游戏的相关文章

基于网页版的2048游戏

前段时间有个很火的游戏叫2048,刚好趁着暑假想练习前端,顺带就把它作为练习项目做成Web Applications 该游戏基于HTML5+JS+CSS 文件结构: index.html 游戏界面展示 index.css 主界面的CSS样式,16方格采用绝对布局的方式 main.js 游戏的主要逻辑 show.js 游戏的一些动画效果 support.js 底层支撑 jquery.js 页面做了响应式布局,游戏效果如下: 代码如下: index.hml <!DOCTYPE html PUBLIC

网页版《2048游戏》教程 - 构建页面

1.     游戏标题 <2048>游戏的标题包含游戏名称.开始新游戏的按钮和游戏分数等三项内容. 创建游戏页面index.html: <!DOCTYPE html> <html > <head > < meta charset= "UTF-8"> < title>2048</title > < link rel= "stylesheet" type ="text/c

网页版《2048游戏》教程 - 游戏初始化

3.1.     初始化棋盘格 我们在main.js文件中,创建newgame()方法用于开始新的游戏.而开始新游戏需要完成两件事情,一是初始化棋盘格,一是在随机两个格子生成两个数字. $(function () { newgame(); }); function newgame() { // 初始化棋盘格 init(); // 在随机两个格子生成数字 generateOneNumber(); generateOneNumber(); } 我们通过编写init()方法来完成棋盘格的初始化工作.棋

网页版《2048游戏》教程 - 完成游戏逻辑

1.     捕获键盘事件 <2048>游戏的操作主要是依靠键盘的上.下.左.右来完成,首先我们需要在game.js文件中捕获键盘响应的事件. $(document).keydown(function (event) { switch (event.keyCode) { case 37://left break; case 38://up break; case 39://right break; case 40://down break; default : break; } }); 键盘事

网页版《2048游戏》教程 - 游戏优化

1.     GameOver部分 下面我们来分析游戏是如何结束的.一种情况是棋盘格中没有空的格子了,一种情况是棋盘格中没有可以移动的格子了. 首先,完成isgameover()方法的逻辑. function isgameover() { if (nospace(board) && nomove(board)) { gameover(); } } 其次,完成棋盘格中没有空的格子. function nospace(board) { for (var i = 0; i < 4; i++

html5 canvas简易版捕鱼达人游戏源码

插件描述:html5利用canvas写的一个js版本的捕鱼,有积分统计,鱼可以全方位移动,炮会跟着鼠标移动,第一次打开需要鼠标移出背景图,再移入的时候就可以控制炮的转动,因为是用的mouseover触发的. 找htm5,html5教程,html开发的朋友来涂志海个人博客网,这里有你想要的一切(万一没有的,请联系涂志海,再解决,嘿嘿) 下 载 演示地址 下载说明: 1.解压密码:tuzhihai.com 2.只有部分模板会提供多页面下载,未加说明都是只有一个首页index.html模板. 3.如果

飞翔的圆(Flappy Bird)游戏源码完整版

这个源码是一个不错的休闲类的游戏源码,飞翔的圆(Flappy Bird)游戏源码V1.0,本项目是一个仿Flappy Bird的小游戏,只不过是把Flappy Bird里面的鸟替换成了简单的圆.感兴趣的朋友可以研究一下.本项目默认编码GBK. 源码下载:http://code.662p.com/view/9013.html public class LoadingActivity extends Activity { @Override public void onCreate(Bundle s

C语言版flappy bird黑白框游戏

在此记录下本人在大一暑假,2014.6~8这段时间复习C语言,随手编的一个模仿之前很火热的小游戏----flappy bird.代码bug基本被我找光了,如果有哪位兄弟找到其他的就帮我留言下吧,谢谢了! 代码的完美度肯定是不够的,随手编的嘛,找完bug后就没再去想怎样优化它了,毕竟时间有限. 先说下它的设计思路吧,算法方面,基本是纯靠for if 语句加上纯粹的坐标x,y运算实现的,在下面的代码里,将会看到很多阿拉伯数字的加加减减.没有用到链表什么的,当然,我相信,如果用到链表的话,会更简单,代

SDL版Flappy bird代码分享

用SDL编写的Flappy bird电脑版. 请大家尊重原创,转载或者用到其中的函数请注明出处,以及作者(五十风) main.cpp /*************************************************************** * 版权所有 (C)2014, 五十风 * * 文件名称:main.cpp * 内容摘要:FlappyBird主函数文件 * 其它说明:无 * 当前版本:v1.2 * 作 者:五十风 * 完成日期:2014.11.4 * 代替版本:v1