在第一篇《怎样使用CCRenderTexture创建动态纹理》基础上,添加�创建动态山丘,原文《How To Create A Game Like Tiny Wings with Cocos2D 2.X Part 1》,在这里继续以Cocos2d-x进行实现。有关源代码、资源等在文章以下给出了地址。

过程例如以下:

1.使用上一篇的project;

2.加入�地形类Terrain,派生自CCNode类。文件Terrain.h代码例如以下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18
#pragma once

#include "cocos2d.h"







class Terrain : public cocos2d::CCNode

{

public:

    Terrain(void);

    ~Terrain(void);



    CREATE_FUNC(Terrain);

    CC_SYNTHESIZE_RETAIN(cocos2d::CCSprite*, _stripes, Stripes);



private:

    int _offsetX;

    cocos2d::CCPoint _hillKeyPoints[kMaxHillKeyPoints];

};

这里声明了一个_hillKeyPoints数组,用来存储每一个山丘顶峰的点,同一时候声明了一个_offsetX代表当前地形滚动的偏移量。文件Terrain.cpp代码例如以下:

1

2

3

4

5

6

7

8

9

10

11

12
#include "Terrain.h"

using namespace cocos2d;



Terrain::Terrain(void)

{

    _stripes = NULL;

    _offsetX = 0;

}



Terrain::~Terrain(void)

{

    CC_SAFE_RELEASE_NULL(_stripes);

}

添加�例如以下方法:

1

2

3

4

5

6

7

8

9

10

11

12
void Terrain::generateHills()

{

    CCSize winSize = CCDirector::sharedDirector()->getWinSize();

    ;

    ;

    ; i < kMaxHillKeyPoints; ++i)

    {

        _hillKeyPoints[i] = ccp(x, y);

        x += winSize.width / ;

        y = rand() % (int)winSize.height;

    }

}

这种方法用来生成随机的山丘顶峰的点。第一个点在屏幕的左側中间,之后的每个点,x轴方向移动半个屏幕宽度,y轴方向设置为0到屏幕高度之间的一个随机值。加入�下面方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23
bool Terrain::init()

{

    bool bRet = false;

    do 

    {

        CC_BREAK_IF(!CCNode::init());



        this->generateHills();



        bRet = true;

    } );



    return bRet;

}



void Terrain::draw()

{

    CCNode::draw();

    ; i < kMaxHillKeyPoints; ++i)

    {

        ccDrawLine(_hillKeyPoints[i - ], _hillKeyPoints[i]);

    }

}

init方法调用generateHills方法创建山丘,draw方法简单地绘制相邻点之间的线段,方便可视化调试。加入�下面方法:

1

2

3

4

5
void Terrain::setOffsetX(float newOffsetX)

{

    _offsetX = newOffsetX;

    ));

}

英雄沿着地形的x轴方法前进,地形向左滑动。因此,偏移量须要乘以-1,还有缩放比例。打开HelloWorldScene.h文件,加入�头文件引用:

1
#include "Terrain.h"

加入�例如以下变量:

1
Terrain *_terrain;

打开HelloWorldScene.cpp文件,在onEnter方法里,调用genBackground方法之前,添�例如以下代码:

1

2
_terrain = Terrain::create();

);

update方法里,最后面加入�例如以下代码:

1
_terrain->setOffsetX(offset);

改动genBackground方法为例如以下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24
void HelloWorld::genBackground()

{

    if (_background)

    {

        _background->removeFromParentAndCleanup(true);

    }



    ccColor4F bgColor = this->randomBrightColor();

    _background = , ); 



    CCSize winSize = CCDirector::sharedDirector()->getWinSize();

    _background->setPosition(ccp(winSize.width / , winSize.height / ));

    ccTexParams tp = {GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT};

    _background->getTexture()->setTexParameters(&tp);



    this->addChild(_background);



    ccColor4F color3 = this->randomBrightColor();

    ccColor4F color4 = this->randomBrightColor();

    CCSprite *stripes = , , );

    ccTexParams tp2 = {GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_CLAMP_TO_EDGE};

    stripes->getTexture()->setTexParameters(&tp2);

    _terrain->setStripes(stripes);

}

