学习版本:cocos2d-x-3.1.1
环境: win8.1, vs2013
现在是0.
环境的搭建从简,只装了python2.7和cocos2d-x,安卓sdk/ndk未进行安装配置。
命令行:cocos new 创建新工程
研读helloWorld,配上一本参考书, 我用的《Cocos2d-x高级开发教程》,貌似市面上的书都依据较旧版本的cocos进行讲解的,后来遇到部分已废弃的接口。代码加书的前几章,大致理解cocos2d-x的代码结构如下:
入口类Application,继承自Application,通过重写applicationDidFinishLaunching等方法实现游戏加载等具体逻辑
游戏加载的时候初始化单例Director, 及其成员GLView,两者看起来控制着游戏的主体,如绘图,主循环等;
游戏加载的时候同时构建场景Scene,层Layer,及精灵Sprite,Layer加到Scene中,Sprite加入到Layer中
菜单和菜单项的设置,注意一个回调设置。
动作有瞬时动作和持续动作,可以通过Sequence编辑动作序列,也可通过Spawn编辑动作并行
内存管理,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
代码好像贴不完,开下篇贴。