如何制作一个简单的游戏 用cocos2dx制作小游戏-x 2.0.4

教你用Cocos2d-x 3.4制作一个简单的游戏 - cocos2dx技术 - 泰课在线 - 国内专业的Unity在线学习平台|Unity3d培训|Unity教程|Unity教程 Unreal 虚幻 AR|移动开发|美术CG|UI平面设计|前端开发 - Powered By EduSoho
教你用Cocos2d-x 3.4制作一个简单的游戏
& & & & 用一个简单游戏的制作教程,让你能从头到尾的了解一下,怎么样为你的smart phone去制作一个2d游戏。
安装 Cocos2d-x
Cocos2d-x 3.x配备的新的安装程序,所以入门从未如此简单!!!
只需要(3.4版本或者其他版本),在终端输入python setup.py将cocos添加到环境变量
脚本运行后你需要重启下你的终端或者设备以使配置生效。
Hello World
首先让我们先创建一个简单的Hello World项目。
打开终端切换到你打算放置项目的目录,然后输入&cocos new,你将获得如下提示:
根据提示信息输入cocos new -p com.wangshaui.helloworld -l cpp HelloWorld,获得如下信息则表示创建成功
运行已创建完成的项目,将得到以下效果
点击左下角的按钮将退出游戏。在本教程中,你将和Cocos2d-x 一起见证奇迹。
忍者登场!
在忍者登场之前,你将需要做一些美术工作...
第一步,下载这个项目的。解压这个文件,并且将资源文件放在Resource文件夹中
第二步,打开HelloWorldScene.cpp。记住,这里的代码是用来显示上图所示场景的,并且这将成为一个建立游戏的好地方。在你修改它之前,先好好看看这部分实例代码:
&&&&boolHelloWorld::init()
&&&&//////////////////////////////
&&&&// 1. super init first
&&&&if( !Layer::init() )
&&&&Size visibleSize = Director::getInstance()-&getVisibleSize();
&&&&Vec2 origin = Director::getInstance()-&getVisibleOrigin();
&&&&/////////////////////////////
&&&&// 2. add a menu item with "X" image, which is clicked to quit the program
&&&&//&&& you may modify it.
&&&&// add a "close" icon to exit the progress. it's an autorelease object
&&&&auto closeItem = MenuItemImage::create(
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"CloseNormal.png",
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"CloseSelected.png",
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&CC_CALLBACK_1(HelloWorld::menuCloseCallback,this));
&&&&closeItem-&setPosition(Vec2(origin.x + visibleSize.width - closeItem-&getContentSize().width/2 ,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&origin.y + closeItem-&getContentSize().height/2));
&&&&// create menu, it's an autorelease object
&&&&auto menu = Menu::create(closeItem, NULL);
&&&&menu-&setPosition(Vec2::ZERO);
&&&&this-&addChild(menu, 1);
&&&&/////////////////////////////
&&&&// 3. add your codes below...
&&&&// add a label shows "Hello World"
&&&&// create and initialize a label
&&&&auto label = Label::createWithTTF("Hello World","fonts/Marker Felt.ttf", 24);
&&&&// position the label on the center of the screen
&&&&label-&setPosition(Vec2(origin.x + visibleSize.width/2,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&origin.y + visibleSize.height - label-&getContentSize().height));
&&&&// add the label as a child to this layer
&&&&this-&addChild(label, 1);
&&&&// add "HelloWorld" splash screen"
&&&&auto sprite = Sprite::create("HelloWorld.png");
&&&&// position the sprite on the center of the screen
&&&&sprite-&setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
&&&&// add the sprite as a child to this layer
&&&&this-&addChild(sprite, 0);
如果要添加一个忍者到场景中,你认为该怎么做。
你认为你该怎么做?这里有一些提示:
在你不久前添加进项目中的ResourcePack中找到忍者的图片。
你仅仅只需要修改一行代码!!
在HelloWorldScene.h中加入一个私有变量,代码如下:
在HelloWorldScene.cpp中加入如下代码:
_player = Sprite::create("player-hd.png");
_player-&setPosition(visibleSize.width/8,visibleSize.height/2);
this-&addChild(_player);
让我们看看这个忍者在不打怪兽的时候会做些什么,构建并运行项目。
然而,忍者用他一生去训练就是为了战斗!所以接下来你将要添加一些怪兽去挑战忍者!
一个凶猛的怪兽出现了
接下来你想在你的场景中加入一些怪兽。对于一个有经验的忍者来说,一个静止的怪兽是没有挑战的,所以要做一点有趣的事情,那就是让这些怪兽动起来。你将要创造一个怪兽让它略微向右偏离屏幕,并且为他们建立一个Aciton,告诉他们要从右边移动到左边。
将下面的代码加入到HelloWorldScene.cpp中:
voidHelloWorld::addMonster(floatdt)
Size visibleSize = Director::getInstance()-&getVisibleSize();
Sprite* MonsterExample = Sprite::create("monster-hd.png");
MonsterExample-&setPosition(
&&&&&&&&&&&&&&&&&&&&&&&&&&&&visibleSize.width + MonsterExample-&getContentSize().width,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&MonsterExample-&getContentSize().height/2+CCRANDOM_0_1()*(visibleSize.height - MonsterExample-&getContentSize().height/2));
Monster.push_back(MonsterExample);
this-&addChild(Monster.back());
MoveTo* Move = MoveTo::create(3, Vec2(-Monster.back()-&getContentSize().width/2,Monster.back()-&getPosition().y));
Monster.back()-&runAction(Move);
接下来让我一步一步的讲解:
获取屏幕的分辨率。
Monster是一个std::vector&sprite*&类型的变量用于存储怪兽。
你将会用到Cocos2D动作MoveTo:,让怪兽从起始点(略微偏离屏幕向右)移动到终点(略微偏离屏幕向左),使其迅速的从屏幕右边移动到左边。
你已经见到过了动作中的转动动作,然后Cocos2D提供了大量非常好用的内置动作,比如移动动作、旋转动作、渐隐动作、动画动作等等。
MoveTo:&这个动作是用来控制怪兽移动的,在这里是用来使怪兽从屏幕右边移动到左边。这段路程的持续时间是需要设置的,它表示怪兽穿越屏幕所需要的时间,持续时间越短,忍者的速度就会越快.
好,现在你有方法添加一个怪兽到你的场景中。然而一个怪兽对于一个有经验的忍者是很难构成威胁的,所以让我们创建一个怪物刷新器。
Cocos2d拥有一个调度程序,允许你每X.X秒回调一次,这样你就能创建一个怪物刷新器,让它每X秒加入一个新的怪兽到场景中。
打开HelloWorldScene.cpp并且加入如下代码在init方法中。
this-&schedule(schedule_selector(HelloWorld::addMonster),1);
这样就会添加一个回调计时器让以前添加的addMonster方法每1秒添加一个怪兽
注意,当你创建addMonster方法时,这里会有一个名为dt的参数,这个代表增量时间,就相当于找出之前时间与当前的不同的地方,调度器要求每一个方法都拥有这样一个参数。然而你不会在这个教程中用到这个。
构建并运行,现在你能看见众多的怪兽飞过屏幕!
&不幸的是你的忍者还没有足够的等级去发射火球,所以你需要依靠你专业的投射基恩能够去消灭这些怪兽。 & &握住你的手里剑并且让我们添加一些投掷动作。 & &你又将用到MoveTo:,这并不像之前的移动那样简单,但也仅仅需要从_player-&getPosition移动到点击点就完成了。如果你想沿着你点击的方向投掷手里剑穿越整个屏幕,你只需要用上一点点数学知识。 & 你必须从原点到点击点构造一个三角形并且再用同样的比例构造一个更大的三角形 — 而且你知道你想要的终点是在屏幕之外。
去做一些计算,它是真的有帮助,如果你知道一些基本的数学矢量知识的话(例如一些轻松加减向量的方法),Cocos2D包含有一个好用的矢量处理功能,就比如ccpAdd和cppSub。
在HelloWorldScene.h中添加如下代码:
virtualboolonTouchBegan(Touch* pTouch, Event* pEvent);
virtualvoidonTouchMoved(Touch* pTouch, Event* pEvent);
virtualvoidonTouchEnded(Touch* pTouch, Event* pEvent);
并且添加一个私有变量:
EventListenerTouchOneByOne* touch_
这个变量用来监听单点触摸事件。
在HelloWorldScene.cpp中的init中添加如下代码:
<div class="line number7 index6 alt2
后发表看法
Unity3D技术交流1群
工作时间: 9:00 - 17:00
北京客服: 010 -
广州客服: 020 -6986人阅读
3.1 Cocos2d-x(22)
& & & &在第一篇《》和第二篇《》基础上,增加游戏难度和关卡。原文《》,在这里继续以Cocos2d-x进行实现。有关源码、资源等在文章下面给出了地址。
步骤如下:
1.使用上一篇的工程;
2.下载本游戏所需的资源,将资源放置&Resources&目录下:
3.创建不同类型的怪物。一种软弱的,但是移动快速的怪物,另一种强壮的,但是移动缓慢的怪物。现在创建一个Monster类,基类为CCSprite。同时也创建以Monster为基类的两种类型怪物类。右键工程,&Add&→&Class...&→&C&#43;&#43;&→&Add&,&Base class&为CCSprite,&Class name&为Monster,如下图所示:
Monster.h文件代码为:
#pragma&once
#include&&cocos2d.h&
class&Monster&:
&&&&public&cocos2d::CCSprite
&&&&virtual&bool&initWithFile(const&char&*pszFilename,&int&hp,&int&minMoveDuration,&int&maxMoveDuration);
protected:
&&&&CC_SYNTHESIZE(int,&_hp,&Hp);
&&&&CC_SYNTHESIZE(int,&_minMoveDuration,&MinMoveDuration);
&&&&CC_SYNTHESIZE(int,&_maxMoveDuration,&MaxMoveDuration);
class&WeakAndFastMonster&:&public&Monster
&&&&virtual&bool&init(void);
&&&&CREATE_FUNC(WeakAndFastMonster);
class&StrongAndSlowMonster&:&public&Monster
&&&&virtual&bool&init(void);
&&&&CREATE_FUNC(StrongAndSlowMonster);
Monster.cpp文件代码为:
#include&&Monster.h&
bool&Monster::initWithFile(const&char&*pszFilename,&int&hp,&int&minMoveDuration,&int&maxMoveDuration)
&&&&bool&bRet&=&false;
&&&&&&&&CC_BREAK_IF(!&CCSprite::initWithFile(pszFilename));
&&&&&&&&this-&_hp&=&
&&&&&&&&this-&_minMoveDuration&=&minMoveD
&&&&&&&&this-&_maxMoveDuration&=&maxMoveD
&&&&&&&&bRet&=&true;
&&&&}&while&(0);
&&&&return&bR
bool&WeakAndFastMonster::init(void)
&&&&return&Monster::initWithFile(&monster.png&,&1,&3,&5);
bool&StrongAndSlowMonster::init(void)
&&&&return&Monster::initWithFile(&monster2.png&,&3,&6,&12);
4.在HelloWorldScene.cpp文件中,添加头文件引用:
#include&&Monster.h&
修改addMonster函数,改变创建怪物精灵的语句,如下代码:
//CCSprite&*monster&=&CCSprite::create(&monster.png&);
Monster&*monster&=&NULL;
if&(rand()&%&2&==&0)
&&&&monster&=&WeakAndFastMonster::create();
&&&&monster&=&StrongAndSlowMonster::create();
这样就有一半的机会创建不同类型的怪物。另外,我们定义了不同类型怪物的移动速度,故需更改移动时间,如下代码:
int&minDuration&=&monster-&getMinMoveDuration();&//2.0;
int&maxDuration&=&monster-&getMaxMoveDuration();&//4.0;
最后,修改update函数,更改_monsters循环的代码:
bool&monsterHit&=&false;
CCArray&*monstersToDelete&=&CCArray::create();
CCARRAY_FOREACH(_monsters,&pObject2)
&&&&Monster&*monster&=&(Monster*)pObject2;
&&&&if&(CCRect::CCRectIntersectsRect(projectile-&boundingBox(),&monster-&boundingBox()))
&&&&&&&&monsterHit&=&true;
&&&&&&&&monster-&setHp(monster-&getHp()&-&1);
&&&&&&&&if&(monster-&getHp()&&=&0)
&&&&&&&&&&&&monstersToDelete-&addObject(monster);
&&&&&&&&break;
&&&&}&&&&&&&&&&&
每次击中怪物,减少它的一个HP血量,只有当小于等于0时,才被消灭。并且,记得跳出循环,因为每个子弹只能打中一只怪物。最后,更改projectilesToDelete语句为:
//if&(monstersToDelete-&count()&&&0)
if&(monsterHit)
&&&&projectilesToDelete-&addObject(projectile);
&&&&CocosDenshion::SimpleAudioEngine::sharedEngine()-&playEffect(&explosion.wav&);
5.编译运行,可以看到不同类型的怪物出现在屏幕上,如下图所示:
6.多个关卡。需要创建一个类,记录所有的信息来区分不同的关卡。在这个简单的游戏中,只需记录三种信息:关卡序号、出现敌人的间隔时间(更高关卡将更快的出现怪物)、背景颜色(可以任意地分辨不同的关卡)。创建Level类,派生自CCObject。Level.h文件代码为:
#pragma&once
#include&&cocos2d.h&
class&Level&:
&&&&public&cocos2d::CCObject
&&&&virtual&bool&initWithLevelNum(int&levelNum,&float&secsPerSpawn,&cocos2d::ccColor4B&backgroundColor);
protected:
&&&&CC_SYNTHESIZE(int,&_levelNum,&LevelNum);
&&&&CC_SYNTHESIZE(float,&_secsPerSpawn,&SecsPerSpawn);
&&&&CC_SYNTHESIZE(cocos2d::ccColor4B,&_backgroundColor,&BackgroundColor);
Level.cpp文件代码为:
#include&&Level.h&
using&namespace&cocos2d;
bool&Level::initWithLevelNum(int&levelNum,&float&secsPerSpawn,&ccColor4B&backgroundColor)
&&&&this-&_levelNum&=&levelN
&&&&this-&_secsPerSpawn&=&secsPerS
&&&&this-&_backgroundColor&=&backgroundC
&&&&return&true;
接下去创建一个类,记录所有的关卡信息,以及当前处在哪个关卡。创建LevelManager类,派生自CCObject。LevelManager.h文件代码为:
#pragma&once
#include&&Level.h&
class&LevelManager&:
&&&&public&cocos2d::CCObject
&&&&static&LevelManager&*sharedInstance(void);
&&&&Level&*curLevel(void);
&&&&void&nextLevel(void);
&&&&void&reset(void);
&&&&bool&init();
&&&&void&end();
&&&&LevelManager(void);
&&&&~LevelManager(void);
&&&&cocos2d::CCArray&*_
&&&&int&_curLevelI
LevelManager.cpp文件代码为:
#include&&LevelManager.h&
using&namespace&cocos2d;
LevelManager::LevelManager(void)
&&&&_levels&=&NULL;
LevelManager::~LevelManager(void)
&&&&if&(_levels)
&&&&&&&&_levels-&release();
&&&&&&&&_levels&=&NULL;
LevelManager*&LevelManager::sharedInstance(void)
&&&&static&LevelManager&*s_SharedLevelManager&=&NULL;
&&&&if&(!s_SharedLevelManager)
&&&&&&&&s_SharedLevelManager&=&new&LevelManager();
&&&&&&&&s_SharedLevelManager-&init();
&&&&return&s_SharedLevelM
Level&*&LevelManager::curLevel(void)
&&&&if&(_curLevelIdx&&=&(int)_levels-&count())
&&&&&&&&return&NULL;
&&&&return&(Level*)_levels-&objectAtIndex(_curLevelIdx);
void&LevelManager::nextLevel(void)
&&&&_curLevelIdx&#43;&#43;;
void&LevelManager::reset(void)
&&&&_curLevelIdx&=&0;
bool&LevelManager::init()
&&&&_curLevelIdx&=&0;
&&&&Level&*level1&=&new&Level();
&&&&level1-&initWithLevelNum(1,&2,&ccc4(<span style="color:#ff,&<span style="color:#ff,&<span style="color:#ff,&<span style="color:#ff));
&&&&level1-&autorelease();
&&&&Level&*level2&=&new&Level();
&&&&level2-&initWithLevelNum(2,&1,&ccc4(<span style="color:#ff,&<span style="color:#ff,&20,&<span style="color:#ff));
&&&&level2-&autorelease();
&&&&_levels&=&CCArray::create(level1,&level2,&NULL);
&&&&_levels-&retain();
&&&&return&true;
void&LevelManager::end()
&&&&this-&release();
用一个数组来记录关卡,一个索引指示当前关卡,以及一些方法,包括:获得当前关卡、进入下一个关卡、重置到第一个关卡。这个类是单例的,要通过sharedInstance方法来操作。
7.在HelloWorldScene.cpp文件中,添加头文件引用:
#include&&LevelManager.h&
修改init函数,修改背景色的设置,如下代码:
CC_BREAK_IF(!&CCLayerColor::initWithColor(LevelManager::sharedInstance()-&curLevel()-&getBackgroundColor()));
修改游戏逻辑定时器的安装,如下代码:
this-&schedule(schedule_selector(HelloWorld::gameLogic),&LevelManager::sharedInstance()-&curLevel()-&getSecsPerSpawn());
这样就能根据不同的关卡,来设定不同的出现敌人间隔时间。
8.接着,在GameOverLayer.cpp文件中,添加头文件引用:
#include&&LevelManager.h&
在initWithWon函数,修改成如下代码:
bool&GameOverLayer::initWithWon(bool&won)
&&&&bool&bRet&=&false;
&&&&&&&&CC_BREAK_IF(!&CCLayerColor::initWithColor(ccc4(<span style="color:#ff,&<span style="color:#ff,&<span style="color:#ff,&<span style="color:#ff)));
&&&&&&&&CCString&*
&&&&&&&&if&(won)
&&&&&&&&&&&&LevelManager::sharedInstance()-&nextLevel();
&&&&&&&&&&&&Level&*curLevel&=&LevelManager::sharedInstance()-&curLevel();
&&&&&&&&&&&&if&(curLevel)
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&message&=&CCString::createWithFormat(&Get&ready&for&level&%d!&,&curLevel-&getLevelNum());
&&&&&&&&&&&&}&
&&&&&&&&&&&&else
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&message&=&CCString::create(&You&Won!&);
&&&&&&&&&&&&&&&&LevelManager::sharedInstance()-&reset();
&&&&&&&&&&&&}
&&&&&&&&}&
&&&&&&&&else
&&&&&&&&&&&&message&=&CCString::create(&You&Lose&:[&);
&&&&&&&&&&&&LevelManager::sharedInstance()-&reset();
&&&&&&&&CCSize&winSize&=&CCDirector::sharedDirector()-&getWinSize();
&&&&&&&&CCLabelTTF&*label&=&CCLabelTTF::create(message-&getCString(),&&Arial&,&32);
&&&&&&&&label-&setColor(ccc3(0,&0,&0));
&&&&&&&&label-&setPosition(ccp(winSize.width&/&2,&winSize.height&/&2));
&&&&&&&&this-&addChild(label);
&&&&&&&&this-&runAction(CCSequence::create(CCDelayTime::create(3),&
&&&&&&&&&&&&CCCallFunc::create(this,&callfunc_selector(GameOverLayer::gameOverDone)),
&&&&&&&&&&&&NULL));
&&&&&&&&bRet&=&true;
&&&&}&while&(0);
&&&&return&bR
9.最后,在AppDelegate.cpp文件,添加头文件引用:
#include&&LevelManager.h&
在AppDelegate析构函数中,添加如下代码:
LevelManager::sharedInstance()-&end();
10.编译运行,就可以看到不同类型的怪物、多个关卡的游戏了,如下图所示:
参考资料:
1.How To Make A Simple iPhone Game with Cocos2D 2.X Part 3
2.如何使用cocos2d来制作简单的iphone游戏:更猛的怪物和更多的关卡
非常感谢以上资料的学习,本例子源代码附加资源下载地址:
如文章存在错误之处,欢迎指出,以便改正。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:2632442次
积分:29508
积分:29508
排名:第123名
原创:381篇
转载:88篇
评论:3282条
联系方式:
(1)(3)(1)(1)(1)(1)(1)(1)(1)(2)(1)(1)(1)(1)(1)(3)(1)(1)(1)(1)(2)(1)(3)(3)(3)(1)(1)(3)(1)(1)(8)(1)(3)(2)(2)(3)(2)(3)(2)(1)(1)(2)(3)(6)(1)(4)(3)(1)(3)(5)(5)(5)(5)(1)(3)(5)(4)(4)(4)(5)(5)(1)(13)(11)(7)(5)(2)(5)(4)(5)(2)(7)(14)(18)(23)(19)(5)(35)(22)(21)(8)(10)(42)(49)9816人阅读
3.1 Cocos2d-x(22)
& & & &在第一篇《》基础上,增加创建动态山丘,原文《》,在这里继续以Cocos2d-x进行实现。有关源码、资源等在文章下面给出了地址。
步骤如下:
1.使用上一篇的工程;
2.添加地形类Terrain,派生自CCNode类。文件Terrain.h代码如下:
#pragma&once
#include&&cocos2d.h&
#define&kMaxHillKeyPoints&<span style="color:#ff
class&Terrain&:&public&cocos2d::CCNode
&&&&Terrain(void);
&&&&~Terrain(void);
&&&&CREATE_FUNC(Terrain);
&&&&CC_SYNTHESIZE_RETAIN(cocos2d::CCSprite*,&_stripes,&Stripes);
&&&&int&_offsetX;
&&&&cocos2d::CCPoint&_hillKeyPoints[kMaxHillKeyPoints];
这里声明了一个_hillKeyPoints数组,用来存储每个山丘顶峰的点,同时声明了一个_offsetX代表当前地形滚动的偏移量。文件Terrain.cpp代码如下:&
#include&&Terrain.h&
using&namespace&cocos2d;
Terrain::Terrain(void)
&&&&_stripes&=&NULL;
&&&&_offsetX&=&0;
Terrain::~Terrain(void)
&&&&CC_SAFE_RELEASE_NULL(_stripes);
增加如下方法:&
void&Terrain::generateHills()
&&&&CCSize&winSize&=&CCDirector::sharedDirector()-&getWinSize();
&&&&float&x&=&0;
&&&&float&y&=&winSize.height&/&2;
&&&&for&(int&i&=&0;&i&&&kMaxHillKeyP&&#43;&#43;i)
&&&&&&&&_hillKeyPoints[i]&=&ccp(x,&y);
&&&&&&&&x&&#43;=&winSize.width&/&2;
&&&&&&&&y&=&rand()&%&(int)winSize.
这个方法用来生成随机的山丘顶峰的点。第一个点在屏幕的左侧中间,之后的每一个点,x轴方向移动半个屏幕宽度,y轴方向设置为0到屏幕高度之间的一个随机&#20540;。添加以下方法:&
bool&Terrain::init()
&&&&bool&bRet&=&false;
&&&&&&&&CC_BREAK_IF(!CCNode::init());
&&&&&&&&this-&generateHills();
&&&&&&&&bRet&=&true;
&&&&}&while&(0);
&&&&return&bR
void&Terrain::draw()
&&&&CCNode::draw();
&&&&for&(int&i&=&1;&i&&&kMaxHillKeyP&&#43;&#43;i)
&&&&&&&&ccDrawLine(_hillKeyPoints[i&-&1],&_hillKeyPoints[i]);
init方法调用generateHills方法创建山丘,draw方法简单地绘制相邻点之间的线段,方便可视化调试。添加以下方法:&
void&Terrain::setOffsetX(float&newOffsetX)
&&&&_offsetX&=&newOffsetX;
&&&&this-&setPosition(ccp(-_offsetX&*&this-&getScale(),&0));
英雄沿着地形的x轴方法前进,地形向左滑动。因此,偏移量需要乘以-1,还有缩放比例。打开HelloWorldScene.h文件,添加头文件引用:&
#include&&Terrain.h&
添加如下变量:&
Terrain&*_
打开HelloWorldScene.cpp文件,在onEnter方法里,调用genBackground方法之前,加入如下代码:&
_terrain&=&Terrain::create();
this-&addChild(_terrain,&1);
在update方法里,最后面添加如下代码:&
_terrain-&setOffsetX(offset);
修改genBackground方法为如下:&
void&HelloWorld::genBackground()
&&&&if&(_background)
&&&&&&&&_background-&removeFromParentAndCleanup(true);
&&&&ccColor4F&bgColor&=&this-&randomBrightColor();
&&&&_background&=&this-&spriteWithColor(bgColor,&<span style="color:#ff,&<span style="color:#ff);&
&&&&CCSize&winSize&=&CCDirector::sharedDirector()-&getWinSize();
&&&&_background-&setPosition(ccp(winSize.width&/&2,&winSize.height&/&2));
&&&&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&=&this-&spriteWithColor1(color3,&color4,&<span style="color:#ff,&<span style="color:#ff,&4);
&&&&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轴随机&#20540;,有时候山丘太高,有时候山丘又太低,而且x轴也没有足够的差别。但是现在已经有了这些测试代码,是时候用更好的算法了。
3.更好的山丘算法。使用的算法来进行实现。打开Terrain.cpp文件,修改generateHills方法为如下:&
void&Terrain::generateHills()
&&&&CCSize&winSize&=&CCDirector::sharedDirector()-&getWinSize();
&&&&float&minDX&=&<span style="color:#ff;
&&&&float&minDY&=&60;
&&&&int&rangeDX&=&80;
&&&&int&rangeDY&=&40;
&&&&float&x&=&-minDX;
&&&&float&y&=&winSize.height&/&2;
&&&&float&dy,&
&&&&float&sign&=&1;&//&&#43;1&-&going&up,&-1&-&going&&down
&&&&float&paddingTop&=&20;
&&&&float&paddingBottom&=&20;
&&&&for&(int&i&=&0;&i&&&kMaxHillKeyP&&#43;&#43;i)
&&&&&&&&_hillKeyPoints[i]&=&ccp(x,&y);
&&&&&&&&if&(i&==&0)
&&&&&&&&&&&&x&=&0;
&&&&&&&&&&&&y&=&winSize.height&/&2;
&&&&&&&&}&
&&&&&&&&else
&&&&&&&&&&&&x&&#43;=&rand()&%&rangeDX&&#43;&minDX;
&&&&&&&&&&&&while&(true)
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&dy&=&rand()&%&rangeDY&&#43;&minDY;
&&&&&&&&&&&&&&&&ny&=&y&&#43;&dy&*&
&&&&&&&&&&&&&&&&if&(ny&&&winSize.height&-&paddingTop&&&&ny&&&paddingBottom)
&&&&&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&&&&&break;
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&}
&&&&&&&&&&&&y&=&
&&&&&&&&sign&*=&-1;
这个算法执行的策略如下:
在范围160加上0-80之间的随机数进行递增x轴。在范围60加上0-40之间的随机数进行递增y轴。每次都反转y轴偏移量。不要让y轴&#20540;过于接近顶部或底部(paddingTop,&paddingBottom)。开始于屏幕外的左侧,硬编码第二个点为(0,&winSize.height/2),所以左侧屏幕外有一个山丘。
编译运行,现在可以看到一个更好的山丘算法,如下图所示:
4.一次只绘制部分。在更进一步之前,需要做出一项重大的性能优化。现在,绘制出了山丘的1000个顶峰点,即使每次都只有少数在屏幕上看得到。所以,可以根据屏幕区域来计算哪些顶峰点会被显示出来,然后只显示那些点,如下图所示:
打开Terrain.h文件,添加如下变量:&
int&_fromKeyPointI;
int&_toKeyPointI;
打开Terrain.cpp文件,在构造函数里面添加如下代码:&
_fromKeyPointI&=&0;
_toKeyPointI&=&0;
添加如下方法:&
void&Terrain::resetHillVertices()
&&&&CCSize&winSize&=&CCDirector::sharedDirector()-&getWinSize();
&&&&static&int&prevFromKeyPointI&=&-1;
&&&&static&int&prevToKeyPointI&=&-1;
&&&&//&key&points&interval&for&drawing
&&&&while&(_hillKeyPoints[_fromKeyPointI&&#43;&1].x&&&_offsetX&-&winSize.width&/&8&/&this-&getScale())
&&&&&&&&_fromKeyPointI&#43;&#43;;
&&&&while&(_hillKeyPoints[_toKeyPointI].x&&&_offsetX&&#43;&winSize.width&*&9&/&8&/&this-&getScale())
&&&&&&&&_toKeyPointI&#43;&#43;;
这里,遍历每一个顶峰点(从0开始),将它们的x轴&#20540;拿来做比较。无论当前对应到屏幕左边缘的偏移量设置为多少,只要将它减去winSize.width/8。如果顶峰点的x轴&#20540;小于结果&#20540;,那么就继续遍历,直到找到一个大于结果&#20540;的,这个顶峰点就是显示的起始点。对于toKeypoint也采用同样的过程。修改draw方法,代码如下:&
void&Terrain::draw()
&&&&CCNode::draw();
&&&&for&(int&i&=&MAX(_fromKeyPointI,&1);&i&&=&_toKeyPointI;&&#43;&#43;i)
&&&&&&&&ccDrawColor4F(1.0,&0,&0,&1.0);
&&&&&&&&ccDrawLine(_hillKeyPoints[i&-&1],&_hillKeyPoints[i]);
现在,不是绘制所有点,而是只绘制当前可见的点,这些点是前面计算得到的。另外,也把线的颜色改成红色,这样更易于分辨。接着,在init方法里面,最后面添加如下代码:&
this-&resetHillVertices();
在setOffsetX方法里面,最后面添加如下代码:&
this-&resetHillVertices();
为了更容易看到,打开HelloWorldScene.cpp文件,在onEnter方法,最后面添加如下代码:
this-&setScale(0.25);
编译运行,可以看到线段出现时才进行绘制,如下图所示:
5.制作平滑的斜坡。山丘是有斜坡的,而不是这样直上直下的直线。一个办法是使用余弦函数让山丘弯曲。回想一下,余弦曲线就如下图所示:
因此,它是从1开始,每隔PI长度,曲线下降到-1。但怎么利用这个函数来创建一个漂亮的曲线连接顶峰点呢?先只考虑两个点的情况,如下图所示:
首先,需要分段绘制线,因此,需要每10个点创建一个区段。同样的,想要一个完整的余弦曲线,因此,可以将PI除以区段的数量,得到每个点的角度。然后,让cos(0)对应p0的y轴&#20540;,而cos(PI)对应p1的y轴&#20540;。要做到这一点,将调用cos(angle),乘以p1和p0之间距离的一半(图上的ampl)。由于cos(0)=1,而cos(PI)=-1,所以,ampl在p0,而-ampl在p1。将它加上中点坐标,就可以得到想要的y轴&#20540;。打开Terrain.h文件,添加区段长度定义,如下代码:&
#define&kHillSegmentWidth&10
然后,打开Terrain.cpp文件,在draw方法里面,ccDrawLine之后,添加如下代码:&
ccDrawColor4F(1.0,&1.0,&1.0,&1.0);
CCPoint&p0&=&_hillKeyPoints[i&-&1];
CCPoint&p1&=&_hillKeyPoints[i];
int&hSegments&=&floorf((p1.x&-&p0.x)&/&kHillSegmentWidth);
float&dx&=&(p1.x&-&p0.x)&/&hS
float&da&=&M_PI&/&hS
float&ymid&=&(p0.y&&#43;&p1.y)&/&2;
float&ampl&=&(p0.y&-&p1.y)&/&2;
CCPoint&pt0,&pt1;
for&(int&j&=&0;&j&&&hSegments&&#43;&1;&&#43;&#43;j)
&&&&pt1.x&=&p0.x&&#43;&j&*&
&&&&pt1.y&=&ymid&&#43;&ampl&*&cosf(da&*&j);
&&&&ccDrawLine(pt0,&pt1);
&&&&pt0&=&pt1;
打开HelloWorldScene.cpp文件,在onEnter方法,设置scale为1.0,如下代码:&
this-&setScale(1.0);
编译运行,现在可以看到一条曲线连接着山丘,如下图所示:
6.绘制山丘。用上一篇文章生成的条纹纹理来绘制山丘。计划是对山丘的每个区段,计算出两个三角形来渲染山丘,如下图所示:
还将设置每个点的纹理坐标。对于x坐标,简单地除以纹理的宽度(因为纹理重复)。对于y坐标,将山丘的底部映射为0,顶部映射为1,沿着条带的方向分发纹理高度。打开Terrain.h文件,添加如下代码:&
#define&kMaxHillVertices&<span style="color:#ff
#define&kMaxBorderVertices&<span style="color:#ff
添加类变量,代码如下:&
int&_nHillV
cocos2d::CCPoint&_hillVertices[kMaxHillVertices];
cocos2d::CCPoint&_hillTexCoords[kMaxHillVertices];
int&_nBorderV
cocos2d::CCPoint&_borderVertices[kMaxBorderVertices];
打开Terrain.cpp文件,在resetHillVertices方法里面,最后面添加如下代码:&
if&(prevFromKeyPointI&!=&_fromKeyPointI&||&prevToKeyPointI&!=&_toKeyPointI)
&&&&//&vertices&for&visible&area
&&&&_nHillVertices&=&0;
&&&&_nBorderVertices&=&0;
&&&&CCPoint&p0,&p1,&pt0,&pt1;
&&&&p0&=&_hillKeyPoints[_fromKeyPointI];
&&&&for&(int&i&=&_fromKeyPointI&&#43;&1;&i&&&_toKeyPointI&&#43;&1;&&#43;&#43;i)
&&&&&&&&p1&=&_hillKeyPoints[i];
&&&&&&&&//&triangle&strip&between&p0&and&p1
&&&&&&&&int&hSegments&=&floorf((p1.x&-&p0.x)&/&kHillSegmentWidth);
&&&&&&&&float&dx&=&(p1.x&-&p0.x)&/&hS
&&&&&&&&float&da&=&M_PI&/&hS
&&&&&&&&float&ymid&=&(p0.y&&#43;&p1.y)&/&2;
&&&&&&&&float&ampl&=&(p0.y&-&p1.y)&/&2;
&&&&&&&&pt0&=&p0;
&&&&&&&&_borderVertices[_nBorderVertices&#43;&#43;]&=&pt0;
&&&&&&&&for&(int&j&=&1;&j&&&hSegments&&#43;&1;&&#43;&#43;j)
&&&&&&&&&&&&pt1.x&=&p0.x&&#43;&j&*&
&&&&&&&&&&&&pt1.y&=&ymid&&#43;&ampl&*&cosf(da&*&j);
&&&&&&&&&&&&_borderVertices[_nBorderVertices&#43;&#43;]&=&pt1;
&&&&&&&&&&&&_hillVertices[_nHillVertices]&=&ccp(pt0.x,&0);
&&&&&&&&&&&&_hillTexCoords[_nHillVertices&#43;&#43;]&=&ccp(pt0.x&/&<span style="color:#ff,&1.0f);
&&&&&&&&&&&&_hillVertices[_nHillVertices]&=&ccp(pt1.x,&0);
&&&&&&&&&&&&_hillTexCoords[_nHillVertices&#43;&#43;]&=&ccp(pt1.x&/&<span style="color:#ff,&1.0f);
&&&&&&&&&&&&_hillVertices[_nHillVertices]&=&ccp(pt0.x,&pt0.y);
&&&&&&&&&&&&_hillTexCoords[_nHillVertices&#43;&#43;]&=&ccp(pt0.x&/&<span style="color:#ff,&0);
&&&&&&&&&&&&_hillVertices[_nHillVertices]&=&ccp(pt1.x,&pt1.y);
&&&&&&&&&&&&_hillTexCoords[_nHillVertices&#43;&#43;]&=&ccp(pt1.x&/&<span style="color:#ff,&0);
&&&&&&&&&&&&pt0&=&pt1;
&&&&&&&&p0&=&p1;
&&&&prevFromKeyPointI&=&_fromKeyPointI;
&&&&prevToKeyPointI&=&_toKeyPointI;
这里的大部分代码,跟上面的使用余弦绘制山丘曲线一样。新的部分,是将山丘每个区段的顶点用来填充数组,每个条纹需要4个顶点和4个纹理坐标。在draw方法里面,最上面添加如下代码:&
CC_NODE_DRAW_SETUP();
ccGLBindTexture2D(_stripes-&getTexture()-&getName());
ccGLEnableVertexAttribs(kCCVertexAttribFlag_Position&|&kCCVertexAttribFlag_TexCoords);
ccDrawColor4F(1.0f,&1.0f,&1.0f,&1.0f);
glVertexAttribPointer(kCCVertexAttrib_Position,&2,&GL_FLOAT,&GL_FALSE,&0,&_hillVertices);
glVertexAttribPointer(kCCVertexAttrib_TexCoords,&2,&GL_FLOAT,&GL_FALSE,&0,&_hillTexCoords);
glDrawArrays(GL_TRIANGLE_STRIP,&0,&(GLsizei)_nHillVertices);
这里绑定条纹纹理作为渲染纹理来使用,传入之前计算好的顶点数组和纹理坐标数组,然后以GL_TRIANGLE_STRIP来绘制这些数组。此外,注释掉绘制山丘直线和曲线的代码。在init方法里面,调用generateHills方法之前,添加如下代码:&
this-&setShaderProgram(CCShaderCache::sharedShaderCache()-&programForKey(kCCShader_PositionTexture));
打开HelloWorldScene.cpp文件,在spriteWithColor1方法里面,注释// Layer&4:&Noise里,更改混合方式,代码如下:&
ccBlendFunc&blendFunc&=&{GL_DST_COLOR,&CC_BLEND_DST};
编译运行,可以看到不错的山丘了,如下图所示:
7.还不完善?仔细看山丘,可能会注意到一些不完善的地方,如下图所示:
增加水平区段数量,可以提高一些质量。打开Terrain.h文件,修改kHillSegmentWidth为如下:&
#define&kHillSegmentWidth&5
通过减少每个区段的宽度,强制代码生成更多的区段来填充空间。编译运行,可以看到山丘看起来更好了。当然,代价是处理时间。效果如下图所示:
在第二部分,将会实现海豹飞翔。
参考资料:
1.How&To&Create&A&Game&Like&Tiny&Wings&with&Cocos2D&2.X&Part&1
2.(译)如何制作一个类&#20284;tiny&wings的游戏:第一部分
非常感谢以上资料,本例子源代码附加资源下载地址:
如文章存在错误之处,欢迎指出,以便改正。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:2632444次
积分:29508
积分:29508
排名:第123名
原创:381篇
转载:88篇
评论:3282条
联系方式:
(1)(3)(1)(1)(1)(1)(1)(1)(1)(2)(1)(1)(1)(1)(1)(3)(1)(1)(1)(1)(2)(1)(3)(3)(3)(1)(1)(3)(1)(1)(8)(1)(3)(2)(2)(3)(2)(3)(2)(1)(1)(2)(3)(6)(1)(4)(3)(1)(3)(5)(5)(5)(5)(1)(3)(5)(4)(4)(4)(5)(5)(1)(13)(11)(7)(5)(2)(5)(4)(5)(2)(7)(14)(18)(23)(19)(5)(35)(22)(21)(8)(10)(42)(49)}

我要回帖

更多关于 cocos2dx制作的游戏 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信