cocos2d学习自学自练--flappy bird

学习版本:cocos2d-x-3.1.1

环境: win8.1, vs2013

现在是0.

环境的搭建从简,只装了python2.7和cocos2d-x,安卓sdk/ndk未进行安装配置。

命令行:cocos new 创建新工程

研读helloWorld,配上一本参考书, 我用的《Cocos2d-x高级开发教程》,貌似市面上的书都依据较旧版本的cocos进行讲解的,后来遇到部分已废弃的接口。代码加书的前几章,大致理解cocos2d-x的代码结构如下:

  1. 入口类Application,继承自Application,通过重写applicationDidFinishLaunching等方法实现游戏加载等具体逻辑

  2. 游戏加载的时候初始化单例Director, 及其成员GLView,两者看起来控制着游戏的主体,如绘图,主循环等;

  3. 游戏加载的时候同时构建场景Scene,层Layer,及精灵Sprite,Layer加到Scene中,Sprite加入到Layer中

  4. 菜单和菜单项的设置,注意一个回调设置。

  5. 动作有瞬时动作和持续动作,可以通过Sequence编辑动作序列,也可通过Spawn编辑动作并行

  6. 内存管理,cocos2d-x几乎都是动态创建对象,多用create()工厂方法创建,此方法会将对象加入autorelease;整个内存采用引用计数,retain()用于保留对象,计数+1, release用户释放对象,计数-1; 加入autorelease的对象,注意成对使用retain()及release()

有了这些概念,开始玩起。

flappy bird美术资源网上淘了一份。

目前实现的内容不多,没菜单与计分等,后面慢慢集成上去,实现了鸟和水管的各种动作及碰撞,鸟有飞,落和死亡,水管高度随机,移动等,鸟与水管和地面的碰撞。

GameScene:主游戏层。游戏简单,一个场景且一个层,都在这里初始化了,并初始化了水管和鸟待用。

头文件GameScene.h

#ifndef _GAME_SCENE_H_
#define _GAME_SCENE_H_

#include "cocos2d.h"

enum class GameState;
class GameScene : public cocos2d::Layer
{
public:
	//create scene and add this layer to scene
	static cocos2d::Scene *createScene();

	//init this this layer
	virtual bool init() override;
	CREATE_FUNC(GameScene);

	virtual void update(float delta) override;

private:
	cocos2d::SpriteBatchNode *_gameBatchNode;

};
#endif //_GAME_SCENE_H_

数据成员增加了,用于批量渲染节点,美术资源里有一份.plist文件,可从中获取画帧,见实现

GameScene.cpp

#include "GameScene.h"
#include "PipeSprite.h"
#include "BirdSprite.h"
#include "GameInfo.h"

USING_NS_CC;

Scene *GameScene::createScene()
{
	auto mainScene = Scene::create();

	auto layer = create();

	mainScene->addChild(layer);

	return mainScene;
}

bool GameScene::init()
{
	if (!Layer::init())
	{
		return false;
	}
	auto visibleSize = Director::getInstance()->getVisibleSize();
	auto origin = Director::getInstance()->getVisibleOrigin();

//	auto bg = Sprite::create("bird_bg.png");
//	bg->setPosition(Vec2(visibleSize.width * 0.5f + origin.x, visibleSize.height * 0.5f + origin.y));
//	this->addChild(bg, 0);

	//SpriteFrameCashe a singleton to manage SpriteFrame
	SpriteFrameCache::getInstance()->addSpriteFramesWithFile("bird.plist", "bird.png");
	_gameBatchNode = SpriteBatchNode::create("bird.png", MAX_BATCHNODE);
//	gameBatchNode->setPosition(Vec2(origin.x, visibleSize.height * 0.5f + origin.y));
	this->addChild(_gameBatchNode, 1);

	auto background = Sprite::createWithSpriteFrameName("bird_bg.png");
	background->setAnchorPoint(Vec2(0, 0));
	//background->setPosition(Vec2(visibleSize.width / 2,0));
	_gameBatchNode->addChild(background, 0, TAG_BACKGROUND);

	PipeManager::getInstance()->initPipesWithSpriteBatchNode(_gameBatchNode);
	auto bird = BirdSprite::getInstance();
	_gameBatchNode->addChild(bird, 10);

	this->schedule((SEL_SCHEDULE)&GameScene::update);

	//touch event
	auto listener = EventListenerTouchOneByOne::create();
	listener->onTouchBegan = [](Touch *touch, Event *evnet)
	{
		if (PipeManager::getInstance()->getGameState() == GameState::NORMAL
			|| PipeManager::getInstance()->getGameState() == GameState::ACTIVE)
		{
			BirdSprite::getInstance()->flyUp();
		}
		return true;
	};
	listener->onTouchMoved = [](Touch *touch, Event *evnet)
	{
		//nothing
	};
	listener->onTouchEnded = [](Touch *touch, Event *evnet)
	{
		//nothing
	};
	_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);

	return true;
}