注意,每次触摸屏幕,地形上的条纹纹理都会随机生成一个新的条纹纹理,这方便于測试。此外,在Update方法里_background调用setTextureRect方法时,能够将offset乘以0.7,这样背景就会比地形滚动地慢一些。编译执行,能够看到一些线段,连接着山丘顶峰的点,例如以下图所看到的:



当看到山丘滚动,能够想象得到,这对于一个Tiny Wings游戏,并不能非常好的工作。因为採用y轴随机值,有时候山丘太高,有时候山丘又太低,并且x轴也没有足够的区别。可是如今已经有了这些測试代码,是时候用更好的算法了。

3.更好的山丘算法。使用Sergey的算法来进行实现。打开Terrain.cpp文件,改动generateHills方法为例如以下:

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
void Terrain::generateHills()

{

    CCSize winSize = CCDirector::sharedDirector()->getWinSize();



    ;

    ;

    ;

    ;



    float x = -minDX;

    ;



    float dy, ny;

    ; // +1 - going up, -1 - going  down

    ;

    ;



    ; i < kMaxHillKeyPoints; ++i)

    {

        _hillKeyPoints[i] = ccp(x, y);

        )

        {

            x = ;

            y = winSize.height / ;

        } 

        else

        {

            x += rand() % rangeDX + minDX;

            while (true)

            {

                dy = rand() % rangeDY + minDY;

                ny = y + dy * sign;

                if (ny < winSize.height - paddingTop && ny > paddingBottom)

                {

                    break;

                }

            }

            y = ny;

        }

        sign *= -;

    }

}

这个算法运行的策略例如以下:

  • 在范围160加上0-80之间的随机数进行递增x轴。
  • 在范围60加上0-40之间的随机数进行递增y轴。
  • 每次都反转y轴偏移量。
  • 不要让y轴值过于接近顶部或底部(paddingTop, paddingBottom)。
  • 開始于屏幕外的左側,硬编码第二个点为(0, winSize.height/2),所以左側屏幕外有一个山丘。

编译执行,如今能够看到一个更好的山丘算法,例如以下图所看到的:



4.一次仅仅绘制部分。在更进一步之前,须要做出一项重大的性能优化。如今,绘制出了山丘的1000个顶峰点,即使每次都仅仅有少数在屏幕上看得到。所以,能够依据屏幕区域来计算哪些顶峰点会被显示出来,然后仅仅显示那些点,例如以下图所看到的:



打开Terrain.h文件,加入�例如以下变量:

1

2
int _fromKeyPointI;

int _toKeyPointI;

打开Terrain.cpp文件,在构造函数里面加入�例如以下代码:

1

2
_fromKeyPointI = ;

_toKeyPointI = ;

加入�例如以下方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17
void Terrain::resetHillVertices()

{

    CCSize winSize = CCDirector::sharedDirector()->getWinSize();



    ;

    ;



    // key points interval for drawing

    ].x < _offsetX - winSize.width /  / this->getScale())

    {

        _fromKeyPointI++;

    }

     / this->getScale())

    {

        _toKeyPointI++;

    }

}

这里,遍历每个顶峰点(从0開始),将它们的x轴值拿来做比較。不管当前相应到屏幕左边缘的偏移量设置为多少,仅仅要将它减去winSize.width/8。假设顶峰点的x轴值小于结果值,那么就继续遍历,直到找到一个大于结果值的,这个顶峰点就是显示的起始点。对于toKeypoint也採用相同的过程。改动draw方法,代码例如以下:

1

2

3

4

5

6

7

8

9
void Terrain::draw()

{

    CCNode::draw();

    ); i <= _toKeyPointI; ++i)

    {

        ccDrawColor4F(., , , .);

        ccDrawLine(_hillKeyPoints[i - ], _hillKeyPoints[i]);

    }

}

如今,不是绘制全部点,而是仅仅绘制当前可见的点,这些点是前面计算得到的。另外,也把线的颜色改成红色,这样更易于分辨。接着,在init方法里面,最后面加入�例如以下代码:

1
this->resetHillVertices();

setOffsetX方法里面,最后面加入�例如以下代码:

1
this->resetHillVertices();

为了更easy看到,打开HelloWorldScene.cpp文件,在onEnter方法,最后面加入�例如以下代码:

1
.);

编译执行,能够看到线段出现时才进行绘制,例如以下图所看到的:



