cocos2dx官网游戏开始时道具加载的实现

cocos2dx对精灵的优化|皂荚花
欢迎来自的盆友!
程序不是看出来的,是写出来的!思想不是想出来的,是敲出来的!欢迎加群交流技术问题!
热度:2,174
cocos2dx针对游戏设计的不同方面会有不同的方案,可以对声音,对内存,对图片格式,对色彩等等进行优化。有关这些方面的方法请大家查找其他的文章。我今天要说的是如何对精灵进行优化,程序中我们用到的最多的就是精灵,大到背景、UI,小到 NPC、道具,只要是用图片展示的,都是精灵或它的子类。精灵是什么,在我看来精灵就是一张纹理图片,是按某种方式显示出来的图片。精灵如此的重要,我们当然要好好的优化优化了。我们可以减小精灵图片的大小,使用缓存Cache的方法将精灵提前加载到内存中,当有很多精灵的时候,使用批次渲染的方法。所以我就从缓存和批次渲染这俩个方面来说一下如何优化我们的精灵图片。
1、通过批次渲染的方法来优化精灵。在游戏中的某一时刻,有时候会用到大量的精灵,比如说发射子弹,粒子效果,这些精灵图片所使用的纹理都是相同的,如果一张一张的图片进行渲染势必会降低效率,大家在开发中看到的窗口的左下角的三行数字中,第一行数字就是渲染批次,这个渲染批次在我看来就是画了多少次,cocos2dx中使用opengl进行绘图,渲染的批次越少当然越好了,所以这个渲染批次才会显示在左下角让我们参考。我们应当尽量的减小这个渲染的批次,提高游戏的效率。方法就是使用CCSpriteBatchNode和CCParticleBatchNode精灵批节点类和粒子批节点类。先来看看在代码中如何使用它们。
CCSize visibleSize = CCDirector::sharedDirector()-&getVisibleSize();
//创建了十个精灵将这十个精灵添加到当前的层中
for(int i=0;i&10;i++)
CCSprite * sprite = CCSprite::create(&icon.png&);
sprite-&setPosition(ccp(CCRANDOM_0_1()*visibleSize.width,CCRANDOM_0_1()*visibleSize.height));
this-&addChild(sprite);
上边的这种情况是采用一般的方式将精灵添加到层中的,这个时候的渲染批次是10。
//创建一个CCSpriteBatchNode,传入的参数就是精灵们将要用到的图片
CCSpriteBatchNode * batchNode = CCSpriteBatchNode::create(&icon.png&);
//或者使用texture2d初始化,里边传入一个texture
//CCSpriteBatchNode * batch = CCSpriteBatchNode::createWithTexture();
//这一句写不写都可以,因为node的默认坐标就是(0,0)
batchNode-&setPosition(CCPointZero);
for(int i=0;i&10;i++)
//创建的这些精灵所使用的纹理必须和CCSpriteBatchNode相同,而且所有这些精灵必须在同一个渲染层
CCSprite * sprite = CCSprite::createWithTexture(batchNode-&getTexture());
sprite-&setPosition(ccp(CCRANDOM_0_1()*visibleSize.width,CCRANDOM_0_1()*visibleSize.height));
batchNode-&addChild(sprite);
this-&addChild(batchNode);
采用上边的方法添加精灵到层中,渲染批次是1,就是因为我们用到了CCSpriteBatchNode这个类,从道理上来说,这个类也是一个node,通常我们使用node的时候就是为了方便的管理精灵,创建一个node节点,然后将精灵放到这个节点中,而node本身是没法显示出来的,显示出来的东西是放到它里边的node子节点,node的长和宽都是0,坐标默认是在父节点的左下角也就是原点处。所以对子节点位置的设置不会产生影响。CCSpriteBatchNode就是这样的一个node,不同的是创建的时候需要一个纹理,要不怎么叫sprite node呢?里边传入的纹理图片是子节点用到的纹理图片,我们可以设置好这些子精灵节点的坐标,然后添加到这个node中,这个node再添加到其他的层中,这样就可以批次渲染了。这个node要求它的字精灵节点和它使用相同的纹理,那如果纹理不一样怎么办,那就把纹理都打包到一张图片上,用的时候从这张图片上截取,工具可以使用texturepacker。
CCTexture2D * texture = CCTextureCache::sharedTextureCache()-&addImage(&fire.png&);
for(int i=0;i&10;i++)
//CCParticleSun里边没有参数
CCParticleSystem * particle = CCParticleSun::create();
particle-&setTexture(texture);
particle-&setPosition(ccp(CCRANDOM_0_1()*visibleSize.width,CCRANDOM_0_1()*visibleSize.height));
this-&addChild(particle);
CCTexture2D * texture = CCTextureCache::sharedTextureCache()-&addImage(&fire.png&);
//参数同样需要一张纹理图片
CCParticleBatchNode * particleBatch = CCParticleBatchNode::createWithTexture(texture);
//也可以采用如下的方式创建
//CCParticleBatchNode * particleBatch = CCParticleBatchNode::create(&fire.png&);
for(int i=0;i&10;i++)
//CCParticleSun里边没有参数
CCParticleSystem * particle = CCParticleSun::create();
//传入的texture必须和CCParticleBatchNode相同
particle-&setTexture(texture);
particle-&setPosition(ccp(CCRANDOM_0_1()*visibleSize.width,CCRANDOM_0_1()*visibleSize.height));
particleBatch-&addChild(particle);
this-&addChild(particleBatch);
这里的道理是和CCSpriteBatchNode相同的,就不用解释了吧。
2、使用缓存提前加载精灵。当我们使用纹理的时候可以制作一个loading界面,将将要用到的纹理加载到缓存中,同时将它们的引用计数增加一,以便这些纹理不会被释放。用的时候直接从缓存中取就可以了,这样也可以提高效率。我们用到的缓存类有CCTextureCache、CCSpriteFrameCache、CCAnimationCache,下面分别说明其用法。
bool HelloWorld::init()
//////////////////////////////
// 1. super init first
if ( !CCLayer::init() )
CCSize visibleSize = CCDirector::sharedDirector()-&getVisibleSize();
//缓存其实就是一个数组,把用到的纹理图片放到这个数组中,纹理的引用计数加1,这样的话就不会释放纹理图片了
//等下一次使用的时候直接从这个数组中取就可以了,这样的话就不必再次加载到内存中了
CCTexture2D * texture = CCTextureCache::sharedTextureCache()-&addImage(&icon.png&);
CCLog(&%d&,texture-&retainCount()); //count=1
//使用如下俩种方法可以获得缓存中的纹理,第二种方法如果之前已经加载了纹理,这个时候不会重新加载
//而是直接返回
texture = CCTextureCache::sharedTextureCache()-&addImage(&icon.png&);
texture = CCTextureCache::sharedTextureCache()-&textureForKey(&icon.png&);
CCLog(&%d&,texture-&retainCount()); //count=1
//异步加载图片,开辟一个新的线程专门用来加载图片,加载完毕以后调用loadingCallBack函数
//所以在这个init函数中是不应该去调用函数textureForKey的,因为你不知道是否加载好了纹理啊
CCTextureCache::sharedTextureCache()-&addImageAsync(&icon1.png&,
this,callfuncO_selector(HelloWorld::loadingCallBack));
//object就是加载好了的纹理
void HelloWorld::loadingCallBack(CCObject * object)
CCTexture2D * texture = (CCTexture2D *)
CCLog(&%d&,texture-&retainCount()); //count=2
//通过以下的方法取得的texture和上边的那个texture相同
//CCTexture2D * texture2 = CCTextureCache::sharedTextureCache()-&textureForKey(&icon1.png&);
//CCLog(&%d&,texture2-&retainCount());
//会清除掉所有的纹理,在其他的地方不可以再引用这些纹理了
CCTextureCache::sharedTextureCache()-&removeAllTextures();
//以下的方法会remove掉count值为1的纹理,icon.png被remove掉了,个人认为实际用的时候就用这个
//CCTextureCache::sharedTextureCache()-&removeUnusedTextures();
CCLog(&%d&,texture-&retainCount());
//查看纹理清除的信息
CCTextureCache::sharedTextureCache()-&dumpCachedTextureInfo();
//切换场景
CCDirector::sharedDirector()-&replaceScene(TestScene::scene());
在新的场景中使用加载好的纹理。
bool TestScene::init()
//因为将所有的纹理清除掉了,所以这里引用的时候会出错
CCTexture2D * texture = CCTextureCache::sharedTextureCache()-&textureForKey(&icon1.png&);
CCLog(&%d&,texture-&retainCount());
CCTextureCache在我看来就是一个数组,将加载的纹理都放到这个数组中,然后将它们的引用计数加1以防释放掉,我们要使用加载好的纹理直接从这个数组中取就可以了,返回一个CCTexture2D的对象,然后我们使用CCSprite:createWithTexture这个方法来创建出精灵。我觉的这里最主要的问题就是纹理的释放,个人认为使用removeUnusedTexture这个函数比较好,它会释放掉引用计数为1的纹理,也就说明程序中没有再为它增加引用计数,肯定是没用了。那些大于1的纹理不会释放掉,即使我们在不知道的情况下再次加载,也只是返回已经加载好了的。接下来是另外俩个缓存了,和这个意思差不多,直接看代码吧。
bool HelloWorld::init()
//////////////////////////////
// 1. super init first
if ( !CCLayer::init() )
CCSize visibleSize = CCDirector::sharedDirector()-&getVisibleSize();
//可以使用texturepacker生成.plist文件,将plist文件和png文件放到资源目录下,通过add方法将ghosts.plist纹理
//加载到了缓存中
CCSpriteFrameCache::sharedSpriteFrameCache()-&addSpriteFramesWithFile(&ghosts.plist&);
//spriteFrameByName中传入的参数可以到.plist文件中查看,通过这个方法可以从缓存中获得一个精灵帧
CCSpriteFrame * spriteFrame = CCSpriteFrameCache::sharedSpriteFrameCache()-&spriteFrameByName(&child1.gif&);
CCLog(&%d&,spriteFrame-&retainCount()); //count=1
//之所以创建精灵帧就是为了播放动画做准备的,这里可以是一个循环不到的创建精灵帧然后添加到animation中
//关于这部分的内容可以查看我前边的文章,这里是为了说明缓存的使用
CCAnimation * animation = CCAnimation::create();
animation-&addSpriteFrame(spriteFrame);
CCLog(&%d&,spriteFrame-&retainCount()); //count=2
CCLog(&%d&,animation-&retainCount()); //count=1,这个1是创建时候的引用计数
//动画缓存,将animation放到缓存中,里边存入一个key,方便以后取出,如果不把动画放到这个缓存中,下一帧的时候
//动画就会被释放掉的,再播放动画就会出错了
CCAnimationCache::sharedAnimationCache()-&addAnimation(animation,&a&); //count=2
CCLog(&%d&,animation-&retainCount());
CCAnimate * animate = CCAnimate::create(CCAnimationCache::sharedAnimationCache()-&animationByName(&a&));
//当不再需要播放动画的时候从动画缓存中清除,应该先清除动画再清除精灵帧
CCAnimationCache::sharedAnimationCache()-&removeAnimationByName(&a&);
//将引用计数为1的精灵帧从缓存中清除
CCSpriteFrameCache::sharedSpriteFrameCache()-&removeUnusedSpriteFrames();
//释放掉所有的精灵帧
//CCSpriteFrameCache::sharedSpriteFrameCache()-&removeSpriteFrames();
小广告-翻墙小插件
文章关键字
小塔网站建设-域名和空间
Powered bycocos2dx实例开发之flappybird(入门版)
cocos2dx社区里有个系列博客完整地复制原版flappybird的所有特性,不过那个代码写得比较复杂,新手学习起来有点捉摸不透,这里我写了个简单的版本。演示如下:
vcyotcS/yda00NDOxLz+oaMK1+7W1bXEz+7Ev7mks8y94bm5yOfPwqO6CjxpbWcgc3JjPQ=="/uploadfile/Collfiles/2.png" alt="\">
很简单,只有三个类,预加载类,游戏主场景类,应用代理类,新手刚入门喜欢将很多东西都写在尽量少的类里面。
游戏结构如下,游戏包含预加载场景和主场景,主场景中包含背景、小鸟、管道和各种UI界面。
1,素材收集
从apk文件里提取出来一些图片和音频,并用TexturePatcher拼成大图,导出plist文件。
2,预加载场景
新建一个LoadingScene,在里面添加一张启动图片,通过异步加载纹理并回调的方式把所有图片素材、小鸟帧动画以及音频文件都加入到缓存,加载完毕后跳转到游戏主场景。
//添加加载回调函数,用异步加载纹理
Director::getInstance()->getTextureCache()->addImageAsync("game.png", CC_CALLBACK_1(LoadingScene::loadingCallBack, this));void LoadingScene::loadingCallBack(Texture2D *texture)
//预加载帧缓存纹理
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("game.plist", texture);
//预加载帧动画
auto birdAnimation = Animation::create();
birdAnimation->setDelayPerUnit(0.2f);
birdAnimation->addSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("bird1.png"));
birdAnimation->addSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("bird2.png"));
birdAnimation->addSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("bird3.png"));
AnimationCache::getInstance()->addAnimation(birdAnimation, "birdAnimation"); //将小鸟动画添加到动画缓存
//预加载音效
SimpleAudioEngine::getInstance()->preloadEffect("die.mp3");
SimpleAudioEngine::getInstance()->preloadEffect("hit.mp3");
SimpleAudioEngine::getInstance()->preloadEffect("point.mp3");
SimpleAudioEngine::getInstance()->preloadEffect("swooshing.mp3");
SimpleAudioEngine::getInstance()->preloadEffect("wing.mp3");
//加载完毕跳转到游戏场景
auto gameScene = GameScene::createScene();
TransitionScene *transition = TransitionFade::create(0.5f, gameScene);
Director::getInstance()->replaceScene(transition);
3,游戏主场景
3.1,背景和logo
用图片精灵即可
//添加游戏背景
Sprite *backGround = Sprite::createWithSpriteFrameName("bg.png");
backGround->setPosition(visibleOrigin.x + visibleSize.width / 2, visibleOrigin.y + visibleSize.height / 2);
this->addChild(backGround);
auto gameLogo = Sprite::createWithSpriteFrameName("bird_logo.png");
gameLogo->setPosition(visibleOrigin.x + visibleSize.width / 2, visibleOrigin.y + visibleSize.height / 2+100);
gameLogo->setName("logo");
this->addChild(gameLogo);logo在游戏开始后要隐藏掉。
birdSprite = Sprite::create();
birdSprite->setPosition(visibleOrigin.x + visibleSize.width / 3, visibleOrigin.y + visibleSize.height / 2);
this->addChild(birdSprite);
auto birdAnim = Animate::create(AnimationCache::getInstance()->animationByName("birdAnimation"));
birdSprite->runAction(RepeatForever::create(birdAnim));
//挥翅动画
auto up = MoveBy::create(0.4f, Point(0, 8));
auto upBack = up->reverse();
if (gameStatus == GAME_READY)
swingAction = RepeatForever::create(Sequence::create(up, upBack, NULL));
birdSprite->runAction(swingAction); //上下晃动动画
}在准备界面下除了有扇翅膀的动作,还有上下浮动的动作。
地板的左移是用两张错位的地板图片循环左移实现的。需要用到自定义调度器,注意调节移动速度。
//添加两个land
land1 = Sprite::createWithSpriteFrameName("land.png");
land1->setAnchorPoint(Point::ZERO);
land1->setPosition(Point::ZERO);
this->addChild(land1, 10);
//置于最顶层
land2 = Sprite::createWithSpriteFrameName("land.png");
land2->setAnchorPoint(Point::ZERO);
land2->setPosition(Point::ZERO);
this->addChild(land2, 10);
Size visibleSize = Director::getInstance()->getVisibleSize();
//两个图片循环移动
land1->setPositionX(land1->getPositionX() - 1.0f);
land2->setPositionX(land1->getPositionX() + land1->getContentSize().width - 2.0f);
if (land2->getPositionX() setPosition(Point::ZERO);3.4,水管
一组水管由上下2半根组成,用Node包起来,弄个vector容器添加两组管道,每次出现在屏幕中的管子只有两组,当一组消失在屏幕范围内则重设置其横坐标,需要提前计算好各种间距或者高度。
//同屏幕出现的只有两根管子,放到容器里面,上下绑定为一根
for (int i = 0; i getVisibleSize();
Sprite *pipeUp = Sprite::createWithSpriteFrameName("pipe_up.png");
Sprite *pipeDown = Sprite::createWithSpriteFrameName("pipe_down.png");
Node *singlePipe = Node::create();
//给上管绑定刚体
auto pipeUpBody = PhysicsBody::createBox(pipeUp->getContentSize());
pipeUpBody->setDynamic(false);
pipeUpBody->setContactTestBitmask(1);
pipeUp->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
pipeUp->setPhysicsBody(pipeUpBody);
//给两个管子分开设置刚体,可以留出中间的空隙使得小鸟通过
//给下管绑定刚体
auto pipeDownBody = PhysicsBody::createBox(pipeDown->getContentSize());
pipeDownBody->setDynamic(false);
pipeDownBody->setContactTestBitmask(1);
pipeDown->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
pipeDown->setPhysicsBody(pipeDownBody);
pipeUp->setPosition(0, PIPE_HEIGHT + PIPE_SPACE);
singlePipe->addChild(pipeUp);
singlePipe->addChild(pipeDown);
//pipeDown默认加到(0,0),上下合并,此时singlePipe以下面的管子中心为锚点
singlePipe->setPosition(i*PIPE_INTERVAL + WAIT_DISTANCE, getRandomHeight() ); //设置初始高度
singlePipe->setName("newPipe");
this->addChild(singlePipe);
//把两个管子都加入到层
pipes.pushBack(singlePipe);
//两个管子先后添加到容器
//管子滚动
for (auto &singlePipe : pipes)
singlePipe->setPositionX(singlePipe->getPositionX() - 1.0f);
if (singlePipe->getPositionX() setPositionX(visibleSize.width+PIPE_WIDTH/2);
singlePipe->setPositionY(getRandomHeight());
singlePipe->setName("newPipe");
//每次重设一根管子,标为new
}3.5,加入物理世界
cocos2dx 3.0后引入了自带的物理引擎,用法和box2D等差不多。
物理世界初始化
gameScene->getPhysicsWorld()->setGravity(Vec2(0, -900)); //设置重力场,重力加速度可以根据手感改小点gameLayer->setPhysicWorld(gameScene->getPhysicsWorld()); //绑定物理世界小鸟绑定刚体
//小鸟绑定刚体
auto birdBody = PhysicsBody::createCircle(BIRD_RADIUS); //将小鸟当成一个圆,懒得弄精确的轮廓线了
birdBody->setDynamic(true);
//设置为可以被物理场所作用而动作
birdBody->setContactTestBitmask(1); //必须设置这项为1才能检测到不同的物体碰撞
birdBody->setGravityEnable(false);
//设置是否被重力影响,准备画面中不受重力影响
birdSprite->setPhysicsBody(birdBody); //为小鸟设置刚体地板绑定刚体
//设置地板刚体
Node *groundNode = Node::create();
auto groundBody = PhysicsBody::createBox(Size(visibleSize.width, land1->getContentSize().height));
groundBody->setDynamic(false);
groundBody->setContactTestBitmask(1);
groundNode->setAnchorPoint(Vec2::ANCHOR_MIDDLE); //物理引擎中的刚体只允许结点锚点设置为中心
groundNode->setPhysicsBody(groundBody);
groundNode->setPosition(visibleOrigin.x+visibleSize.width/2,land1->getContentSize().height/2);
this->addChild(groundNode);管道设置刚体,上下半根分别设置,留出中间的缝隙
//给上管绑定刚体
auto pipeUpBody = PhysicsBody::createBox(pipeUp->getContentSize());
pipeUpBody->setDynamic(false);
pipeUpBody->setContactTestBitmask(1);
pipeUp->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
pipeUp->setPhysicsBody(pipeUpBody);
//给两个管子分开设置刚体,可以留出中间的空隙使得小鸟通过
//给下管绑定刚体
auto pipeDownBody = PhysicsBody::createBox(pipeDown->getContentSize());
pipeDownBody->setDynamic(false);
pipeDownBody->setContactTestBitmask(1);
pipeDown->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
pipeDown->setPhysicsBody(pipeDownBody);碰撞检测
现在层的init里面的事件分发器中加入碰撞侦听
//添加碰撞监测
auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = CC_CALLBACK_1(GameScene::onContactBegin, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);//碰撞监测
bool GameScene::onContactBegin(const PhysicsContact& contact)
if (gameStatus == GAME_OVER)
//当游戏结束后不再监控碰撞
gameOver();
}3.6,触摸检测
//触摸监听
bool GameScene::onTouchBegan(Touch *touch, Event *event)3.7,控制小鸟
由准备模式变到游戏开始模式后,触摸屏幕会给小鸟一个向上的速度,写在触摸检测里面
birdSprite->getPhysicsBody()->setVelocity(Vec2(0, 250)); //给一个向上的初速度
小鸟的旋转角度与纵向速度有关,写在update()里
//小鸟的旋转
auto curVelocity = birdSprite->getPhysicsBody()->getVelocity();
birdSprite->setRotation(-curVelocity.y*0.1 - 20);
//根据竖直方向的速度算出旋转角度,逆时针为负
3.8,游戏开始
开始后启动各种定时器
//游戏开始
void GameScene::gameStart()
gameStatus = GAME_START;
score = 0;//重置分数
scoreLabel->setString(String::createWithFormat("%d", score)->getCString());
this->getChildByName("logo")->setVisible(false); //logo消失
scoreLabel->setVisible(true); //计分开始
this->scheduleUpdate();//启动默认更新
this->schedule(schedule_selector(GameScene::scrollLand), 0.01f); //启动管子和地板滚动
birdSprite->stopAction(swingAction); //游戏开始后停止上下浮动
birdSprite->getPhysicsBody()->setGravityEnable(true); //开始受重力作用
}3.9,计分和数据存储
在默认的update()函数里对得分进行判断和更新,通过默认xml存储历史分数
//当游戏开始时,判断得分,这个其实也可以写在其他地方,比如管子滚动的更新函数里面或者触摸监测里面
if (gameStatus == GAME_START)
for (auto &pipe : pipes)
if (pipe->getName() == "newPipe") //新来一根管子就判断
if (pipe->getPositionX() getPositionX())
scoreLabel->setString(String::createWithFormat("%d", score)->getCString());
SimpleAudioEngine::getInstance()->playEffect("point.mp3");
pipe->setName("passed"); //标记已经过掉的管子
}4.0,游戏结束
//游戏结束
void GameScene::gameOver()
gameStatus = GAME_OVER;
//获取历史数据
bestScore = UserDefault::getInstance()->getIntegerForKey("BEST");
if (score > bestScore)
bestScore =
//更新最好分数
UserDefault::getInstance()->setIntegerForKey("BEST", bestScore);
SimpleAudioEngine::getInstance()->playEffect("hit.mp3");
//游戏结束后停止地板和管道的滚动
this->unschedule(schedule_selector(GameScene::scrollLand));
}结束后比较当前分数和历史分数,以便更新。
音效文件已经加入到缓存,在适当的地方加上全局音频控制器播放音效即可
SimpleAudioEngine::getInstance()->playEffect("hit.mp3");4.2,记分板
游戏结束后滑入记分板,并显示重玩按钮。
//加入记分板和重玩菜单
void GameScene::gamePanelAppear()
Size size = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
//用node将gameoverlogo和记分板绑在一起
Node *gameOverPanelNode = Node::create();
auto gameOverLabel = Sprite::createWithSpriteFrameName("gameover.png");
gameOverPanelNode->addChild(gameOverLabel);
auto panel = Sprite::createWithSpriteFrameName("board.PNG");//注意这里是大写PNG,原图片用什么后缀这里就用什么,区分大小写
gameOverLabel->setPositionY(panel->getContentSize().height); //设置一下坐标
gameOverPanelNode->addChild(panel);
//记分板上添加两个分数
auto curScoreTTF = LabelTTF::create(String::createWithFormat("%d", score)->getCString(), "Arial", 20);
curScoreTTF->setPosition(panel->getContentSize().width-40, panel->getContentSize().height-45);
curScoreTTF->setColor(Color3B(255, 0, 0));
panel->addChild(curScoreTTF);
auto bestScoreTTF = LabelTTF::create(String::createWithFormat("%d", bestScore)->getCString(), "Arial", 20);
bestScoreTTF->setPosition(panel->getContentSize().width - 40, panel->getContentSize().height - 90);
bestScoreTTF->setColor(Color3B(0, 255, 0));
panel->addChild(bestScoreTTF);
this->addChild(gameOverPanelNode);
gameOverPanelNode->setPosition(origin.x + size.width / 2, origin.y + size.height );
//滑入动画
gameOverPanelNode->runAction(MoveTo::create(0.5f, Vec2(origin.x + size.width / 2, origin.y + size.height / 2)));
SimpleAudioEngine::getInstance()->playEffect("swooshing.mp3");
//添加菜单
MenuItemImage *restartItem = MenuItemImage::create("start_btn.png", "start_btn_pressed.png", this,menu_selector(GameScene::gameRetart));
auto menu = CCMenu::createWithItem(restartItem);
menu->setPosition(origin.x + size.width / 2, 150);
this->addChild(menu);
//游戏重新开始
void GameScene::gameRetart(Ref *sender)
//重新回到初始画面
auto gameScene = GameScene::createScene();
Director::getInstance()->replaceScene(gameScene); //这里懒得加特效了,直接转场
源代码:MyFlappyBird
还有很多要完善的地方,比如没有加入图片数字以及社交分享等等。
您对本文章有什么意见或着疑问吗?请到您的关注和建议是我们前行的参考和动力&&
您的浏览器不支持嵌入式框架,或者当前配置为不显示嵌入式框架。

我要回帖

更多关于 1733176 的文章

 

随机推荐