void GameScene::update(float delta)
{
	if (PipeManager::getInstance()->getGameState() == GameState::OVER)
	{
		return;
	}

	if (BirdSprite::getInstance()->isOnFloor() || PipeManager::getInstance()->isCollision(BirdSprite::getInstance()))
	{
		BirdSprite::getInstance()->birdDead();
		PipeManager::getInstance()->setGameState(GameState::OVER);
		return;
	}
	PipeManager::getInstance()->update(delta);
}


水管,两个类,一个水管精灵类,一个单例水管管理类, 主要用来做水管的移动及碰撞,且控制游戏状态

PipeSprite.h

#ifndef _PIPE_SRPITE_H_
#define _PIPE_SRPITE_H_

#include "cocos2d.h"
#include "GameInfo.h"


class PipeManager;
class PipeSprite : public cocos2d::Sprite
{
public:
	PipeSprite();
	~PipeSprite();
public:
	virtual bool init() override;
//	bool initWithSpriteBatchNode(cocos2d::SpriteBatchNode *gameBatchNode);
	virtual void update(float delta) override;
	CREATE_FUNC(PipeSprite);

private:
	void setRandomY();
	bool isCollision(cocos2d::Sprite *sprite);

private:
	friend class PipeManager;
	Sprite *_upPipe;
	Sprite *_lowPipe;
};

class PipeManager
{
public:
	static PipeManager *getInstance();
	bool initPipesWithSpriteBatchNode(cocos2d::SpriteBatchNode *gameBatchNode);
	void update(float delta);

	GameState getGameState(){ return _state; }
	void setGameState(GameState state){ _state = state; }
	bool isCollision(cocos2d::Sprite *sprite);

public:
	~PipeManager();

private:
	PipeManager();

private:
	static PipeManager *_manager;
	PipeSprite *_pipes;
	GameState _state;
};
#endif

PipeSprite.cpp

#include "PipeSprite.h"
#include "GameInfo.h"

USING_NS_CC;

PipeSprite::PipeSprite() : _upPipe(nullptr), _lowPipe(nullptr)
{}

PipeSprite::~PipeSprite()
{
}

bool PipeSprite::init()
{
	if (!Sprite::init())
	{
		return false;
	}

	_lowPipe = Sprite::createWithSpriteFrameName("obstacle_down.png");
	_lowPipe->setAnchorPoint(Vec2(0, 1));
	_lowPipe->retain();

	_upPipe = Sprite::createWithSpriteFrameName("obstacle_up.png");
	_upPipe->setAnchorPoint(Vec2(0, 0));
	_upPipe->retain();

	setRandomY();

	return true;
}

void PipeSprite::setRandomY()
{
	float randHeight = CCRANDOM_MINUS1_1();
	_lowPipe->setPositionY(Director::getInstance()->getVisibleSize().height / 2.0f - GAP / 2.0f + randHeight * 100.0f);
	_upPipe->setPositionY(Director::getInstance()->getVisibleSize().height / 2.0f + GAP / 2.0f + randHeight * 100.0f);
}

/*
bool PipeSprite::initWithSpriteBatchNode(SpriteBatchNode *gameBatchNode)
{
	if (!Sprite::init())
	{
		return false;
	}

	if (!gameBatchNode->getChildByTag(TAG_BACKGROUND))
	{
		return false;
	}

	float randHeight = CCRANDOM_MINUS1_1();

	_lowPipe = Sprite::createWithSpriteFrameName("obstacle_down.png");
	_lowPipe->setAnchorPoint(Vec2(0, 1));
	_lowPipe->setPosition(Vec2(Director::getInstance()->getVisibleSize().width * 3 - _lowPipe->getContentSize().width,
		Director::getInstance()->getVisibleSize().height / 2.0f - GAP / 2.0f + randHeight * 100.0f));
	_lowPipe->retain();
	gameBatchNode->addChild(_lowPipe, 10);

	_upPipe = Sprite::createWithSpriteFrameName("obstacle_up.png");
	_upPipe->setAnchorPoint(Vec2(0, 0));
	_upPipe->setPosition(Vec2(Director::getInstance()->getVisibleSize().width * 3 - _upPipe->getContentSize().width,
		Director::getInstance()->getVisibleSize().height / 2.0f + GAP / 2.0f + randHeight * 100.0f));
	_upPipe->retain();
	gameBatchNode->addChild(_upPipe, 10);

	return true;
}
*/

