(十三) 分数场景

最后更新于:2022-04-01 16:12:34

分数场景要用到TableView,这个之前也没用过,主要参考网上的代码。cocos2d-x的功能十分强大,以后还有好多东西要学啊! 分数场景类的.h文件中的内容 ~~~ class ScoreScene : public Layer, public TableViewDataSource, public TableViewDelegate { public: ScoreScene(); ~ScoreScene(); public: static Scene * createScene(); bool init(); CREATE_FUNC(ScoreScene); public: void tableCellTouched(TableView *table, TableViewCell * cell){}; TableViewCell * tableCellAtIndex(TableView * table, ssize_t index); Size tableCellSizeForIndex(TableView * table, ssize_t index); ssize_t numberOfCellsInTableView(TableView * table); void scrollViewDidScroll(ScrollView *){}; void scrollViewDidZoom(ScrollView *){}; //对返回键的响应 void onKeyReleased(EventKeyboard::KeyCode keyCode, Event * pEvent); private: Size size; ~~~ .cpp文件 ~~~ ScoreScene::ScoreScene() { } ScoreScene::~ScoreScene() { _eventDispatcher->removeEventListenersForTarget(this); } Scene * ScoreScene::createScene() { auto scene = Scene::create(); auto layer = ScoreScene::create(); scene->addChild(layer); return scene; } bool ScoreScene::init() { if (!Layer::init()) return false; //背景音乐 CocosDenshion::SimpleAudioEngine::getInstance()->stopBackgroundMusic(true); CocosDenshion::SimpleAudioEngine::getInstance()->playBackgroundMusic("sound/BackgroundMusic2.mp3", true); size = Director::getInstance()->getWinSize(); //添加背景图片 auto background = Sprite::createWithSpriteFrameName("backgroundTollgateTwo.png"); background->setPosition(Point(size.width / 2, size.height / 2)); background->setAnchorPoint(Vec2(0.5, 0.5)); this->addChild(background); //创建tableView并设置一些参数 auto tableView = TableView::create(this, Size(size.width, size.height*0.8)); //设置滑动方向 tableView->setDirection(ScrollView::Direction::VERTICAL); //设置TableViewDelegate tableView->setDelegate(this); //index的大小是从上到下依次增大 tableView->setVerticalFillOrder(TableView::VerticalFillOrder::TOP_DOWN); //用当前的配置刷新cell tableView->reloadData(); this->addChild(tableView); //排名 auto dictionary = Dictionary::createWithContentsOfFile("fonts/AboutMe.xml"); auto rankNum = Label::createWithTTF( ((__String*)(dictionary->objectForKey("rankNum")))->getCString(), "fonts/DFPShaoNvW5-GB.ttf", 40); rankNum->setColor(Color3B(255, 0, 0)); rankNum->setPosition(Point(size.width*0.4, size.height*0.9)); this->addChild(rankNum); //得分 auto rankScore = Label::createWithTTF( ((__String*)(dictionary->objectForKey("rankScore")))->getCString(), "fonts/DFPShaoNvW5-GB.ttf", 40); rankScore->setPosition(Point(size.width*0.8, size.height*0.9)); rankScore->setColor(Color3B(255, 0, 0)); this->addChild(rankScore); //对返回键的响应 auto listener = EventListenerKeyboard::create(); listener->onKeyReleased = CC_CALLBACK_2(ScoreScene::onKeyReleased, this); _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this); return true; } //对返回键的响应 void ScoreScene::onKeyReleased(EventKeyboard::KeyCode keyCode, Event * pEvent) { if (keyCode == EventKeyboard::KeyCode::KEY_ESCAPE) { //背景音乐 CocosDenshion::SimpleAudioEngine::getInstance()->stopBackgroundMusic(true); CocosDenshion::SimpleAudioEngine::getInstance()->playBackgroundMusic("sound/game_start.mp3", true); //场景弹出 Director::getInstance()->popScene(); } } //用来设置每个cell的内容的 TableViewCell * ScoreScene::tableCellAtIndex(TableView * table, ssize_t index) { //设置每条记录前边的文本内容 auto index_text = __String::createWithFormat("%ld", index + 1); TableViewCell * cell = table->dequeueCell(); if (cell == NULL) { //创建一个cell cell = new TableViewCell(); cell->autorelease(); //创建显示排名的文本信息 auto text = Label::createWithTTF(index_text->getCString(), "fonts/DFPShaoNvW5-GB.ttf", 24); text->setTag(1024); text->setColor(Color3B(255, 0, 0)); //文本信息在cell的中间 text->setPosition(Point(size.width*0.4, size.height*0.025)); cell->addChild(text); //显示用户得分的文本信息 auto index_score = __String::createWithFormat("%d", index); //根据index值获得得分的文本,因为这个时候的score是int型,所以还需要转化一下类型 int i_score = UserDefault::getInstance()->getIntegerForKey(index_score->getCString()); auto * str = __String::createWithFormat("%d", i_score); auto score = Label::createWithTTF( str->getCString(), "fonts/DFPShaoNvW5-GB.ttf", 24); score->setTag(2048); //设置坐标 score->setPosition(Point(size.width*0.8, size.height*0.025)); score->setColor(Color3B(255, 0, 0)); cell->addChild(score); } //这里获得的cell是原来的cell,所以原来cell的文本信息等还是原来的,所以要做一些改变 else { //通过tag值获得文本,并且改变 auto text = (Label *)cell->getChildByTag(1024); text->setString(index_text->getCString()); //改变分数 auto * score = (Label *)cell->getChildByTag(2048); auto * index_score = __String::createWithFormat("%d", index); int i_score = UserDefault::getInstance()->getIntegerForKey(index_score->getCString()); auto * str = __String::createWithFormat("%d", i_score); score->setString(str->getCString()); if (cell->getChildByTag(100) != NULL) { Sprite * sprite = (Sprite *)cell->getChildByTag(100); sprite->removeFromParentAndCleanup(true); } } if (index == 0 || index == 1 || index == 2) { Sprite * sprite; switch (index) { case 0: sprite = Sprite::createWithSpriteFrameName("gold.png"); break; case 1: sprite = Sprite::createWithSpriteFrameName("silvere.png"); break; case 2: sprite = Sprite::createWithSpriteFrameName("copper.png"); break; } sprite->setPosition(Point(size.width*0.15, size.height*0.025)); sprite->setTag(100); cell->addChild(sprite); } return cell; } //这个函数是用来设置每个cell的大小的 Size ScoreScene::tableCellSizeForIndex(TableView * table, ssize_t index) { return Size(size.width, size.height*0.05); } //这个函数是用来设置cell的个数的 ssize_t ScoreScene::numberOfCellsInTableView(TableView * table) { //个数是从XML文件中读取到的,有多少条记录,就设置多少个cell,如果刚开始没有count这个字段,就返回0 int count = UserDefault::getInstance()->getIntegerForKey("count", 0); return count; } ~~~ 好了,这个游戏到这里就已经完成啦!再有的就是移植到其它平台了。我是将这个游戏移植到了Android平台。 代码以及资源文件 [资源文件](http://download.csdn.net/detail/u011694809/9038951)
';

(十二) 游戏结束场景

最后更新于:2022-04-01 16:12:32

游戏结束的时候,要显示分数,还要能够选择是返回主场景还是退出游戏 ~~~ // 退出游戏 void menuCloseCallback(cocos2d::Ref* pSender); // 返回主界面 void menuMainCallback(cocos2d::Ref* pSender); ~~~ 实现该功能的代码如下 ~~~ bool GameOver::init() { ////////////////////////////// // 1. super init first if (!Layer::init()) { return false; } bool bRect = false; //背景音乐 if (CocosDenshion::SimpleAudioEngine::getInstance()->isBackgroundMusicPlaying()) { CocosDenshion::SimpleAudioEngine::getInstance()->stopBackgroundMusic(true); CocosDenshion::SimpleAudioEngine::getInstance()->playBackgroundMusic("sound/game_over.mp3", true); } do { Size visibleSize = Director::getInstance()->getVisibleSize(); Vec2 origin = Director::getInstance()->getVisibleOrigin(); //添加背景图片 auto m_background = Sprite::createWithSpriteFrameName("backgroundGameOver.png"); m_background->setPosition(Point(visibleSize.width / 2, visibleSize.height / 2)); m_background->setAnchorPoint(Vec2(0.5, 0.5)); CC_BREAK_IF(!m_background); this->addChild(m_background); //添加分数 auto score_int = UserDefault::getInstance()->getIntegerForKey("currentScore"); auto score_str = __String::createWithFormat("%d", score_int); auto score = Label::createWithTTF(score_str->getCString(), "fonts/DFPShaoNvW5-GB.ttf", 40); score->setPosition(Point(visibleSize.width / 2, visibleSize.height/3*2)); score->setColor(Color3B(255, 0, 0)); CC_BREAK_IF(!score); this->addChild(score); //设定等级 //设置标签 并 获取中文文本 auto dictionary = Dictionary::createWithContentsOfFile("fonts/AboutMe.xml"); String rank_str; switch (score_int/1000) { case 0: rank_str = ((__String*)(dictionary->objectForKey("Eleven")))->getCString(); break; case 1: rank_str = ((__String*)(dictionary->objectForKey("Ten")))->getCString(); break; case 2: rank_str = ((__String*)(dictionary->objectForKey("Nine")))->getCString(); break; case 3: rank_str = ((__String*)(dictionary->objectForKey("Eight")))->getCString(); break; case 4: rank_str = ((__String*)(dictionary->objectForKey("Seven")))->getCString(); break; case 5: rank_str = ((__String*)(dictionary->objectForKey("Six")))->getCString(); break; case 6: rank_str = ((__String*)(dictionary->objectForKey("Five")))->getCString(); break; case 7: rank_str = ((__String*)(dictionary->objectForKey("Four")))->getCString(); break; case 8: rank_str = ((__String*)(dictionary->objectForKey("Three")))->getCString(); break; case 9: rank_str = ((__String*)(dictionary->objectForKey("Two")))->getCString(); break; case 10: rank_str = ((__String*)(dictionary->objectForKey("One")))->getCString(); break; default: rank_str = ((__String*)(dictionary->objectForKey("Zere")))->getCString(); break; }; auto m_label1 = Label::createWithTTF( rank_str.getCString(), "fonts/DFPShaoNvW5-GB.ttf", 65 ); m_label1->setColor(Color3B(255, 0, 0)); m_label1->setPosition(Point(visibleSize.width / 2, visibleSize.height / 2 - m_label1->getContentSize().height)); this->addChild(m_label1); ///////////////////////////// // 2. add a menu item with "X" image, which is clicked to quit the program // you may modify it. //退出游戏 按钮 auto tempClose1 = Sprite::createWithSpriteFrameName("GameOver_nor.png"); auto tempClose2 = Sprite::createWithSpriteFrameName("GameOver_touched.png"); auto closeItem = MenuItemSprite::create( tempClose1, tempClose2, CC_CALLBACK_1(GameOver::menuCloseCallback, this) ); //返回主界面 按钮 auto tempBack1 = Sprite::createWithSpriteFrameName("ReturnGame_nor.png"); auto tempBack2 = Sprite::createWithSpriteFrameName("ReturnGame_touched.png"); auto backItem = MenuItemSprite::create( tempBack1, tempBack2, CC_CALLBACK_1(GameOver::menuMainCallback, this) ); // create menu, it's an autorelease object auto menu = Menu::create(closeItem, backItem, NULL); menu->alignItemsVerticallyWithPadding(closeItem->getContentSize().height / 2); menu->setPosition(Vec2(origin.x + visibleSize.width / 2, visibleSize.height / 4)); CC_BREAK_IF(!menu); this->addChild(menu, 1); bRect = true; } while (0); ///////////////////////////// // 3. add your codes below... return true; } // 退出游戏 void GameOver::menuCloseCallback(Ref* pSender) { Director::getInstance()->end(); #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) exit(0); #endif } // 返回主界面 void GameOver::menuMainCallback(cocos2d::Ref* pSender) { CocosDenshion::SimpleAudioEngine::getInstance()->stopBackgroundMusic(true); Director::getInstance()->replaceScene(TransitionProgressRadialCCW::create(0.8f,HelloWorld::createScene())); } ~~~
';

(十一) 关于游戏场景

最后更新于:2022-04-01 16:12:29

这个场景作为弹出场景,主要介绍下游戏。。。还有自己的联系方式(*^__^*) …… 主要使用文本,就一个返回按键的响应函数 ~~~ //返回按钮 void back(EventKeyboard::KeyCode keyCode, Event* pEvent); ~~~ 同样要记得在析构函数中移除监听 ~~~ AboutGame::~AboutGame() { _eventDispatcher->removeEventListenersForTarget(this); } ~~~ 文本已在XML文件中生成,直接调用即可 ~~~ bool AboutGame::init() { if (!Layer::init()) return false; //背景音乐 CocosDenshion::SimpleAudioEngine::getInstance()->stopBackgroundMusic(true); CocosDenshion::SimpleAudioEngine::getInstance()->playBackgroundMusic("sound/BackgroundMusic1.mp3", true); bool bRect = false; do { auto size = Director::getInstance()->getWinSize(); //设置背景图片 auto m_background = Sprite::createWithSpriteFrameName("backgroundTollgateOne.png"); m_background->setPosition(Vec2(size.width/2,size.height/2)); m_background->setAnchorPoint(Vec2(0.5, 0.5)); this->addChild(m_background); //设置监听器 auto m_listener = EventListenerKeyboard::create(); m_listener->onKeyReleased = CC_CALLBACK_2(AboutGame::back, this); _eventDispatcher->addEventListenerWithSceneGraphPriority(m_listener, this); //设置标签 并 获取中文文本 auto dictionary = Dictionary::createWithContentsOfFile("fonts/AboutMe.xml"); auto m_label3 = Label::createWithTTF( ((__String*)(dictionary->objectForKey("Others")))->getCString(), "fonts/DFPShaoNvW5-GB.ttf", 25 ); m_label3->setDimensions(size.width / 3 * 2, size.height / 2); m_label3->setColor(Color3B(255, 255, 255)); m_label3->setPosition(Point(size.width / 2, size.height/2)); this->addChild(m_label3); auto m_label1 = Label::createWithTTF( ((__String*)(dictionary->objectForKey("AboutMe")))->getCString(), "fonts/DFPShaoNvW5-GB.ttf", 35 ); m_label1->setColor(Color3B(255, 0, 0)); m_label1->setPosition( Point(size.width/2, size.height-m_label3->getContentSize().height-m_label1->getContentSize().height) ); this->addChild(m_label1); auto m_label2 = Label::createWithTTF( ((__String*)(dictionary->objectForKey("QQ")))->getCString(), "fonts/DFPShaoNvW5-GB.ttf", 25 ); m_label2->setDimensions(size.width / 3 * 2, size.height / 6); m_label2->setColor(Color3B(0, 255, 0)); m_label2->setPosition(Point(size.width / 2, m_label1->getPositionY() - m_label2->getContentSize().height)); this->addChild(m_label2); bRect = true; } while (0); return bRect; } //返回按钮 void AboutGame::back(EventKeyboard::KeyCode keyCode, Event* pEvent) { if (keyCode == EventKeyboard::KeyCode::KEY_ESCAPE) { //背景音乐 CocosDenshion::SimpleAudioEngine::getInstance()->stopBackgroundMusic(true); CocosDenshion::SimpleAudioEngine::getInstance()->playBackgroundMusic("sound/game_start.mp3", true); Director::getInstance()->popScene(); } } ~~~
';

(十) 游戏主场景

最后更新于:2022-04-01 16:12:27

主场景要包含其他类的头文件 ~~~ #include "cocos2d.h" #include "MyPlane.h" #include "Bullet.h" #include "EnemyManager.h" #include "Controller.h" #include "BackgroundMove.h" #include "FlowWord.h" ~~~ 在这个游戏中,我将各种碰撞检测也放到主场景中进行 ~~~ void gameUpdate(float dt); // 碰撞检测 bool bulletCollisionEnemy(Sprite* pBullet); // 子弹和敌机碰撞 void enemyCollisionPlane(); // 我机和敌机、敌机子弹碰撞 virtual void onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event); ~~~ 当然,主场景要有各个类的实例做变量 ~~~ MyPlane *planeLayer; Bullet *bulletLayer; EnemyManager *enemyLayer; Controller *controlLayer; ~~~ 主场景的实现 ~~~ TollgateOne::TollgateOne() : planeLayer(NULL), bulletLayer(NULL), enemyLayer(NULL), controlLayer(NULL) { } TollgateOne::~TollgateOne() { _eventDispatcher->removeEventListenersForTarget(this); } cocos2d::Scene* TollgateOne::createScene() { auto scene = Scene::create(); auto layer = TollgateOne::create(); scene->addChild(layer); return scene; } bool TollgateOne::init() { if (!Layer::init()) { return false; } // 启动触摸机制 this->setTouchEnabled(true); // 背景无限滚动 auto m_back = BackgroundMove::create(); this->addChild(m_back,0); //游戏开始 飘字效果 auto dictionary = Dictionary::createWithContentsOfFile("fonts/AboutMe.xml"); auto m_flow = FlowWord::create(); m_flow->showFlowWord( ((__String *)(dictionary->objectForKey("play")))->getCString(), Point(Director::getInstance()->getVisibleSize().width / 2+60, Director::getInstance()->getVisibleSize().height/2), m_flow->otherFlowWord() ); this->addChild(m_flow); //游戏更新 this->schedule(schedule_selector(TollgateOne::gameUpdate)); // 加入控制层 controlLayer = Controller::create(); this->addChild(controlLayer); // 加入飞机 planeLayer = MyPlane::create(); this->addChild(planeLayer,1); // 加入敌机和分数显示 enemyLayer = EnemyManager::create(); enemyLayer->bindController(controlLayer); this->addChild(enemyLayer,1); // 开启子弹 bulletLayer = Bullet::create(); bulletLayer->bindEnemyManager(enemyLayer); this->addChild(bulletLayer,1); //对返回键的响应 auto m_listener = EventListenerKeyboard::create(); m_listener->onKeyReleased = CC_CALLBACK_2(TollgateOne::onKeyReleased, this); _eventDispatcher->addEventListenerWithSceneGraphPriority(m_listener, this); return true; } void TollgateOne::gameUpdate(float dt) { bool bMoveButt = false; //子弹和敌机对碰 for (auto& eButtle : bulletLayer->vecBullet) { Sprite* pBullet = (Sprite*)eButtle; // 获取子弹精灵 bMoveButt = bulletCollisionEnemy(pBullet); if (bMoveButt) { // 子弹删除了,无需再遍历 return; } } // 敌机、敌机子弹与我方飞机碰撞 enemyCollisionPlane(); } bool TollgateOne::bulletCollisionEnemy(Sprite* pBullet) { for (auto& eEnemy : enemyLayer->vecEnemy) { Enemy* pEnemySprite = (Enemy*)eEnemy; // 是否发生碰撞 if (pBullet->boundingBox().intersectsRect(pEnemySprite->getBoundingBox())) { // 飞机只剩下一格生命值 if (1 == pEnemySprite->getLife()) { pEnemySprite->loseLife(); // 知道为什么这里也要loselife吗?你可以试着注释掉看看 enemyLayer->blowupEnemy(pEnemySprite); } else { pEnemySprite->loseLife(); } //删除子弹 bulletLayer->removeBullet(pBullet); return true; } } return false; } void TollgateOne::enemyCollisionPlane() { Sprite* pPlane = (Sprite*)planeLayer->getChildByTag(AIRPLANE); for (auto& eEnemy : enemyLayer->vecEnemy) { Enemy* pEnemySprite = (Enemy*)eEnemy; // 是否发生碰撞 if (pPlane->boundingBox().intersectsRect(pEnemySprite->getBoundingBox()) && pEnemySprite->getLife() > 0) { if (1 == planeLayer->getAlive()) { planeLayer->loseAlive(); controlLayer->getSaveData()->save(); this->unscheduleAllSelectors(); this->bulletLayer->StopBulletShoot(); this->planeLayer->blowUp(); Director::getInstance()->replaceScene( TransitionMoveInT::create(0.8f, GameOver::createScene())); // 替换场景 } else planeLayer->loseAlive(); } } for (auto& eEnemyBullet : bulletLayer->vecEnemyBullet) { Sprite* pEnemyBullet = (Sprite*)eEnemyBullet; // 获取子弹精灵 // 是否发生碰撞 if (pPlane->boundingBox().intersectsRect(pEnemyBullet->getBoundingBox())) { if (1 == planeLayer->getAlive()) { planeLayer->loseAlive(); controlLayer->getSaveData()->save(); this->unscheduleAllSelectors(); this->bulletLayer->StopBulletShoot(); this->planeLayer->blowUp(); Director::getInstance()->replaceScene( TransitionMoveInT::create(0.8f, GameOver::createScene())); // 替换场景 } else planeLayer->loseAlive(); bulletLayer->removeEnemyBullet(pEnemyBullet); return; } } } void TollgateOne::onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event) { if (keyCode == EventKeyboard::KeyCode::KEY_ESCAPE) { Director::getInstance()->replaceScene(HelloWorld::createScene()); } } ~~~ 主场景就是将其它类融合在一起,碰撞检测也是在这里进行。 注意一下这里的事件监听,和开始界面的不太一样,但都要在析构函数中移除。
';

(九) 飘字特效

最后更新于:2022-04-01 16:12:25

之前在一个闯关游戏中第一次接触飘字效果,因为那个游戏没有发教程,所以在这里介绍下飘字效果 ~~~ class FlowWord :public Node { public: FlowWord(); ~FlowWord(); //创建和初始化 飘字 static FlowWord* create(); bool init(); //显示飘字 void showFlowWord(const char* text, Point pos, ActionInterval* flowWord); //显示飘字结束 void showEnd(); // ActionInterval* beginFlowWord(); // ActionInterval* propFlowWord(); ActionInterval* otherFlowWord(); protected: private: Label* m_textLabel; }; ~~~ 在这个类中,内置了几种常见的飘字动作和一个飘字函数(需要传入 飘字内容、位置、动作) 其实现如下 ~~~ bool FlowWord::init() { m_textLabel = Label::createWithTTF("", "fonts/DFPShaoNvW5-GB.ttf", 25); m_textLabel->setColor(ccc3(CCRANDOM_0_1() * 255, CCRANDOM_0_1() * 255, CCRANDOM_0_1() * 255)); m_textLabel->setAnchorPoint(ccp(1, 0)); m_textLabel->setVisible(false); this->addChild(m_textLabel); return true; } //显示飘字 void FlowWord::showFlowWord(const char* text, Point pos, ActionInterval* flowWord) { m_textLabel->setPosition(pos); m_textLabel->setString(text); m_textLabel->setVisible(true); m_textLabel->runAction(flowWord); } //显示飘字结束 void FlowWord::showEnd() { CCLOG("showWord End!"); m_textLabel->setVisible(false); m_textLabel->removeFromParentAndCleanup(true); } #if 0 //游戏开始飘字 ActionInterval* FlowWord::beginFlowWord() { //放大缩小 ActionInterval* m_scaleLarge = ScaleTo::create(2.0f, 2.5, 2.5); ActionInterval* m_scaleSmall = ScaleTo::create(2.0f, 0.5, 0.5); //倾斜 ActionInterval* m_skew = SkewTo::create(2.0f, 180, 0); ActionInterval* m_skewBack = SkewTo::create(2.0f, 0, 0); //组合动作 ActionInterval* m_action = Spawn::create(m_scaleLarge, m_skew, NULL); ActionInterval* m_actionBack = Spawn::create(m_scaleSmall, m_skewBack, NULL); CallFunc* callFunc = CallFunc::create(this, callfunc_selector(FlowWord::showEnd)); ActionInterval* flow = Sequence::create(m_action, m_actionBack, callFunc, NULL); return flow; } //获得道具飘字 ActionInterval* FlowWord::propFlowWord() { //放大缩小 ActionInterval* m_scaleLarge = ScaleTo::create(2.0f, 2.5, 2.5); ActionInterval* m_scaleSmall = ScaleTo::create(2.0f, 0.5, 0.5); CallFunc* callFunc = CallFunc::create(this, callfunc_selector(FlowWord::showEnd)); ActionInterval* flow = Sequence::create(m_scaleLarge, m_scaleSmall, callFunc, NULL); return flow; } #endif //其它飘字 ActionInterval* FlowWord::otherFlowWord() { //移位 ActionInterval* m_move1 = MoveBy::create(2.0f, ccp(30, 30)); ActionInterval* m_move2 = MoveBy::create(2.0f, ccp(-30, -30)); ActionInterval* m_move3 = MoveBy::create(2.0f, ccp(30, -30)); ActionInterval* m_move4 = MoveBy::create(2.0f, ccp(-30, 30)); //放大缩小 ActionInterval* m_scale1 = ScaleTo::create(2.0f, CCRANDOM_0_1() * 4, CCRANDOM_0_1() * 4); ActionInterval* m_scale2 = ScaleTo::create(2.0f, CCRANDOM_0_1() * 4, CCRANDOM_0_1() * 4); ActionInterval* m_scale3 = ScaleTo::create(2.0f, CCRANDOM_0_1() * 4, CCRANDOM_0_1() * 4); ActionInterval* m_scale4 = ScaleTo::create(2.0f, CCRANDOM_0_1() * 4, CCRANDOM_0_1() * 4); ActionInterval* m_action1 = Spawn::create(m_move1, m_scale1, NULL); ActionInterval* m_action2 = Spawn::create(m_move2, m_scale2, NULL); ActionInterval* m_action3 = Spawn::create(m_move3, m_scale3, NULL); ActionInterval* m_action4 = Spawn::create(m_move4, m_scale4, NULL); CallFunc* callFunc = CallFunc::create(this, callfunc_selector(FlowWord::showEnd)); ActionInterval* flow = Sequence::create(m_action1, m_action2, m_action3, m_action4, callFunc, NULL); return flow; } ~~~
';

(八) 背景移动

最后更新于:2022-04-01 16:12:23

采用双层背景,这样效果更好 .h ~~~ class BackgroundMove : public Layer { public: BackgroundMove(); ~BackgroundMove(); virtual bool init(); virtual void onEnterTransitionDidFinish(); //等进入场景之后在进行背景的移动 CREATE_FUNC(BackgroundMove); public: void move(float dt); private: Sprite* m_background1; Sprite* m_background2; Sprite* m_background3; Sprite* m_background4; enum { OFFSET = 3 }; }; ~~~ 背景无限滚动的方式有很多,只要不出现黑边即可 .cpp ~~~ BackgroundMove::BackgroundMove() : m_background1(NULL), m_background2(NULL), m_background3(NULL), m_background4(NULL) { } BackgroundMove::~BackgroundMove() { CC_SAFE_DELETE(m_background1); CC_SAFE_DELETE(m_background2); CC_SAFE_DELETE(m_background3); CC_SAFE_DELETE(m_background4); } bool BackgroundMove::init() { bool bRect = false; do { if (!Layer::init()) return false; //加载背景图片 m_background1 = Sprite::createWithSpriteFrameName("backgroundTollgate2.png"); m_background1->setPosition(Point(0,0)); m_background1->setAnchorPoint(Vec2(0, 0)); this->addChild(m_background1,1); m_background2 = Sprite::createWithSpriteFrameName("backgroundTollgate2.png"); m_background2->setPosition(Point(0, 0)); m_background2->setAnchorPoint(Vec2(0, 0)); m_background2->setFlipY(true); this->addChild(m_background2,1); //加载背景图片 m_background3 = Sprite::createWithSpriteFrameName("backgroundTollgateThree.png"); m_background3->setPosition(Point(0, 0)); m_background3->setAnchorPoint(Vec2(0, 0)); this->addChild(m_background3, 0); m_background4 = Sprite::createWithSpriteFrameName("backgroundTollgateThree.png"); m_background4->setPosition(Point(0, 0)); m_background4->setAnchorPoint(Vec2(0, 0)); m_background4->setFlipY(true); this->addChild(m_background4, 0); CocosDenshion::SimpleAudioEngine::getInstance()->playBackgroundMusic("sound/BackgroundMusic.mp3", true); bRect = true; } while (0); return bRect; } void BackgroundMove::onEnterTransitionDidFinish() { Layer::onEnterTransitionDidFinish(); this->schedule(SEL_SCHEDULE(&BackgroundMove::move), 0.01f); } void BackgroundMove::move(float dt) { Vec2 origin = Director::getInstance()->getVisibleOrigin(); m_background1->setPositionY(m_background1->getPositionY() - OFFSET); m_background2->setPositionY(m_background1->getPositionY() + m_background1->getContentSize().height); if (m_background2->getPositionY() <= origin.y) m_background1->setPositionY(0); m_background3->setPositionY(m_background3->getPositionY() + OFFSET); m_background4->setPositionY(m_background3->getPositionY() - m_background3->getContentSize().height); if (m_background4->getPositionY() >= origin.y) m_background3->setPositionY(0); } ~~~
';

(七) 控制器的实现

最后更新于:2022-04-01 16:12:20

控制器中的功能并不多,主要是下面这些 ~~~ //对玩家分数的操作 CC_SYNTHESIZE_READONLY(SaveData *, m_saveData, SaveData); void update(float tm); //游戏暂停与恢复 void menuPauseCallback(cocos2d::Ref* pSender); //声音控制 void menuMusicCallback(cocos2d::Ref* pSender); ~~~ 下面是这些功能的实现 ~~~ bool Controller::init() { if (!Layer::init()) { return false; } bool bRect = false; do { Size winSize = Director::getInstance()->getWinSize(); //从xml文件中读取中文显示出来 auto dictionary = Dictionary::createWithContentsOfFile("fonts/AboutMe.xml"); score_label = Label::createWithTTF( ((__String *)(dictionary->objectForKey("score")))->getCString(), "fonts/DFPShaoNvW5-GB.ttf", 25); score_label->setPosition(score_label->getContentSize().width / 2, winSize.height - score_label->getContentSize().height * 2); CC_BREAK_IF(!score_label); this->addChild(score_label); //添加显示分数的标签 m_saveData = SaveData::create(); //这里一定要retain一下saveData,在析构函数中release一下 m_saveData->retain(); auto str = __String::createWithFormat("%d", m_saveData->getScore()); m_score = Label::createWithTTF(str->getCString(), "fonts/DFPShaoNvW5-GB.ttf", 25); m_score->setPosition(Point(score_label->getContentSize().width + m_score->getContentSize().width / 2 + 30, winSize.height - score_label->getContentSize().height * 2)); CC_BREAK_IF(!m_score); this->addChild(m_score); //记得更新分数的显示 this->scheduleUpdate(); //游戏声音控制按钮 Sprite *normalMusic = Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("game_musicPause.png")); Sprite *pressedMusic = Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("game_musicPause.png")); pMusicItem = MenuItemSprite::create( normalMusic, normalMusic, NULL, CC_CALLBACK_1(Controller::menuMusicCallback, this)); //游戏暂停按钮 Sprite *normalPause = Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("game_resume_nor.png")); Sprite *pressedPause = Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("game_resume_pressed.png")); pPauseItem = MenuItemSprite::create( normalPause, pressedPause, NULL, CC_CALLBACK_1(Controller::menuPauseCallback, this)); Menu *menuPause = Menu::create(pMusicItem,pPauseItem, NULL); menuPause->alignItemsHorizontallyWithPadding(pPauseItem->getContentSize().width/2); menuPause->setPosition( Point(winSize.width - pPauseItem->getContentSize().width*2, winSize.height - normalPause->getContentSize().height)); this->addChild(menuPause); } while (0); return true; } //游戏暂停 void Controller::menuPauseCallback(cocos2d::Ref* pSender) { CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("sound/button.mp3"); if (!Director::getInstance()->isPaused()) { // 图标状态设置 pPauseItem->setNormalImage(Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("game_pause_nor.png"))); pPauseItem->setSelectedImage(Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("game_pause_press.png"))); CocosDenshion::SimpleAudioEngine::getInstance()->pauseBackgroundMusic(); // 停止背景音乐 CocosDenshion::SimpleAudioEngine::getInstance()->stopAllEffects(); // 停止所有的特效 Director::getInstance()->pause(); // 停止所有的动作,敌机飞行,子弹前进等 } else { pPauseItem->setNormalImage(Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("game_resume_nor.png"))); pPauseItem->setSelectedImage(Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("game_resume_pressed.png"))); CocosDenshion::SimpleAudioEngine::getInstance()->resumeBackgroundMusic();// 恢复 Director::getInstance()->resume(); // 恢复 } } void Controller::menuMusicCallback(cocos2d::Ref* pSender) { CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("sound/button.mp3"); if (CocosDenshion::SimpleAudioEngine::getInstance()->isBackgroundMusicPlaying()) { // 图标状态设置 pMusicItem->setNormalImage(Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("game_music.png"))); pMusicItem->setSelectedImage(Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("game_music.png"))); CocosDenshion::SimpleAudioEngine::getInstance()->stopBackgroundMusic(); // 停止背景音乐 // CocosDenshion::SimpleAudioEngine::getInstance()->stopAllEffects(); // 停止所有的特效 } else { pMusicItem->setNormalImage(Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("game_musicPause.png"))); pMusicItem->setSelectedImage(Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("game_musicPause.png"))); CocosDenshion::SimpleAudioEngine::getInstance()->playBackgroundMusic("sound/BackgroundMusic.mp3", true);// 恢复 // CocosDenshion::SimpleAudioEngine::getInstance()->resumeAllEffects(); } } void Controller::update(float tm) { auto str = __String::createWithFormat("%d", m_saveData->getScore()); //更新分数和坐标 m_score->setColor(Color3B(255, 0, 0)); m_score->setString(str->getCString()); m_score->setPositionX(score_label->getContentSize().width + m_score->getContentSize().width / 2 + 30); } ~~~ 要实现游戏的暂停功能,可以直接将当前运行的场景暂停,而要实现声音的暂停,通过简单的停止背景音乐、音效却不行。因为不断有新的子弹在发射、新的敌机在爆炸等。所以,我使用的方法是 将背景音乐与其他音效绑定。 比如下面子弹类中的代码 ~~~ if (CocosDenshion::SimpleAudioEngine::getInstance()->isBackgroundMusicPlaying()) { CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("sound/bullet.wav"); } ~~~ 只有背景音乐处于播放状态,音效才会播放。 虽然功能实现了,不过总感觉方法太水了。。。谁有更好的方式欢迎告知。
';

(六) 保存玩家数据

最后更新于:2022-04-01 16:12:18

玩家要保存的数据可能很多,这里,我们只保存分数 这个类的成员变量和函数如下 ~~~ //用户数据操作的成员变量 UserDefault * m_userDefault; //要用到这种特殊的容器了。。 ValueVector m_vector; //记录玩家的当前分数 CC_SYNTHESIZE(int, m_score, Score); void save(); ~~~ 我们使用cocos2d-x提供的UserDefault类来实现数据保存功能 对于UserDefault类,我也不太熟悉,主要是参考官方文档和网上的例子来做的,不过基本思路还算清晰 ~~~ bool SaveData::init() { m_userDefault = UserDefault::getInstance(); m_vector = ValueVector(); m_score = 0; //每玩一次游戏,分数的记录条数就会加一 m_userDefault->setIntegerForKey("count", (m_userDefault->getIntegerForKey("count", 0)) + 1); //首先判断XML文件是否存在,如果不存在的话就会执行if中的语句 if (m_userDefault->getBoolForKey("isExit", false) == false) { //玩家初次玩游戏会执行这里 m_userDefault->setBoolForKey("isExit", true); } else { //将分数记录保存在vector集合中 for (int i = 0; i < m_userDefault->getIntegerForKey("count") - 1; i++) { __String * index = String::createWithFormat("%d", i); //将要放的数据使用Value包装一下 m_vector.push_back(Value(m_userDefault->getIntegerForKey(index->getCString()))); } } return true; } void SaveData::save() { /*本函数的整体思路是先对vector中保存的玩家数据进行排序,然后重新写入到xml文件中*/ //将玩家的分数保存到set集合中,以便排序,分数和原先的分数不同才保存 int i = 0; for (auto tem : m_vector) { if (tem.asInt() == m_score) { break; } i++; } if (i == m_vector.size()) { m_vector.push_back(Value(m_score)); //自定义排序函数,对m_vector中的内容进行排序,方便以后对数据的操作 auto sortData = [](Value value1, Value value2) { return value1.asInt() > value2.asInt(); }; //调用c++模板中的sort函数进行排序,前俩个参数是数组的地址,最后一个参数是使用的排序函数 std::sort(m_vector.begin(), m_vector.end(), sortData); //将玩家的得分保存在文件中 for (int i = 0; i < m_vector.size(); i++) { auto value = m_vector.at(i); auto index = __String::createWithFormat("%d", i); m_userDefault->setIntegerForKey(index->getCString(), value.asInt()); } } //单独保存本次游戏的得分 UserDefault::getInstance()->setIntegerForKey("currentScore", m_score); //重新设置一下count UserDefault::getInstance()->setIntegerForKey("count", m_vector.size()); //刷新 m_userDefault->flush(); } ~~~
';

(五) 添加子弹

最后更新于:2022-04-01 16:12:16

我方飞机的子弹和敌机子弹都在这个类中产生。将子弹专门设计成一个类,主要是为了方便扩展。之后如果想更换我方飞机子弹或者是敌机子弹,都会很方便。 类的功能很直观,就是添加子弹、移除子弹 ~~~ void bindEnemyManager(EnemyManager* enemyManager); void BeginBulletShoot(float dt = 0.0f); // 开启子弹射击 void StopBulletShoot(); // 停止子弹射击 void addBullet(float dt); // 添加子弹 void removeBullet(Node* pNode); // 移除子弹 void addEnemyBullet(float dt); // 添加敌机子弹 void removeEnemyBullet(Node* pNode); // 移除敌机子弹 ~~~ 添加的子弹像敌机一样,存储在容器中 ~~~ Vector<Sprite*> vecBullet; Vector<Sprite*> vecEnemyBullet; EnemyManager* m_enemyManager; ~~~ 想给敌机添加子弹,要将子弹类和敌机管理类绑定,通过敌机管理类中的容器,来获取敌机对象。 我们通过对比敌机类和我方飞机类,可以发现,我方飞机精灵直接嵌入在飞机层中,并通过setTag()设置标识,可以通过标识来调用,也可以通过调用飞机层的实例instancePlane来间接调用。而敌机虽然也设置了标识,但是可以通过getSprite()函数来获取精灵实体。 接下来实现添加子弹的功能 ~~~ bool Bullet::init() { if (!Layer::init()) { return false; } BeginBulletShoot(); return true; } void Bullet::bindEnemyManager(EnemyManager* enemyManager) { this->m_enemyManager = enemyManager; m_enemyManager->retain(); } void Bullet::BeginBulletShoot(float dt) { this->schedule(schedule_selector(Bullet::addBullet), 0.2f, kRepeatForever, dt); this->schedule(schedule_selector(Bullet::addEnemyBullet), 1.0f, kRepeatForever, dt); } void Bullet::StopBulletShoot() { this->unschedule(schedule_selector(Bullet::addBullet)); this->unschedule(schedule_selector(Bullet::addEnemyBullet)); } void Bullet::addBullet(float dt) { // 子弹 auto bullet = Sprite::createWithSpriteFrameName("bullet1.png"); if (NULL == bullet) { return; } this->addChild(bullet); // 加到Layer中去 vecBullet.pushBack(bullet); // 加到容器中去,用于以后的碰撞检测等 // 获得飞机的位置 Point planePos = MyPlane::instancePlane->getChildByTag(AIRPLANE)->getPosition(); Point bulletPos = Point(planePos.x, planePos.y + MyPlane::instancePlane->getChildByTag(AIRPLANE)->getContentSize().height / 2); bullet->setPosition(bulletPos); // 飞行长度 飞行就是超出窗体 float flyLen = Director::getInstance()->getWinSize().height + bullet->getContentSize().height / 2 - bulletPos.y; float flyVelocity = 320 / 1; //飞行速度 float realFlyDuration = flyLen / flyVelocity; // 飞行时间 auto actionMove = MoveTo::create(realFlyDuration, Point(bulletPos.x, Director::getInstance()->getWinSize().height + bullet->getContentSize().height / 2)); auto actionDone = CallFuncN::create(CC_CALLBACK_1(Bullet::removeBullet, this)); Sequence* sequence = Sequence::create(actionMove, actionDone, NULL); bullet->runAction(sequence); if (CocosDenshion::SimpleAudioEngine::getInstance()->isBackgroundMusicPlaying()) { CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("sound/bullet.wav"); } } void Bullet::removeBullet(Node* pNode) { if (NULL == pNode) { return; } Sprite* bullet = (Sprite*)pNode; this->removeChild(bullet, true); vecBullet.eraseObject(bullet); } // 添加敌机子弹 void Bullet::addEnemyBullet(float dt) { for (auto& eEnemy : m_enemyManager->vecEnemy) { Enemy* pEnemySprite = (Enemy*)eEnemy; // 子弹 auto bullet = Sprite::createWithSpriteFrameName("enemyBullet.png"); if (NULL == bullet) { return; } this->addChild(bullet); // 加到Layer中去 vecEnemyBullet.pushBack(bullet); // 加到容器中去,用于以后的碰撞检测等 // 获得敌方飞机的位置 Point enemyPos = pEnemySprite->getPosition(); Point bulletPos = Point(enemyPos.x, enemyPos.y - pEnemySprite->getContentSize().height / 2); bullet->setPosition(bulletPos); // 飞行长度 飞行就是超出窗体 float flyLen = bulletPos.y - bullet->getContentSize().height / 2; float flyVelocity = 320 / 1; //飞行速度 float realFlyDuration = flyLen / flyVelocity; // 飞行时间 auto actionMove = MoveTo::create(realFlyDuration, Point(bulletPos.x, Director::getInstance()->getVisibleOrigin().y - bullet->getContentSize().height / 2)); auto actionDone = CallFuncN::create(CC_CALLBACK_1(Bullet::removeEnemyBullet, this)); Sequence* sequence = Sequence::create(actionMove, actionDone, NULL); bullet->runAction(sequence); if (CocosDenshion::SimpleAudioEngine::getInstance()->isBackgroundMusicPlaying()) { CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("sound/enemyBullet.wav"); } } } // 移除敌机子弹 void Bullet::removeEnemyBullet(Node* pNode) { if (NULL == pNode) { return; } Sprite* bullet = (Sprite*)pNode; this->removeChild(bullet, true); vecEnemyBullet.eraseObject(bullet); } ~~~ 注意不要将添加我方飞机子弹和添加敌机子弹放在一个函数中,那样不仅使代码混乱,而且不利于扩展。
';

(四) 敌机管理

最后更新于:2022-04-01 16:12:14

敌方飞机应该不定时的出现,有自己的生命周期、运动轨迹。这个类用来管理敌机的产生、移动、爆炸、销毁等。 敌机管理类主要函数如下 ~~~ //绑定控制器(更新分数) void bindController(Controller* controller); //根据分数决定添加敌机速度 void addSpeed(float dt); // 添加敌机1 void addEnemy1(float dt); // 添加敌机2 void addEnemy2(float dt); // 添加敌机3 void addEnemy3(float dt); // 添加敌机4 void addEnemy4(float dt); // 敌机爆炸 void blowupEnemy(Enemy* pEnemySprite); // 移除敌机pNode void removeEnemy(Node *pNode); ~~~ 其成员变量如下 ~~~ Vector<Enemy*> vecEnemy;// 敌机容器,用于遍历碰撞问题 Controller* m_controlLayer; //控制器 float m_fSpeed; //添加敌机速度 float m_fEnemy1; float m_fEnemy2; float m_fEnemy3; float m_fEnemy4; ~~~ 敌机产生后,要先存储在容器中,通过容器来进行与子弹、我方飞机的碰撞检测,之后统一销毁。 还有就是,因为分数是要存储在数据库中的,所以我们用一个控制器来管理分数。对于我方飞机的触摸移动,也可以用控制器来控制,由于本游戏中的我方飞机操作简单,代码就嵌在了我方飞机类中。(⊙o⊙)…扯远了,先介绍敌机管理类的实现,再讲控制器。 ~~~ bool EnemyManager::init() { if (!Layer::init()) { return false; } cocos2d::Vector<SpriteFrame*> vecTemp; vecTemp.clear(); // 敌机1爆炸 for (int i = 0; i < 4; i++) { auto blowUpName = __String::createWithFormat("enemy1_down%d.png", i + 1); auto tempBlowUp = SpriteFrameCache::getInstance()->getSpriteFrameByName( blowUpName->getCString()); vecTemp.pushBack(tempBlowUp); } Animation* pAnimation1 = Animation::createWithSpriteFrames(vecTemp, 0.1f); // 添加到AnimationCache,并且命名为Enemy1Blowup AnimationCache::getInstance()->addAnimation(pAnimation1, "Enemy1Blowup"); // 敌机2爆炸 vecTemp.clear(); for (int i = 0; i < 4; i++) { auto blowUpName = __String::createWithFormat("enemy2_down%d.png", i + 1); auto tempBlowUp = SpriteFrameCache::getInstance()->getSpriteFrameByName( blowUpName->getCString()); vecTemp.pushBack(tempBlowUp); } Animation *pAnimation2 = Animation::createWithSpriteFrames(vecTemp,0.1f); AnimationCache::getInstance()->addAnimation(pAnimation2, "Enemy2Blowup"); // 敌机3爆炸 vecTemp.clear(); for (int i = 0; i < 4; i++) { auto blowUpName = __String::createWithFormat("enemy3_down%d.png", i + 1); auto tempBlowUp = SpriteFrameCache::getInstance()->getSpriteFrameByName( blowUpName->getCString()); vecTemp.pushBack(tempBlowUp); } Animation *pAnimation3 = Animation::createWithSpriteFrames(vecTemp,0.1f); AnimationCache::getInstance()->addAnimation(pAnimation3, "Enemy3Blowup"); // 敌机4爆炸 vecTemp.clear(); for (int i = 0; i < 4; i++) { auto blowUpName = __String::createWithFormat("enemy4_down%d.png", i + 1); auto tempBlowUp = SpriteFrameCache::getInstance()->getSpriteFrameByName( blowUpName->getCString()); vecTemp.pushBack(tempBlowUp); } Animation *pAnimation4 = Animation::createWithSpriteFrames(vecTemp, 0.1f); AnimationCache::getInstance()->addAnimation(pAnimation4, "Enemy4Blowup"); //根据当前分数来设定添加各种敌机的速度 this->schedule(schedule_selector(EnemyManager::addSpeed), 0.1f); return true; } //绑定控制器(更新分数) void EnemyManager::bindController(Controller* controller) { this->m_controlLayer = controller; m_controlLayer->retain(); } //根据分数决定添加敌机速度 void EnemyManager::addSpeed(float dt) { m_fSpeed = m_controlLayer->getSaveData()->getScore() / 1000 + 1; this->schedule(schedule_selector(EnemyManager::addEnemy1), m_fEnemy1 / m_fSpeed); // 每1秒出现一架敌机1 this->schedule(schedule_selector(EnemyManager::addEnemy2), m_fEnemy2 / m_fSpeed); this->schedule(schedule_selector(EnemyManager::addEnemy3), m_fEnemy3 / m_fSpeed); this->schedule(schedule_selector(EnemyManager::addEnemy4), m_fEnemy4 / m_fSpeed); } void EnemyManager::addEnemy1(float dt) { Size size = Director::getInstance()->getVisibleSize(); Enemy *pEnemySprite = Enemy::create(); pEnemySprite->setEnemyByType(Enemy1); pEnemySprite->setTag(Enemy1); this->addChild(pEnemySprite); vecEnemy.pushBack(pEnemySprite); // 设置运动轨迹 以及到终点时调用的函数 ccBezierConfig m_bezier; m_bezier.controlPoint_1 = ccp(size.width/20, size.height*0.7); m_bezier.controlPoint_2 = ccp(size.width/2, size.height/2); m_bezier.endPosition = ccp(size.width*0.9, size.height*0.9); auto actionMove = BezierTo::create(2.0f, m_bezier); auto actionDone = CallFuncN::create(CC_CALLBACK_1(EnemyManager::removeEnemy, this)); Sequence* sequence = Sequence::create(actionMove, actionDone, NULL); pEnemySprite->runAction(sequence); //根据分数改变敌机数量 } void EnemyManager::addEnemy2(float dt) { Size size = Director::getInstance()->getVisibleSize(); Enemy *pEnemySprite = Enemy::create(); pEnemySprite->setEnemyByType(Enemy2); pEnemySprite->setTag(Enemy2); this->addChild(pEnemySprite); vecEnemy.pushBack(pEnemySprite); // 设置运动轨迹 以及到终点时调用的函数 ccBezierConfig m_bezier; m_bezier.controlPoint_1 = ccp(40, 500); m_bezier.controlPoint_2 = ccp(250, 400); m_bezier.endPosition = ccp(400, 700); auto actionMove = BezierTo::create(4.0f, m_bezier); auto actionDone = CallFuncN::create(CC_CALLBACK_1(EnemyManager::removeEnemy, this)); Sequence* sequence = Sequence::create(actionMove, actionDone, NULL); pEnemySprite->runAction(sequence); } void EnemyManager::addEnemy3(float dt) { Size size = Director::getInstance()->getVisibleSize(); Enemy *pEnemySprite = Enemy::create(); pEnemySprite->setEnemyByType(Enemy3); pEnemySprite->setTag(Enemy3); this->addChild(pEnemySprite); vecEnemy.pushBack(pEnemySprite); // 设置运动轨迹 以及到终点时调用的函数 ccBezierConfig m_bezier; m_bezier.controlPoint_1 = ccp(60, 550); m_bezier.controlPoint_2 = ccp(100, 400); m_bezier.endPosition = ccp(400, 700); auto actionMove = BezierTo::create(6.0f, m_bezier); auto actionDone = CallFuncN::create(CC_CALLBACK_1(EnemyManager::removeEnemy, this)); Sequence* sequence = Sequence::create(actionMove, actionDone, NULL); pEnemySprite->runAction(sequence); } // 添加敌机4 void EnemyManager::addEnemy4(float dt) { Enemy *pEnemySprite = Enemy::create(); pEnemySprite->setEnemyByType(Enemy4); pEnemySprite->setTag(Enemy4); this->addChild(pEnemySprite); vecEnemy.pushBack(pEnemySprite); // 设置运动轨迹 以及到终点时调用的函数 ccBezierConfig m_bezier; m_bezier.controlPoint_1 = ccp(80, 650); m_bezier.controlPoint_2 = ccp(350, 450); m_bezier.endPosition = ccp(400, 700); auto actionMove = BezierTo::create(8.0f, m_bezier); auto actionDone = CallFuncN::create(CC_CALLBACK_1(EnemyManager::removeEnemy, this)); // 按顺序执行 敌机飞到边缘,敌机移动结束 Sequence* sequence = Sequence::create(actionMove, actionDone, NULL); pEnemySprite->runAction(sequence); } void EnemyManager::removeEnemy(Node *pNode) { Enemy* enemy = (Enemy*)pNode; if (enemy != NULL) { this->removeChild(enemy, true); vecEnemy.eraseObject(enemy); } } void EnemyManager::blowupEnemy(Enemy* pEnemySprite) { auto saveData = m_controlLayer->getSaveData(); Animation *pAnimation = NULL; if (Enemy1 == pEnemySprite->getTag()) { // 之前缓存的爆炸动作 pAnimation = AnimationCache::getInstance()->getAnimation("Enemy1Blowup"); if (CocosDenshion::SimpleAudioEngine::getInstance()->isBackgroundMusicPlaying()) { } if (CocosDenshion::SimpleAudioEngine::getInstance()->isBackgroundMusicPlaying()) { CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("sound/enemy1_down.wav"); } saveData->setScore(saveData->getScore() + ENEMY1_SCORE); } else if (Enemy2 == pEnemySprite->getTag()) { pAnimation = AnimationCache::getInstance()->getAnimation("Enemy2Blowup"); if (CocosDenshion::SimpleAudioEngine::getInstance()->isBackgroundMusicPlaying()) { CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("sound/enemy2_down.wav"); } saveData->setScore(saveData->getScore() + ENEMY2_SCORE); } else if (Enemy3 == pEnemySprite->getTag()) { pAnimation = AnimationCache::getInstance()->getAnimation("Enemy3Blowup"); if (CocosDenshion::SimpleAudioEngine::getInstance()->isBackgroundMusicPlaying()) { CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("sound/enemy3_down.wav"); } saveData->setScore(saveData->getScore() + ENEMY3_SCORE); } else if (Enemy4 == pEnemySprite->getTag()) { pAnimation = AnimationCache::getInstance()->getAnimation("Enemy4Blowup"); if (CocosDenshion::SimpleAudioEngine::getInstance()->isBackgroundMusicPlaying()) { CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("sound/enemy4_down.wav"); } saveData->setScore(saveData->getScore() + ENEMY4_SCORE); } else { return; } Animate *pAnimate = Animate::create(pAnimation); // 爆炸完,要移除敌机 auto pActionDone = CallFuncN::create(CC_CALLBACK_0(EnemyManager::removeEnemy, this, pEnemySprite)); Sequence* pSequence = Sequence::create(pAnimate, pActionDone, NULL); pEnemySprite->getSprite()->runAction(pSequence); } ~~~ 每种类型的敌机的爆炸代码比较短,直接在init()函数中进行初始化。为了使游戏难度不断增加,会根据分数来改变添加敌机的速度。敌机的运动轨迹大同小异,不过贝塞尔曲线看起来确实挺不错的。
';

(三) 敌机实现

最后更新于:2022-04-01 16:12:11

        现在来实现敌机类         敌机和我方飞机相似,具有生命值、能够发射子弹,并且有自己的运动轨迹。其实可以为它们设计一个共同的基类,这样可以更方便扩展。 不同的敌机,应设置不同的标识、属性 ~~~ // 敌机生命值 const int ENEMY1_MAXLIFE = 1; const int ENEMY2_MAXLIFE = 2; const int ENEMY3_MAXLIFE = 5; const int ENEMY4_MAXLIFE = 10; // 敌机分数 const int ENEMY1_SCORE = 1; const int ENEMY2_SCORE = 6; const int ENEMY3_SCORE = 20; const int ENEMY4_SCORE = 50; ~~~ ~~~ // 敌机类型 enum EnemyType { Enemy1 = 1, Enemy2, Enemy3, Enemy4, }; ~~~ 头文件中的主要函数 ~~~ void setEnemyByType(EnemyType enType); Sprite* getSprite(); int getLife(); void loseLife(); Rect getBoundingBox(); ~~~ 函数的实现 ~~~ void Enemy::setEnemyByType(EnemyType enType) { switch (enType) { case Enemy1: pEnemySprite = Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("enemy1.png")); nLife = ENEMY1_MAXLIFE; break; case Enemy2: pEnemySprite = Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("enemy2.png")); nLife = ENEMY2_MAXLIFE; break; case Enemy3: pEnemySprite = Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("enemy3.png")); nLife = ENEMY3_MAXLIFE; break; case Enemy4: pEnemySprite = Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("enemy4.png")); nLife = ENEMY4_MAXLIFE; break; default: return; break; } this->addChild(pEnemySprite); Size winSize = Director::getInstance()->getWinSize(); Size enemySize = pEnemySprite->getContentSize(); int minX = enemySize.width / 2; int maxX = winSize.width - enemySize.width / 2; int rangeX = maxX - minX; int actualX = (rand() % rangeX) + minX; // 设置敌机Node方位 Node包含Sprite this->setPosition(Point(actualX, winSize.height - enemySize.height / 2)); } bool Enemy::init() { bool pRet = true; if (!Node::init()) { pRet = false; } return pRet; } Sprite* Enemy::getSprite() { return pEnemySprite; } int Enemy::getLife() { return nLife; } void Enemy::loseLife() { --nLife; } Rect Enemy::getBoundingBox() { Rect rect = pEnemySprite->boundingBox(); Point pos = this->convertToWorldSpace(rect.origin); Rect enemyRect(pos.x, pos.y, rect.size.width, rect.size.height); return enemyRect; } ~~~ 根据敌机类型,绑定相应的图片和生命值,对于我方飞机,如果想根据生命值来设定不同的飞机样式,也可以通过此类方法。
';

(二) 我方飞机的实现

最后更新于:2022-04-01 16:12:09

        在上一篇中,我们实现了游戏的开始界面,接下来要实现游戏的主界面,主界面包含地图、我方飞机、敌机等         先来实现我方飞机 我方飞机具有哪些属性呢? 飞机要具有生命值、要有动画效果(尾部喷气),飞机不能够飞出边界,所以要进行边界检测,当飞机生命值为0时,飞机会爆炸,然后被移除。 .h文件 ~~~ //飞机动画 Animate* planeFly(); //边界检测 void borderCheck(float dt); //飞机爆炸 void blowUp(); //移除飞机 void removePlane(); //获取生命值 int getAlive(); //设定生命值 void loseAlive(); // 更新生命值 void updateAlive(int alive); ~~~ 这个变量在create()函数中初始化,方便其他层调用我方飞机的相关数据 ~~~ static MyPlane* instancePlane; //飞机实例 ~~~ 我方飞机的生命值直接在这里显示、更新,不受控制器的控制 ~~~ private: int m_alive; Label* aliveItem1; Label* aliveItem2; ~~~ .cpp文件 ~~~ /* ************************************************************************ * * MyPlane.cpp * 杜星飞 2015年8月13日 * 描述: 包含飞机的属性、功能等 * ************************************************************************ */ #include "MyPlane.h" #include "SimpleAudioEngine.h" MyPlane::MyPlane() :m_alive(5) { } MyPlane::~MyPlane() { } MyPlane* MyPlane::instancePlane = NULL; MyPlane* MyPlane::create() { MyPlane* m_plane = NULL; do { m_plane = new MyPlane(); CC_BREAK_IF(!m_plane); if (m_plane && m_plane->init()) { m_plane->autorelease(); instancePlane = m_plane; } else CC_SAFE_DELETE(m_plane); } while (0); return m_plane; } //飞机动画 Animate* MyPlane::planeFly() { Vector<SpriteFrame *> vector; for (int i = 0; i < 2; i++) { auto frameName = __String::createWithFormat("chinaFly%d.png", i + 1); auto temSpriteFrame = SpriteFrameCache::getInstance()->getSpriteFrameByName(frameName->getCString()); vector.pushBack(temSpriteFrame); } //设置不断播放飞机的动画 auto animation = Animation::createWithSpriteFrames(vector, 0.2f, -1); auto animate = Animate::create(animation); return animate; } bool MyPlane::init() { if(!Layer::init()) return false; Size winSize = Director::getInstance()->getWinSize(); //添加飞机 auto m_planeSprite = Sprite::createWithSpriteFrameName("chinaFly1.png"); m_planeSprite->setPosition(Point(winSize.width / 2, m_planeSprite->getContentSize().height / 2)); m_planeSprite->setTag(AIRPLANE); this->addChild(m_planeSprite); m_planeSprite->runAction(this->planeFly()); // 飞机触摸 auto listener = EventListenerTouchOneByOne::create(); listener->setSwallowTouches(true); //吞噬触摸事件 //对触摸事件的监听过程直接写在这里 listener->onTouchBegan = [](Touch* touch, Event *event) { auto target = static_cast<Sprite*>(event->getCurrentTarget()); Point locationInNode = target->convertToNodeSpace(touch->getLocation()); Size s = target->getContentSize(); Rect rect = Rect(0, 0, s.width, s.height); if (rect.containsPoint(locationInNode)) return true; else return false; }; listener->onTouchMoved = [](Touch* touch, Event *event) { auto target = static_cast<Sprite*>(event->getCurrentTarget()); target->setPosition(target->getPosition() + touch->getDelta()); }; listener->onTouchEnded = [](Touch* touch, Event* event) { }; //将触摸监听添加到eventDispacher中去 _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, m_planeSprite); //初始化生命值 //设置标签 并 获取中文文本 auto dictionary = Dictionary::createWithContentsOfFile("fonts/AboutMe.xml"); aliveItem1 = Label::createWithTTF( (((__String*)(dictionary->objectForKey("alive"))))->getCString(), "fonts/DFPShaoNvW5-GB.ttf", 25); aliveItem1->setPosition(Point(winSize.width/8, winSize.height-aliveItem1->getContentSize().height)); aliveItem1->setColor(Color3B(255, 0, 0)); this->addChild(aliveItem1); aliveItem2 = Label::createWithTTF( "5", "fonts/DFPShaoNvW5-GB.ttf", 25); aliveItem2->setPosition(Point(aliveItem1->getPositionX()*2, winSize.height - aliveItem1->getContentSize().height)); aliveItem2->setColor(Color3B(255, 0, 0)); this->addChild(aliveItem2); // 开启边界检测 this->schedule(schedule_selector(MyPlane::borderCheck)); return true; } //边界检测 void MyPlane::borderCheck(float dt) { //进行边界判断,不可超出屏幕 Point location = this->getChildByTag(AIRPLANE)->getPosition(); Size winSize = Director::getInstance()->getWinSize(); // 返回的就是这个矩形的大小 Size planeSize = this->getChildByTag(AIRPLANE)->getContentSize(); if (location.x<planeSize.width / 2) location.x = planeSize.width / 2; if (location.x>winSize.width - planeSize.width / 2) location.x = winSize.width - planeSize.width / 2; if (location.y<planeSize.height / 2) location.y = planeSize.height / 2; if (location.y>winSize.height - planeSize.height / 2) location.y = winSize.height - planeSize.height / 2; this->getChildByTag(AIRPLANE)->setPosition(location); } //飞机爆炸 void MyPlane::blowUp() { this->unscheduleAllSelectors(); // 停止飞机的所有行动 //加载飞机爆炸动画 音效 if (CocosDenshion::SimpleAudioEngine::getInstance()->isBackgroundMusicPlaying()) { CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("sound/chinaDown.mp3"); } Vector<SpriteFrame*> planeBlowUp; for (int i = 0; i < 4; i++) { auto planeName = __String::createWithFormat("china1_down%d.png", i + 1); auto tempBlowUp = SpriteFrameCache::getInstance()->getSpriteFrameByName( planeName->getCString()); planeBlowUp.pushBack(tempBlowUp); } Animation* animation = Animation::createWithSpriteFrames(planeBlowUp, 0.2f); Animate* animate = Animate::create(animation); CallFunc* m_removePlane = CallFunc::create(this, callfunc_selector(MyPlane::removePlane)); Sequence* sequence = Sequence::create(animate, m_removePlane, NULL); // 停止一切的飞机动作 this->getChildByTag(AIRPLANE)->stopAllActions(); this->getChildByTag(AIRPLANE)->runAction(sequence); } //移除飞机 void MyPlane::removePlane() { // 移除飞机精灵 true子节点上的所有运行行为和回调将清理 this->removeChildByTag(AIRPLANE, true); } //获取生命值 int MyPlane::getAlive() { return m_alive; } //设定生命值 void MyPlane::loseAlive() { --m_alive; updateAlive(m_alive); } // 更新生命值 void MyPlane::updateAlive(int alive) { if (alive >= 0) { CCString* strAlive = CCString::createWithFormat("%d", alive); aliveItem2->setString(strAlive->getCString()); aliveItem2->setColor(Color3B(rand_0_1() * 255, rand_0_1() * 255, rand_0_1() * 255)); } } ~~~ 更新生命值的函数只用在我方飞机生命值减少是调用。 还有就是对于中文字符的处理 ~~~ //设置标签 并 获取中文文本 auto dictionary = Dictionary::createWithContentsOfFile("fonts/AboutMe.xml"); ~~~ 可以在项目中添加一个XML文件 ~~~ <?xml version="1.0" encoding="UTF-8"?> <dict> <key>play</key> <string>开始游戏</string> <key>score</key> <string>得分:</string> <key>alive</key> <string>生命:</string> ~~~ 通过相应的key来显示显示相应的中文。 还有就是,有些字体不支持中文的显示,比如系统自带的 arial.ttf就不行,而DFPShaoNvW5-GB.ttf可以。
';

(一) 开始界面

最后更新于:2022-04-01 16:12:07

        好久没写过博客了,现在把刚做的游戏发上来吧,以后要注意更新博客啦~! 游戏截图 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-26_57972c2ac4884.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-26_57972c2b2e64f.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-26_57972c2b72726.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-26_57972c2bbb451.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-26_57972c2c2b752.jpg) 游戏整体结构图 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-26_57972c2c765a7.jpg) 第一步 在 AppDelegate 中设定游戏界面大小以及缩放方式 cocos2d-x3.7新生成的项目中,AppDelegate有默认的界面大小以及缩放方式,这里,我对其作出一些更改,使其适应本项目 ~~~ Size frameSize = glview->getFrameSize(); Size winSize=Size(450,750); float widthRate = frameSize.width/winSize.width; float heightRate = frameSize.height/winSize.height; if (widthRate > heightRate) { glview->setDesignResolutionSize(winSize.width, winSize.height*heightRate/widthRate, ResolutionPolicy::NO_BORDER); } else { glview->setDesignResolutionSize(winSize.width*widthRate/heightRate, winSize.height, ResolutionPolicy::NO_BORDER); } ~~~ 游戏背景图片大小为 450*750,所以,这里以背景图片为标准,设置不同的缩放比例 第二步 更改HelloWorld类,使其成为启动界面 自带的HelloWorld类里面并没有太多内容,只要将其删除即可,然后,设计主界面 ~~~ HelloWorld(); ~HelloWorld(); // there's no 'id' in cpp, so we recommend returning the class instance pointer static cocos2d::Scene* createScene(); // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone virtual bool init(); // implement the "static create()" method manually CREATE_FUNC(HelloWorld); ~~~ 在每一个场景中,一般都会出现上面的函数,以后在介绍其他类的时候,除非特殊情况,否则不再说明。 通过文章一开始的图片,我们可以看到,启动界面包含四个菜单项按钮,以及初始动画 ~~~ //预加载声音和图片 void preLoadSoundAndPicture(); //开始游戏 void startGame(Ref* pSender); //高分记录 void highScore(Ref* pSender); //游戏说明 void aboutGame(Ref* pSender); //退出游戏 void menuCloseCallback(cocos2d::Ref* pSender); //启动界面动画 Animate* startMainAnimate(); //卸载不必要的资源 virtual void onExit(); //响应键盘(主要针对Android) void onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event); ~~~ 下面这个变量可以不必在头文件中声明,我们后面介绍另一种方式 ~~~ EventListenerKeyboard* m_listener; ~~~ 下面是cpp文件的实现 ~~~ #include "HelloWorldScene.h" #include "TollgateOne.h" #include "ScoreScene.h" #include "AboutGame.h" USING_NS_CC; HelloWorld::HelloWorld() { } HelloWorld::~HelloWorld() { Director::getInstance()->getEventDispatcher()->removeEventListener(m_listener);<span style="white-space:pre"> </span>//一定要记得在析构函数中移除 } Scene* HelloWorld::createScene() { // 'scene' is an autorelease object auto scene = Scene::create(); // 'layer' is an autorelease object auto layer = HelloWorld::create(); // add layer as a child to scene scene->addChild(layer); // return the scene return scene; } // on "init" you need to initialize your instance bool HelloWorld::init() { ////////////////////////////// // 1. super init first if ( !Layer::init() ) { return false; } //预加载声音 图片 preLoadSoundAndPicture(); //播放背景音乐 CocosDenshion::SimpleAudioEngine::getInstance()->playBackgroundMusic("sound/game_start.mp3",true); Size visibleSize = Director::getInstance()->getVisibleSize(); Vec2 origin = Director::getInstance()->getVisibleOrigin(); //加载背景 auto m_background = Sprite::createWithSpriteFrame( SpriteFrameCache::getInstance()->getSpriteFrameByName("backgroundStartGame.jpg")); m_background->setPosition(Vec2(origin.x + visibleSize.width / 2, visibleSize.height / 2)); m_background->setAnchorPoint(Vec2(0.5, 0.5)); this->addChild(m_background); //加载启动界面动画 auto startSprite = Sprite::createWithSpriteFrameName("backgroundAnimate1.png"); startSprite->setPosition(Vec2(origin.x + visibleSize.width / 2, visibleSize.height / 2)); this->addChild(startSprite, 1); startSprite->runAction(this->startMainAnimate()); ///////////////////////////// // 2. add a menu item with "X" image, which is clicked to quit the program // you may modify it. //开始游戏 按钮 auto tempStart1 = Sprite::createWithSpriteFrameName("StartGame_nor.png"); auto tempStart2 = Sprite::createWithSpriteFrameName("StartGame_touched.png"); auto startItem = MenuItemSprite::create( tempStart1, tempStart2, CC_CALLBACK_1(HelloWorld::startGame, this) ); //高分记录 按钮 auto tempScore1 = Sprite::createWithSpriteFrameName("GameScore_nor.png"); auto tempScore2 = Sprite::createWithSpriteFrameName("GameScore_touched.png"); auto highScoreItem = MenuItemSprite::create( tempScore1, tempScore2, CC_CALLBACK_1(HelloWorld::highScore, this) ); //游戏说明 按钮 auto tempHelp1 = Sprite::createWithSpriteFrameName("GameHelp_nor.png"); auto tempHelp2 = Sprite::createWithSpriteFrameName("GameHelp_touched.png"); auto aboutGameItem = MenuItemSprite::create( tempHelp1, tempHelp2, CC_CALLBACK_1(HelloWorld::aboutGame, this) ); //退出游戏 按钮 auto tempOver1 = Sprite::createWithSpriteFrameName("GameOver_nor.png"); auto tempOver2 = Sprite::createWithSpriteFrameName("GameOver_touched.png"); auto closeItem = MenuItemSprite::create( tempOver1, tempOver2, CC_CALLBACK_1(HelloWorld::menuCloseCallback, this) ); // create menu, it's an autorelease object auto menu = Menu::create(startItem, highScoreItem, aboutGameItem,closeItem, NULL); menu->alignItemsVerticallyWithPadding(closeItem->getContentSize().height/2); menu->setPosition(Vec2(origin.x + visibleSize.width / 2, visibleSize.height / 2)); this->addChild(menu, 1); //监听手机键盘 m_listener = EventListenerKeyboard::create(); m_listener->onKeyReleased = CC_CALLBACK_2(HelloWorld::onKeyReleased, this); Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority( m_listener, this); return true; } //预加载声音 图片 void HelloWorld::preLoadSoundAndPicture() { //加载声音 CocosDenshion::SimpleAudioEngine::getInstance()->preloadBackgroundMusic("sound/game_start.mp3"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadBackgroundMusic("sound/game_over.wav"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadBackgroundMusic("sound/BackgroundMusic.wav"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("sound/achievement.mp3"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("sound/bullet.wav"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("sound/button.mp3"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("sound/enemy1_down.wav"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("sound/enemy2_down.wav"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("sound/enemy3_down.wav"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("sound/get_bomb.mp3"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("sound/get_double_laser.mp3"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("sound/out_porp.mp3"); CocosDenshion::SimpleAudioEngine::getInstance()->preloadEffect("sound/use_bomb.mp3"); //加载图片 SpriteFrameCache::getInstance()->addSpriteFramesWithFile("ui/background.plist"); SpriteFrameCache::getInstance()->addSpriteFramesWithFile("ui/plane.plist"); } //开始游戏 void HelloWorld::startGame(Ref* pSender) { CocosDenshion::SimpleAudioEngine::getInstance()->stopBackgroundMusic(); Director::getInstance()->replaceScene(TollgateOne::createScene()); } //高分记录 void HelloWorld::highScore(Ref* pSender) { Director::getInstance()->pushScene( TransitionProgressRadialCCW::create(1.0f, ScoreScene::createScene())); } //游戏说明 void HelloWorld::aboutGame(Ref* pSender) { Director::getInstance()->pushScene( TransitionJumpZoom::create(1.0f, AboutGame::createScene())); } void HelloWorld::menuCloseCallback(Ref* pSender) { Director::getInstance()->end(); #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) exit(0); #endif } //启动界面动画 Animate* HelloWorld::startMainAnimate() { Vector<SpriteFrame*> vecStartAnimate; for (int i = 0; i < 5; i++) { auto tempString = __String::createWithFormat("backgroundAnimate%d.png", i + 1); auto tempAnimate = SpriteFrameCache::getInstance()->getSpriteFrameByName(tempString->getCString()); vecStartAnimate.pushBack(tempAnimate); } auto animate = Animate::create(Animation::createWithSpriteFrames( vecStartAnimate, 0.5f, -1)); return animate; } //卸载不必要的资源 void HelloWorld::onExit() { Layer::onExit(); Director::getInstance()->getTextureCache()->removeUnusedTextures(); } //响应键盘(主要针对Android) void HelloWorld::onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event) { if (keyCode == EventKeyboard::KeyCode::KEY_ESCAPE) Director::getInstance()->end(); } ~~~ 之前一直用cocos2d-x2.2.6,现在换成3.7了,感觉变化挺大的。 游戏中用到的所有资源文件,会在最后上传。
';

前言

最后更新于:2022-04-01 16:12:04

> 原文出处:[cocos2d-x游戏实战-决战南海](http://blog.csdn.net/column/details/southchinaseawar.html) 作者:[u011694809](http://blog.csdn.net/u011694809) **本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!** # cocos2d-x游戏实战-决战南海 > 使用cocos2d-x3.7开发的飞行射击对战游戏。包括各种精彩道具以及广告的植入
';