Cocos2dx游戏开发笔记21:动手学习《Flappy Bird》之继续优化(付源码)

懒骨头(http://blog.csdn.net/iamlazybone QQ:124774397 青岛)


笔记分好几次完成,可能有的地方转载的不全,请 传送到这里

《Flappy Bird》

Cocos2dx游戏开发笔记21:动手学习《Flappy Bird》之继续优化(付源码)_第1张图片

关于这个游戏骨头不多说了

直接开始学习吧(山寨不好听)

正好前段时间看了几个DEMO拿这个游戏练练手

开搞!


报环境:

vs2013+cocos2dx3.0beta2

首先下载apk,找到资源文件,裁剪几个图片:

Cocos2dx游戏开发笔记21:动手学习《Flappy Bird》之继续优化(付源码)_第2张图片

用脚本新建一个空的Cocos2dx项目

新建一个Scene类

#include "cocos2d.h"
#include "Obstacle.h"

class FlyBirdGame :public cocos2d::Layer
{
public:
	static cocos2d::Scene* createScene();
	virtual bool init();
	CREATE_FUNC(FlyBirdGame);
	void initUI();
	void gameStart(Object* pSender); 
	void update(float time);
	Obstacle* obstacle;
};

#include "cocos2d.h"
#include "FlyBirdGame.h"
#include "resource.h"; 

USING_NS_CC;

Scene* FlyBirdGame::createScene()
{
	auto scene = Scene::create();
	auto layer = FlyBirdGame::create();
	scene->addChild(layer);
	return scene;
}

bool FlyBirdGame::init()
{
	if (!Layer::init())
	{
		return false;
	}
	initUI();
	return true;
}

initUI里是一些UI初始化方法:

        // win size
	auto winSize = Director::getInstance()->getVisibleSize();

	// game bg
	auto bg = Sprite::create(bird_bg);
	bg->setPosition(winSize.width / 2, winSize.height / 2);
	bg->setScale(winSize.width / bg->getContentSize().width, winSize.height / bg->getContentSize().height);
	this->addChild(bg);

	// start btn
	auto startBtn = MenuItemImage::create(bird_start_btn, bird_start_btn_pressed, CC_CALLBACK_1(FlyBirdGame::gameStart, this));
	auto menu = Menu::create(startBtn, NULL);
	menu->setTag(100);
	this->addChild(menu);

	// hero
	auto hero = Sprite::create(bird_hero);
	hero->setPosition(winSize.width / 5, winSize.height*0.8);
	hero->setVisible(false);
	hero->setTag(200);
	this->addChild(hero);

开始游戏按钮绑定的gameStart方法:

void FlyBirdGame::gameStart(Object* pSender)
{
	auto btn = this->getChildByTag(100);
	btn->setVisible(false);
	auto hero = this->getChildByTag(200); 
	Size win = Director::getInstance()->getWinSize();
	obstacle->gameStart = true;
}

隐藏开始按钮,显示小鸟,水管开始移动

还有更新方法:

scheduleUpdate();
void FlyBirdGame::update(float time)
{
	obstacle->update();
}

=======================================

水管类:Obstacle.cpp

update方法里判断游戏是游戏是否开始

Cocos2dx游戏开发笔记21:动手学习《Flappy Bird》之继续优化(付源码)_第3张图片
void Obstacle::update()
{
	if (gameStart == false)
		return;
	addCount++;
	if (addCount == 60)
	{
		addOne(0);
		addCount = 0;
	}
	for (int i = obstacleList->count() - 1; i >= 0; i--)
	{
		auto s = (Sprite*)obstacleList->getObjectAtIndex(i);
		s->setPositionX(s->getPositionX() - 3);
		if (s->getPositionX() < -s->getContentSize().width / 2)
		{
			obstacleList->removeObjectAtIndex(i);
			this->removeChild(s);
		}
	}
}
水管类的更新方法里,每60帧(1秒)添加一对水管

并且遍历水管列表

出边界的化销毁

接下来是addOne方法:添加水管方法:

void Obstacle::addOne(int offsetX)
{
	Size size = Director::getInstance()->getWinSize();
	auto sprite = Sprite::create(bird_obstacle_up);
	Size spriteSize = sprite->getContentSize();
	obstacleList->addObject(sprite);
	this->addChild(sprite);
	auto sprite2 = Sprite::create(bird_obstacle_down);
	Size spriteSize2 = sprite->getContentSize();
	obstacleList->addObject(sprite2);
	this->addChild(sprite2);
	// set positon
	int maxUpY = size.height + spriteSize.height / 4;
	int minUpY = size.height - spriteSize.height / 4;
	int y1 = CCRANDOM_0_1()*(maxUpY - minUpY) + minUpY;
	int maxDownY = spriteSize.height / 4;
	int minDownY = -spriteSize.height / 4;
	int y2 = CCRANDOM_0_1()*(maxDownY - minDownY) + minDownY;
	if (y1 - y2 - spriteSize.height < 160)
	{
		y2 = y1 - spriteSize.height - 160;
	}
	sprite->setPosition(ccp(size.width + spriteSize.width / 2 + offsetX, y1));
	sprite2->setPosition(ccp(size.width + spriteSize2.width / 2 + offsetX, y2));
}
这段代码比较凌乱,就是找到水管上下位置的范围

然后随机一下,并且保证上下连个水管有个最小的距离

效果如下:

Cocos2dx游戏开发笔记21:动手学习《Flappy Bird》之继续优化(付源码)_第4张图片

=============================

此时的游戏还没触摸和碰撞逻辑

马上添加:(刚才抽空玩了把魔方:五阶的我只能搞定一个面,虽然有官方规律但是那样好像比的是记忆力)

听说cocos2dx3.0的事件监听方式改变了

先在FlyBirdGame.h里声明俩方法:

	void onTouchesEnded(const vector<Touch*>& touches, Event* event);
	void onTouchesBegan(const vector<Touch*>& touches, Event* event);

在cpp文件的初始化里绑定事件:

	// touch
	auto dispatcher = Director::getInstance()->getEventDispatcher();
	auto listener = EventListenerTouchAllAtOnce::create();
	listener->onTouchesEnded = CC_CALLBACK_2(FlyBirdGame::onTouchesEnded, this);
	listener->onTouchesBegan = CC_CALLBACK_2(FlyBirdGame::onTouchesBegan, this);
	dispatcher->addEventListenerWithSceneGraphPriority(listener, this);

在两个事件方法里改变标记位,在小鸟的update方法里根据这个标记位来改变高度

(哲哲喊我休息了,先到这吧,待续。。。)

=============================

碰撞检测:

记得cocos2dx3.0以前都是自己写一个根据CCSprite获取CCRect的方法

现在直接用 Sprite的getBoundingBox()方法

不过如果有特殊需求还得自己写,比如想扩大或缩小碰撞区域

在FlyBirdGame.cpp文件里

update算主逻辑方法:

void FlyBirdGame::update(float time)
{
	auto winSize = Director::getInstance()->getVisibleSize();
	auto hero = this->getChildByTag(TAG_HERO);
	Rect rHero = ((Sprite*)hero)->getBoundingBox();

	switch (GAME_STATUS)
	{
	case GAME_STATUS_PLAYING:
		obstacle->update();
		// update bird positionY
		if (isFlying&&hero->getPositionY() < winSize.height)
		{
			hero->setPositionY(hero->getPositionY() + v);
		}
		else if (hero->getPositionY()>0)
		{
			hero->setPositionY(hero->getPositionY() - v);
		}
		//check collision
		for (int i = 0; i < obstacle->obstacleList->count(); i++)
		{
			Sprite* obstacleRect = (Sprite*)obstacle->obstacleList->getObjectAtIndex(i);
			bool pia = rHero.intersectsRect(obstacleRect->getBoundingBox());
			if (pia == true)
			{
				GAME_STATUS = GAME_STATUS_GAME_OVER;
				break;
			}
		}
		break;
	case GAME_STATUS_GAME_OVER:
		CCLog("over");
		this->getChildByTag(TAG_OVER)->setVisible(true);
		break;
	case GAME_STATUS_RESTART:
		//reset game
		obstacle->removeAllChildren();
		obstacle->obstacleList->removeAllObjects();
		// reset hero
		hero->setPosition(winSize.width / 5, winSize.height*0.8);
		// show btn
		auto btn = this->getChildByTag(TAG_START_BTN);
		btn->setVisible(true);
		// show logo
		auto logo = this->getChildByTag(TAG_LOGO);
		logo->setVisible(true);
		break;
	}
}

根据游戏状态处理。

碰撞检测方法:intersectsRect

bool pia = rHero.intersectsRect(obstacleRect->getBoundingBox());
if (pia == true)
{
GAME_STATUS = GAME_STATUS_GAME_OVER;
break;
}


游戏状态定义在 resource.h 文件里

static const int GAME_STATUS_START = 10;
static const int GAME_STATUS_PLAYING = 20;
static const int GAME_STATUS_GAME_OVER = 30;
static const int GAME_STATUS_RESTART = 40;


整个游戏流程已经通了:开始,游戏,结束,重新开始

接下来可以优化很多很多东西

===================================================

真搞不懂为什么和弦要那么别扭的按法

相较之下

还是代码简单

继续优化

添加小鸟动画:


添加到resource.h 文件中等待使用

也可以直接在cpp文件中用不过骨头习惯了资源统一管理

	Animation* an = Animation::create();
	an->addSpriteFrameWithFileName(bird_hero);
	an->addSpriteFrameWithFileName(bird_hero2);
	an->addSpriteFrameWithFileName(bird_hero3);
	an->setDelayPerUnit(0.5f / 3.0f);
	an->setLoops(-1);
	Animate* anim = Animate::create(an);
	hero->runAction(anim);

F5运行看效果,确实生动一点了

=========================================================

分辨率问题:

在AppDelegate.cpp入口方法中加入:

CCEGLView::sharedOpenGLView()->setDesignResolutionSize(320, 480, kResolutionExactFit);

其中320,480是游戏設計時的分辨率。

后面参数介绍如下“:

	//kResolutionExactFit x,y都拉伸,使铺满屏幕
	//kResolutionNoBorder 一个方向铺满屏幕,另外一个方向超出屏幕
	//kResolutionShowAll  854 * 480 的设计区域全部可见,但是可能留有黑边(如在960 * 640的屏幕上)
=============================================================

分数的显示:

之前的几个demo都是使用本办法显示的特殊数字:一排图片然后根据每位上的数字决定显示哪个图片

本来打算自己封装个方法,再一看,有更简单的:

Cocos2dx游戏开发笔记21:动手学习《Flappy Bird》之继续优化(付源码)_第5张图片

Cocos2dx游戏开发笔记21:动手学习《Flappy Bird》之继续优化(付源码)_第6张图片

从自带的demo里找点合适的字体资源,拷贝到fonts文件夹下

记住把fnt和对应的png都拷贝过来

然后

	auto label1 = LabelBMFont::create("123456", "fonts/futura-48.fnt");
	addChild(label1);
	label1->setPosition(Point(win.width / 2, win.height / 2));


================================================

分数显示ok了,下面看下分数逻辑

当小鸟的坐标等于柱子的坐标时,score+1(因为是上下俩柱子对象,所以除以2显示)

别忘记判断时要让小鸟飞过柱子,而不是跟柱子纵轴对齐

相关代码在检测碰撞时添加:

		//check collision
		for (int i = 0; i < obstacle->obstacleList->count(); i++)
		{
			Sprite* obstacleSprite = (Sprite*)obstacle->obstacleList->getObjectAtIndex(i);
			bool pia = rHero.intersectsRect(obstacleSprite->getBoundingBox());
			if (pia == true)
			{
				GAME_STATUS = GAME_STATUS_GAME_OVER;
				break;
			}
			int oPosX = obstacleSprite->getPositionX() + obstacleSprite->getContentSize().width / 2;
			int heroX = hero->getPositionX() - hero->getContentSize().width;
			if (oPosX == heroX)
			{
				score++;
				auto scoreSprite = (LabelBMFont*)this->getChildByTag(TAG_SCORE);
				String* s = String::createWithFormat("%d", score / 2);
				scoreSprite->setString(s->getCString());
			}
		}
Cocos2dx游戏开发笔记21:动手学习《Flappy Bird》之继续优化(付源码)_第7张图片


======================================================

增加下游戏难度

之所以死不了是因为小鸟的速度改变的很均匀

因为触摸事件只改变速度 不改变加速度

		// update bird positionY
		if (hero->getPositionY() > 0 && hero->getPositionY() < winSize.height)
		{
			velocity -= gravity;
			hero->setPositionY(hero->getPositionY() + velocity);
		}
		//if (isFlying&&hero->getPositionY() < winSize.height)
		//{
		//	hero->setPositionY(hero->getPositionY() + velocity);
		//}
		//else if (hero->getPositionY()>0)
		//{
		//	hero->setPositionY(hero->getPositionY() - velocity);
		//}
		//check collision

将之前的速度改变方式修改一下,每次点击给一个向上的速度,然后重力向下,这样就类似原游戏的跳跃感了

至于数值可以继续微调来加大游戏难度


=====================================

原作还有好多细节等愿意动弹了慢慢添加

源码下载

http://download.csdn.net/detail/iamlazybone/6914353





你可能感兴趣的:(开源项目,引擎,游戏开发,VS2013,cocos2dx3.0)