bool PipeSprite::isCollision(Sprite *sprite)
{
	if (sprite->getPositionX() >= _upPipe->getPositionX() && sprite->getPositionX() <= _upPipe->getPositionX() + _upPipe->getContentSize().width
		&& (sprite->getPositionY() >= _upPipe->getPositionY() || sprite->getPositionY() <= _lowPipe->getPositionY()))
	{
		return true;
	}
	return false;

	/*
	//getBoundingBox().intersectsRect这个间隙好大
	if (_upPipe->getBoundingBox().intersectsRect(sprite->getBoundingBox()) || _lowPipe->getBoundingBox().intersectsRect(sprite->getBoundingBox()))
	{
		return true;
	}
	return false;
	*/
}

void PipeSprite::update(float delta)
{
	float x = _upPipe->getPositionX() - STEP;
	if (x <= -_upPipe->getContentSize().width)
	{
		x = Director::getInstance()->getVisibleSize().width * 3 - _lowPipe->getContentSize().width;
		setRandomY();
	}
	_upPipe->setPositionX(x);
	_lowPipe->setPositionX(x);
}


PipeManager *PipeManager::_manager = NULL;

PipeManager::PipeManager() :_pipes(nullptr)
{
}

PipeManager::~PipeManager()
{
	delete []_pipes;
}

PipeManager *PipeManager::getInstance()
{
	if (!_manager)
	{
		_manager = new PipeManager;
	}
	return _manager;
}

bool PipeManager::initPipesWithSpriteBatchNode(SpriteBatchNode *gameBatchNode)
{
	_state = GameState::NORMAL;
	if (!_pipes)
	{
		_pipes = new PipeSprite[MAX_PIPES];

		for (int i(0); i < MAX_PIPES; ++i)
		{
			_pipes[i].init();
			_pipes[i]._upPipe->setPositionX(Director::getInstance()->getVisibleSize().width * 3
				+ i * (Director::getInstance()->getVisibleSize().width - 2 * _pipes[i].getContentSize().width - 30));

			_pipes[i]._lowPipe->setPositionX(Director::getInstance()->getVisibleSize().width * 3
				+ i * (Director::getInstance()->getVisibleSize().width - 2 * _pipes[i].getContentSize().width - 30));
			gameBatchNode->addChild(_pipes[i]._upPipe, 10);
			gameBatchNode->addChild(_pipes[i]._lowPipe,10);
		}
	}
	return true;
}

void PipeManager::update(float delta)
{  
	if (_state == GameState::OVER)
	{
		return;
	}

	for (int i(0); i < MAX_PIPES - 1; ++i)
	{
		_pipes[i].update(delta);
	}
}

bool PipeManager::isCollision(Sprite *sprite)
{
	for (int i(0); i < MAX_PIPES; ++i)
	{
		if (_pipes[i].isCollision(sprite))
		{
			return true;
		}
	}
	return false;
}

常量头文件GameInfo.h

#ifndef _GAME_INFO_H_
#define _GAME_INFO_H_

#include "base/CCConsole.h"

//@constent and macro in game

//max num to create spritebatchnode
const ssize_t MAX_BATCHNODE = 200L;

//max num to mem of PipeManager
const unsigned short MAX_PIPES = 4;

//gap's size
const float GAP = 100;

//speed
const float STEP = 3;

//height of one fly action
const float HEIGHT_FLY = 45;

//
const float GA = 2;

//
enum NodeTag
{
	TAG_BACKGROUND = 0,
	TAG_LABLE
};

enum class GameState
{
	INIT = 0,
	NORMAL,
	ACTIVE,
	OVER
};



#endif

代码好像贴不完,开下篇贴。

你可能感兴趣的:(cocos2d-x基础)