5.制作平滑的斜坡。山丘是有斜坡的,而不是这样直上直下的直线。一个办法是使用余弦函数让山丘弯曲。回忆一下,余弦曲线就例如以下图所看到的:



因此,它是从1開始,每隔PI长度,曲线下降到-1。但怎么利用这个函数来创建一个美丽的曲线连接顶峰点呢?先仅仅考虑两个点的情况,例如以下图所看到的:



首先,须要分段绘制线,因此,须要每10个点创建一个区段。相同的,想要一个完整的余弦曲线,因此,能够将PI除以区段的数量,得到每一个点的角度。然后,让cos(0)相应p0的y轴值,而cos(PI)相应p1的y轴值。要做到这一点,将调用cos(angle),乘以p1和p0之间距离的一半(图上的ampl)。因为cos(0)=1,而cos(PI)=-1,所以,ampl在p0,而-ampl在p1。将它加上中点坐标,就能够得到想要的y轴值。打开Terrain.h文件,加入�区段长度定义,例如以下代码:

1

然后,打开Terrain.cpp文件,在draw方法里面,ccDrawLine之后,加入�例如以下代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20
ccDrawColor4F(., ., ., .);

CCPoint p0 = _hillKeyPoints[i - ];

CCPoint p1 = _hillKeyPoints[i];

int hSegments = floorf((p1.x - p0.x) / kHillSegmentWidth);

float dx = (p1.x - p0.x) / hSegments;

float da = M_PI / hSegments;

;

;



CCPoint pt0, pt1;

pt0 = p0;

; j < hSegments + ; ++j)

{

    pt1.x = p0.x + j * dx;

    pt1.y = ymid + ampl * cosf(da * j);



    ccDrawLine(pt0, pt1);



    pt0 = pt1;

}

打开HelloWorldScene.cpp文件,在onEnter方法,设置scale为1.0,例如以下代码:

1
.);

编译执行,如今能够看到一条曲线连接着山丘,例如以下图所看到的:



6.绘制山丘。用上一篇文章生成的条纹纹理来绘制山丘。计划是对山丘的每一个区段,计算出两个三角形来渲染山丘,例如以下图所看到的:



还将设置每一个点的纹理坐标。对于x坐标,简单地除以纹理的宽度(由于纹理反复)。对于y坐标,将山丘的底部映射为0,顶部映射为1,沿着条带的方向分发纹理高度。打开Terrain.h文件,加入�例如以下代码:

1

2

加入�类变量,代码例如以下:

1

2

3

4

5
int _nHillVertices;

cocos2d::CCPoint _hillVertices[kMaxHillVertices];

cocos2d::CCPoint _hillTexCoords[kMaxHillVertices];

int _nBorderVertices;

cocos2d::CCPoint _borderVertices[kMaxBorderVertices];

打开Terrain.cpp文件,在resetHillVertices方法里面,最后面加入�例如以下代码:

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
if (prevFromKeyPointI != _fromKeyPointI || prevToKeyPointI != _toKeyPointI)

{

    // vertices for visible area

    _nHillVertices = ;

    _nBorderVertices = ;

    CCPoint p0, p1, pt0, pt1;

    p0 = _hillKeyPoints[_fromKeyPointI];

    ; i < _toKeyPointI + ; ++i)

    {

        p1 = _hillKeyPoints[i];



        // triangle strip between p0 and p1

        int hSegments = floorf((p1.x - p0.x) / kHillSegmentWidth);

        float dx = (p1.x - p0.x) / hSegments;

        float da = M_PI / hSegments;

        ;

        ;

        pt0 = p0;

        _borderVertices[_nBorderVertices++] = pt0;

        ; j < hSegments + ; ++j)

        {

            pt1.x = p0.x + j * dx;

            pt1.y = ymid + ampl * cosf(da * j);

            _borderVertices[_nBorderVertices++] = pt1;



            _hillVertices[_nHillVertices] = ccp(pt0.x, );

            _hillTexCoords[_nHillVertices++] = ccp(pt0.x / , .0f);

            _hillVertices[_nHillVertices] = ccp(pt1.x, );

            _hillTexCoords[_nHillVertices++] = ccp(pt1.x / , .0f);



            _hillVertices[_nHillVertices] = ccp(pt0.x, pt0.y);

            _hillTexCoords[_nHillVertices++] = ccp(pt0.x / , );

            _hillVertices[_nHillVertices] = ccp(pt1.x, pt1.y);

            _hillTexCoords[_nHillVertices++] = ccp(pt1.x / , );



            pt0 = pt1;

        }



        p0 = p1;

    }



    prevFromKeyPointI = _fromKeyPointI;

    prevToKeyPointI = _toKeyPointI;

}

