现在特性99游戏的功能基本完成了,但是游戏没有首页也挺奇怪,所以这篇博客为游戏添加首页,通过首页进入游戏。
首页的元素比较简单,就是一张背景图,一个logo,一只煽着翅膀的飞猪,还有一个开始游戏按钮。另外为了效果更好些,添加了背景雪花飘落的粒子效果。也给按钮添加了粒子特效:往上浮动的雪花和绕着按钮运动的精灵粒子特效。粒子编辑器使用的是ParticleEditor,下载地址:https://code.google.com/p/cocos2d-windows-particle-editor/downloads/detail?name=ParticleEditor%20V2.0.7z&can=2&q=
编辑器效果图:

ParticleEditor是windows下的一款很好用的粒子编辑器,可以直接导出plist文件。在Samples中有一些现成的例子可以参考。下面简单介绍下编辑器右边的功能:
半径模式:可以使粒子以圆圈方式旋转
-EndRadius 结束半径
-EndRadiusVar 结束半径变化范围,即结束半径值的范围在 (EndRadius – EndRadiusVar) 和 (EndRadius + EndRadiusVar )之间
-RotatePerSecond 粒子每秒围绕起始点的旋转角度
-RotatePerSecondVar 粒子每秒围绕起始点的旋转角度变化范围
-StartRadius 初始半径
-StartRadiusVar 初始半径变化范围
编辑器
-IsBackgroundMove 背景是否动
-Scale 缩放
大小
EndSize:粒子结束大小
StartSize:粒子开始大小
角度
Angle:粒子运动方向(-90表示垂直向下)
生命
Life:粒子生命,粒子生存时间
位置
-PositionType 粒子位置类型,有自由模式(Free)、相对模式(Relative)和打组模式(Grouped)三种
-PosVarX 发射器位置的横向变化范围
-PosVarY 发射器位置的纵向变化范围
下面这两个一般不用管,默认值就好。
-SourcePositionX 发射器原始X坐标位置
-SourcePositionY 发射器原始Y坐标位置
纹理渲染
-DestBlendFunc 目的纹理的混合模式
-SrcBlendFunc 源纹理的混合模式
-TextureImageData 纹理数据
-TexturePath 纹理路径
颜色
-EndColor 粒子结束颜色
-StartColor 粒子初始颜色
-StartColorVar 粒子初始颜色浮动范围
-EndColorVar 粒子结束颜色浮动范围
重力模式
-GravityX 重力X
-GravityY 重力Y
-RadiaAccel 粒子径向加速度,即平行于重力方向的加速度
-Speed 速度
-TangentialAccel 粒子切向加速度,即垂直于重力方向的加速度
主要
-Duration 发射器生存时间,这个时间和粒子生存时间不同,单位秒,-1表示永远。
-EmissionRate 每秒喷发的粒子数目
-IsAutoRemoveOnFinish 粒子结束时是否自动删除
-Mode 喷发器模式,有重力模式(Gravity)和半径模式(Radius,也叫放射模式)两种
-TotalParticles 场景中存在的最大粒子数目
自旋
-EndSpin 粒子结束自旋角度
-StartSpin 粒子开始自旋角度

下面开始写代码吧,创建一个WelcomeScene类:

class WelcomeScene : public cocos2d::Scene
{
public:
    static cocos2d::Scene* scene();
};

class WelcomeLayer : public cocos2d::Layer
{
public:
    static cocos2d::Scene* createScene();

    virtual bool init(); 

    CREATE_FUNC(WelcomeLayer);

    cocos2d::RepeatForever* buildParticleMovePath(float controlX, float controlY, float w, bool isClockwise);
};

buildParticleMovePath函数的作用:创建一个Action,使粒子沿着button绕圈运动。
首先来实现WelcomeLayer ::init函数:

