(十三) 分数场景
最后更新于: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开发的飞行射击对战游戏。包括各种精彩道具以及广告的植入