这里的大部分代码,跟上面的使用余弦绘制山丘曲线一样。新的部分,是将山丘每一个区段的顶点用来填充数组,每一个条纹须要4个顶点和4个纹理坐标。在draw方法里面,最上面加入�例如以下代码:

1

2

3

4

5

6

7

8

9

10
CC_NODE_DRAW_SETUP();



ccGLBindTexture2D(_stripes->getTexture()->getName());

ccGLEnableVertexAttribs(kCCVertexAttribFlag_Position | kCCVertexAttribFlag_TexCoords);



ccDrawColor4F(.0f, .0f, .0f, .0f);

glVertexAttribPointer(kCCVertexAttrib_Position, , GL_FLOAT, GL_FALSE, , _hillVertices);

glVertexAttribPointer(kCCVertexAttrib_TexCoords, , GL_FLOAT, GL_FALSE, , _hillTexCoords);



glDrawArrays(GL_TRIANGLE_STRIP, , (GLsizei)_nHillVertices);

这里绑定条纹纹理作为渲染纹理来使用,传入之前计算好的顶点数组和纹理坐标数组,然后以GL_TRIANGLE_STRIP来绘制这些数组。此外,凝视掉绘制山丘直线和曲线的代码。在init方法里面,调用generateHills方法之前,加入�例如以下代码:

1
this->setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTexture));

打开HelloWorldScene.cpp文件,在spriteWithColor1方法里面,凝视// Layer 4: Noise里,更改混合方式,代码例如以下:

1
ccBlendFunc blendFunc = {GL_DST_COLOR, CC_BLEND_DST};

编译执行,能够看到不错的山丘了,例如以下图所看到的:



7.还不完好?细致看山丘,可能会注意到一些不完好的地方,例如以下图所看到的:



添加�水平区段数量,能够提高一些质量。打开Terrain.h文件,改动kHillSegmentWidth为例如以下:

1

通过降低每一个区段的宽度,强制代码生成很多其它的区段来填充空间。编译执行,能够看到山丘看起来更好了。当然,代价是处理时间。效果例如以下图所看到的:



在第二部分,将会实现海豹飞翔。

參考资料:

1.How To Create A Game Like Tiny Wings with Cocos2D 2.X Part 1 http://www.raywenderlich.com/32954/how-to-create-a-game-like-tiny-wings-with-cocos2d-2-x-part-1

2.(译)怎样制作一个类似tiny wings的游戏:第一部分 http://www.cnblogs.com/zilongshanren/archive/2011/07/01/2095489.html

很感谢以上资料,本样例源码附加资源下载地址http://download.csdn.net/detail/akof1314/5733037

如文章存在错误之处,欢迎指出,以便改正。