bool WelcomeLayer::init()
{
    if ( !Layer::init() )
    {
        return false;
    }

    Size visibleSize = Director::getInstance()->getVisibleSize();
    Point origin = Director::getInstance()->getVisibleOrigin();

    auto bgSprite = Sprite::create("index_bg.jpg");
    bgSprite->setPosition(Point(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
    this->addChild(bgSprite, 0);

 auto titleSprite= Sprite::create("LOGO.png");
 titleSprite->setPosition(Point(origin.x + visibleSize.width / 2, origin.y + visibleSize.height - titleSprite->getContentSize().height / 2 - 160));
 this->addChild(titleSprite);

 SpriteFrameCache::getInstance()->addSpriteFramesWithFile("pig.plist");

 auto planeSprite = Plane::create();
 planeSprite->setScale(2.0f);
 planeSprite->setPosition(Point(origin.x + visibleSize.width / 2, origin.y + titleSprite->getPositionY() - titleSprite->getContentSize().height - planeSprite->getContentSize().height / 2 - 100));
 this->addChild(planeSprite);

 //C++11之lambda表达式
 auto startBtnItem = MenuItemImage::create("", "", [](Object *sender) {
    Scene *scene = GameScene::scene();
    Director::getInstance()->replaceScene(CCTransitionCrossFade::create(1.2f,scene));
  });

 auto btnSprite = Sprite::create("btn_yellow.png");
 startBtnItem->setNormalSpriteFrame(btnSprite->getDisplayFrame());
 startBtnItem->setPosition(Point(origin.x + visibleSize.width / 2, origin.y + startBtnItem->getContentSize().height / 2 + 100));
 auto startMenu = Menu::create(startBtnItem, NULL);
 startMenu->setPosition(Point::ZERO);
 this->addChild(startMenu);

 auto startBtnText = Sprite::create("start_game_text.png");
 startBtnText->setPosition(startBtnItem->getPosition());
 this->addChild(startBtnText);

 //背景雪花粒子效果
 auto particleEmitter = ParticleSystemQuad::create("snow.plist");
 particleEmitter->setPosition(Point(origin.x + visibleSize.width / 2, origin.y + visibleSize.height + 5));
 auto particleBatch = ParticleBatchNode::createWithTexture(particleEmitter->getTexture());
 particleBatch->addChild(particleEmitter);
 this->addChild(particleBatch, 5);

 //开始游戏按钮上的雪花粒子效果
 auto btnParticleEmitter = ParticleSystemQuad::create("btn_snow.plist");
 btnParticleEmitter->setPosition(Point(startBtnItem->getPositionX(), startBtnItem->getPositionY() - startBtnItem->getContentSize().height / 2 + 20));
 auto btnParticleBatch = ParticleBatchNode::createWithTexture(btnParticleEmitter->getTexture());
 btnParticleBatch->addChild(btnParticleEmitter);
 this->addChild(btnParticleBatch, 5);

 //沿着"开始游戏按钮"游动的粒子1
 auto starSprite1 = Sprite::create("star.png");
 starSprite1->setScale(0.4f);
 starSprite1->setPosition(Point(startBtnItem->getPosition().x - startBtnItem->getContentSize().width / 2, startBtnItem->getPosition().y - startBtnItem->getContentSize().height / 2));	
 this->addChild(starSprite1, 10);

 auto emitter1 = ParticleSystemQuad::create("btn_move.plist");
 emitter1->setPosition(Point(startBtnItem->getPosition().x - startBtnItem->getContentSize().width / 2 - 2, startBtnItem->getPosition().y - startBtnItem->getContentSize().height / 2 + 3));
 auto emitterBatch1 = ParticleBatchNode::createWithTexture(emitter1->getTexture());
 emitterBatch1->addChild(emitter1);
 this->addChild(emitterBatch1, 5);

 float X = 2;
 auto path1 = buildParticleMovePath(X, startBtnItem->getContentSize().height, startBtnItem->getContentSize().width - 2 * X, true);
 starSprite1->runAction(path1);
 emitter1->runAction(path1->clone());

 //沿着"开始游戏按钮"游动的粒子2
 auto starSprite2 = Sprite::create("star.png");
 starSprite2->setScale(0.4f);
 starSprite2->setPosition(Point(startBtnItem->getPosition().x + startBtnItem->getContentSize().width / 2, startBtnItem->getPosition().y + startBtnItem->getContentSize().height / 2));	
 this->addChild(starSprite2, 10);

 auto emitter2 = ParticleSystemQuad::create("btn_move.plist");
 emitter2->setPosition(Point(startBtnItem->getPosition().x + startBtnItem->getContentSize().width / 2 - 2, startBtnItem->getPosition().y + startBtnItem->getContentSize().height / 2 + 3));
 auto emitterBatch2 = ParticleBatchNode::createWithTexture(emitter2->getTexture());
 emitterBatch2->addChild(emitter2);
 this->addChild(emitterBatch2, 5);

 auto path2 = buildParticleMovePath(X, startBtnItem->getContentSize().height, startBtnItem->getContentSize().width - 2 * X, false);
 starSprite2->runAction(path2);
 emitter2->runAction(path2->clone());

    return true;
}

其他效果比较简单,重点来学习一下怎么实现一上一下两个精灵带着粒子尾巴绕着按钮运动,有点类似于天天飞车中的开始游戏按钮上的效果。其实精灵和粒子尾巴运动的轨迹是一样的,都是执行了buildParticleMovePath函数返回的Action。精灵1从下图originPoint位置开始运动,精灵2从originPoint对角线上的(矩形右上角)对应的那个点开始运动。
这个按钮是一个圆角矩形按钮,请看下图:


上图中的1和2是上下两个精灵,两者沿着相反的方向绕着按钮运动。把上图圆角矩形可以分成四段,两边的圆弧+上下直线。如果精灵从上图originPoint位置出发,可以把精灵绕按钮一圈分解成四个Action:BezierBy + MoveBy +BezierBy + MoveBy。这里的实现方式参考了http://www.ityran.com/archives/5460这篇文章。在这里来温习一下cocos2d-x中以To结尾的动作和以By结尾的动作的区别:To是设置Sprite 到指定坐标位置,而By则是设置Sprite 到相对的坐标位置。如:一个Sprite的坐标是(0, 0),XXTo(1.0f, Point(100, 100))是在1秒内运动到点(100, 100)处,XXBy(1.0f, Point (100, 100))则是表示1秒内向x轴方向运动100个单位,向y轴方向运动100个单位。直线运动比较简单,先来了解一下贝塞尔曲线吧,先来看看cocos2d-x中贝塞尔曲线结构的定义:

typedef struct _ccBezierConfig {
    //! end position of the bezier
    Point endPosition;
    //! Bezier control point 1
    Point controlPoint_1;
    //! Bezier control point 2
    Point controlPoint_2;
} ccBezierConfig;

包括:终止点和两个控制点,加上起始点就可以得到该曲线的四个点,通过调整两个控制点(对应上图中的point1和point2),就可以控制贝塞尔曲线的形状。
接下来实现buildParticleMovePath函数:

RepeatForever* WelcomeLayer::buildParticleMovePath(float controlX, float controlY, float w, bool isClockwise)
{
 int flag = isClockwise ? -1 : 1;

 ccBezierConfig bezier1;
 bezier1.controlPoint_1 = Point(flag * controlX, 0);
 bezier1.controlPoint_2 = Point(flag * controlX, (-flag) * controlY);
 bezier1.endPosition = Point(0, (-flag) * controlY);

 auto bezierBy1 = BezierBy::create(0.8f, bezier1);

 float t_w = w;
 if(!isClockwise) {
  t_w = -w;
 }
 auto move1 = MoveBy::create(1.0f, Point(t_w, 0));

 ccBezierConfig bezier2;
 bezier2.controlPoint_1 = Point((-flag) * controlX, 0);
 bezier2.controlPoint_2 = Point((-flag) * controlX, flag * controlY);
 bezier2.endPosition = Point(0, flag * controlY);

 auto bezierBy2 = BezierBy::create(0.8f, bezier2);
 auto move2 = MoveBy::create(1.0f, Point(-t_w, 0));
 auto path = RepeatForever::create(Sequence::create(bezierBy1, move1, bezierBy2, move2, NULL));
 return path;
}

controlX和controlY、w分别表示上图中的controlX和controlY、w,isClockwise表示是否顺时针运动,用来区分两个精灵的运动轨迹。

到这里就完成啦,运行项目,看看效果吧。

 

cocos2d-x手游性能优化总结

近段时间在使用cocos2d-x开发2D手游,技术方案使用的是cocos2d-x+lua,因为游戏使用的是cocos2d-x 2.1.5版本,有些优化方案在最新版的cocos2d-x版本已经实现...

阅读全文

cocos2dx-html5 实现网页版flappy bird游戏

我也是第一次使用cocos2d_html5,对js和html5也不熟,看引擎自带的例子和引擎源码,边学边做,如果使用过cocos2d-x的话,完成这个游戏还是十分简单的。游戏体...

阅读全文

【cocos2d-x开发实战 特训99-终结篇】移植到android平台和添加admob广告

上一篇已经完成特性99在win32平台下的开发,现在把它移植到android上,首先修改Android.mk文件,内容如下: LOCAL_PATH := $(call my-dir) include $(CLEAR_...

阅读全文

1 条评论

欢迎留言