怎样制作一个相似Tiny Wings的游戏 Cocos2d-x 2.1.4的更多相关文章

  1. 如何制作一个类似Tiny Wings的游戏(2) Cocos2d-x 2.1.4

    在第二篇<如何制作一个类似Tiny Wings的游戏>基础上,增加添加主角,并且使用Box2D来模拟主角移动,原文<How To Create A Game Like Tiny Wi ...

  2. 如何制作一个类似Tiny Wings的游戏 Cocos2d-x 2.1.4

    在第一篇<如何使用CCRenderTexture创建动态纹理>基础上,增加创建动态山丘,原文<How To Create A Game Like Tiny Wings with Co ...

  3. [0]尝试用Unity3d制作一个王者荣耀(持续更新)->游戏规划

    太得闲了于是想写个农药,虽然可能会失败但是还是要试一试. 因为是自学的不是Unity专业的可能表达语言会有些不标准!望见谅! 结构: 以组件式(比如说摇杆控制和玩家部分的编写是分离的,可以自由拼装)作 ...

  4. 怎样制作一个横版格斗过关游戏 Cocos2d-x 2.0.4

     本文实践自 Allen Tan 的文章<How To Make A Side-Scrolling Beat 'Em Up Game Like Scott Pilgrim with Coco ...

  5. three.js 制作一个三维的推箱子游戏

    今天郭先生发现大家更喜欢看我发的three.js小作品,今天我就发一个3d版本推箱子的游戏,其实webGL有很多框架,three.js并不合适做游戏引擎,但是可以尝试一些小游戏.在线案例请点击博客原文 ...

  6. 教你如何用python和pygame制作一个简单的贪食蛇游戏,可自定义

    1.效果图 2.完整的代码 #第1步:导出模块 import pygame, sys, random from pygame.locals import * # 第2步:定义颜色变量,在pygame中 ...

  7. 使用CocosSharp制作一个游戏 - CocosSharp中文教程

    注:本教程翻译自官方<Walkthrough - Building a game with CocosSharp>,官方教程有很多地方说的不够详细,或者代码不全,导致无法继续,本人在看了G ...

  8. 用Phaser来制作一个html5游戏——flappy bird (二)

    在上一篇教程中我们完成了boot.preload.menu这三个state的制作,下面我们就要进入本游戏最核心的一个state的制作了.play这个state的代码比较多,我不会一一进行说明,只会把一 ...

  9. 用Phaser来制作一个html5游戏——flappy bird (一)

    Phaser是一个简单易用且功能强大的html5游戏框架,利用它可以很轻松的开发出一个html5游戏.在这篇文章中我就教大家如何用Phaser来制作一个前段时间很火爆的游戏:Flappy Bird,希 ...

随机推荐

  1. linux中grep的用法

    http://www.9usb.net/200902/linux-grep.html http://blog.51yip.com/linux/1008.html http://blog.csdn.ne ...

  2. 基于visual Studio2013解决C语言竞赛题之0302字符数出

     题目 解决代码及点评 根据题目要求,只要根据用户输入的字母,判断字母之后,给出相应的输出即可 在以下代码中,f32函数实现了该功能,通过if条件判断语句 #include <stdio.h ...

  3. paip.tree 生成目录树到txt后的折叠查看

    paip.tree 生成目录树到txt后的折叠查看 作者Attilax ,  EMAIL:1466519819@qq.com  来源:attilax的专栏 地址:http://blog.csdn.ne ...

  4. 乐视(letv)网tkey破解

    乐视网tkey算法频繁变动,怎样才干获得她算法的源代码,以不变应万变? 本文仅仅用于技术交流.提醒各位尊重站点版权,请勿用于其他用途,否则后果自负! 使用软件 Adobe Flash Builder ...

  5. 跟我一起学extjs5(25--模块Form的自己定义的设计[3])

    跟我一起学extjs5(25--模块Form的自己定义的设计[3])         自己定义的Form已经能够执行了,以下改一下配置,把Form里面的FieldSet放在Tab之下.改动一下Modu ...

  6. Chapter 6 装饰模式

    修饰模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式.就功能而言,修饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能. 例如:DataOutputStrea ...

  7. 在VC6.0中能不能使用Duilib界面库呢?

    Duilib库的源代码是在vs2010下编译的,一般适用于vs2008及以上的版本开发使用,那么duilib能不能在vc6.0的工程中使用呢?如何在vc6.0中使用duilib库呢? 今天,由于工作要 ...

  8. 为什么要用BASE64

    BASE64和其他相似的编码算法通常用于转换二进制数据为文本数据,其目的是为了简化存储或传输.更具体地说,BASE64算法主要用于转换二进 制数据为ASCII字符串格式.Java语言提供了一个非常好的 ...

  9. solrCloud+tomcat+zookeeper配置

    一.环境准备: Solr版本:4.7.0 下载地址:http://www.apache.org/dyn/closer.cgi/lucene/solr/4.7.0 Tomcat版本:6.0.39 下载地 ...

  10. WebFetch 是无依赖极简网页爬取组件

    WebFetch 是无依赖极简网页爬取组件,能在移动设备上运行的微型爬虫. WebFetch 要达到的目标: 没有第三方依赖jar包 减少内存使用 提高CPU利用率 加快网络爬取速度 简洁明了的api ...