使用Cocos2d-x实现微信“天天爱消除”炫耀button特效


http://www.cocoachina.com/gamedev/gameengine/2014/0110/7688.html

引言

Cocos2d-x引擎中有很多Action,这样可以方便的让开发者调用相应的Action去完成一些动作,例如:移动,弹跳,淡入淡出等。可在实际的开发过程中,由于游戏的需要,显然地,引擎自带的Action是完全不够用的,很多时候都需要我们自己去写。今天我就来分享一个让精灵和粒子绕着圆角矩形运动的实例,细讲一下实现过程(仅凭个人想法实现,如有其他更好的方法,欢迎交流分享)。
 
效果
该实例类似天天爱消除等系列游戏中的按钮特效:
没图片资源,将就看吧。
 
实现方法
刚开始本来考虑自己定义一条圆角矩形的路径,即建一个自己的动作模块,让精灵和粒子按照自己的定义的动作来运动来着,就像MoveTo/MoveBy、BezierTo/BezierBy等Action一样。但是后来发现好难啊,因为根本没有函数可以来定义圆角矩形(至少我不知道),所以一番纠结后,果断放弃了这种设想。
 
另辟蹊径,不是说cocos2d-x引擎提供很多的Action吗,那我们就来“拼”一个圆角矩形的Action啊!要“拼”这个Action,我觉得很有必要了解清楚Cocos2d-x中的各个Action,引擎中常用的Action有:
Sequence 按顺序执行一系列的动作;
Repeat 重复执行一个动作n次;
RepeatForever 永远重复执行某动作;
RotateTo / RotateBy 旋转;
MoveTo / MoveBy 移动;
JumpTo / JumpBy 跳跃;
BezierTo / BezierBy 贝塞尔曲线;
ScaleTo / ScaleBy 缩放;
FadeIn / FadeOut / FadeTo 淡入淡出;
TintTo / CCTintBy 染色;
Animate 动画;
等等。
 
 
其中以To结尾的动作和以By结尾的动作的区别在于:
To是设置Node(引擎中所以物体的基类)到指定坐标位置,而By则是设置Node到相对的坐标位置。例如:一个Sprite的坐标是(100,200),XXTo(1.0f, ccp(100, 100))是在1秒内运动到点(100, 100)处,无论节点之前的坐标在哪,它最终的位置坐标都是点(100,100);XXBy(1.0f, ccp(100, 100))则是表示1秒内向x轴方向运动100个单位,向y轴方向运动100个单位,它是以自身节点为参照位置,运动了(100,100)个单位。
 
理清Action后回归到正题,根据圆角矩形的特征,这里我们可以把圆角矩形分成四段:左右两半弧和上下两直线,如下图:
沿直线的运动的2、4段我们可以用MoveTo、MoveBy,而沿着左右两边的曲线运动呢?曲线!首先想到的应该是贝塞尔曲线吧,先来了解一下贝塞尔曲线。
 
贝赛尔曲线的每一个顶点都有两个控制点,用于控制在该顶点两侧的曲线的弧度。它是应用于二维图形应用程序的数学曲线。曲线的定义有四个点:起始点、终止点(也称锚点)以及两个相互分离的中间点。滑动两个中间点,贝塞尔曲线的形状会发生变化。
 
在引擎中是这样子定义一个贝塞尔曲线的配置信息的:
 
 
  1. typedef struct _ccBezierConfig { 
  2.     //! end position of the bezier 
  3.     Point endPosition; 
  4.   
  5.     //! Bezier control point 1 
  6.     Point controlPoint_1; 
  7.   
  8.     //! Bezier control point 2 
  9.     Point controlPoint_2; 
  10.   
  11. } ccBezierConfig; 
 
描述结构体中三个点分别是:曲线的目的结束点,控制点1和控制点2, Bezier的起始点则是它当前的位置坐标。贝塞尔曲线的两个控制点的位置将决定曲线的形状,不清楚的可以在Photoshop等有贝塞尔曲线的软件中去调。以圆角矩形左边的曲线为例,我调了一条曲线,如下图所示:
从图中可以看到,只要Point1与起始点连成的直线和Point2与结束点连成的直线平行 && 两个控制点都在以起点和终点为连线的同一个面 && 两个控制点的连线与起始点终点的连线平行时,就可以确定一条相对标准的曲线,可以近似圆角矩形的圆角部分。左右移动Point1和Point2的位置可调节这条曲线的形状,如图:
理清思路以后,上代码“画”上图的贝塞尔曲线:
 
 
  1. ccBezierConfig bezier; 
  2.     bezier.controlPoint_1 = Point(-controlX, 0);        //控制点1 
  3.     bezier.controlPoint_2 = Point(-controlX, controlY);     //控制点2 
  4.     bezier.endPosition = Point(0, controlY);            //目的地(终点) 
  5.     auto bezierBy = BezierBy::create(1.0f, bezier);//创建动作 
  6.     node->runAction(bezierBy);                  //让节点按bezierBy路径运动 
 
注意,使用BezierBy时,节点始终把自己的运动初始位置看做(0, 0),与它的实际场景位置无关,ccBezierConfig中的点都是相对坐标点,如果使用BezierTo,ccBezierConfig的点就都是绝对坐标点,曲线的起始点也将被认作为实际场景位置坐标,而非(0, 0),因此也将不能达到上图的效果。
 
右边的贝塞尔曲线类似,直线的运动同理用MoveBy,而不用MoveTo。移动的代码很简单,如: auto move = MoveBy::create(1.0f, Point(w, 0)); //向右移动W个单位。
 
这样圆角矩形的四部分都能用相应的Action来实现了,接下来该完成的就是把它们按顺序的串连在一起并重复的执行下去。那就要用到Sequenc(按顺序执行一系列的动作) 和RepeatForever(重复执行动作)了。下面就是抽象出来的方法,返回值为一个能绕圆角矩形运动的循环动作:
 
 
  1. RepeatForever* HelloWorld::MyPathFun(float controlX, float controlY, float w) 
  2.     ccBezierConfig bezier1; 
  3.     bezier1.controlPoint_1 = Point(-controlX, 0); 
  4.     bezier1.controlPoint_2 = Point(-controlX, controlY); 
  5.     bezier1.endPosition = Point(0, controlY); 
  6.     auto bezierBy1 = BezierBy::create(0.8f, bezier1); 
  7.   
  8.     auto move1 = MoveBy::create(0.8f, Point(w, 0)); 
  9.   
  10.     ccBezierConfig bezier2; 
  11.     bezier2.controlPoint_1 = Point(controlX, 0); 
  12.     bezier2.controlPoint_2 = Point(controlX, -controlY); 
  13.     bezier2.endPosition = Point(0, -controlY); 
  14.     auto bezierBy2 = BezierBy::create(0.8f, bezier2); 
  15.     auto move2 = MoveBy::create(0.8f, Point(-w, 0)); 
  16.     auto path = RepeatForever::create(Sequence::create(bezierBy1, move1, bezierBy2, move2, NULL)); 
  17.     return path; 
 
函数MyPathFun()的参数分别是控制点1的X分量、控制点2的Y分量(圆角矩形的高)、直线移动的x分量,如上图。因为参数controlX和w都不是确定的值,所以传参时要特别注意,要根据按钮的形状来调节这两个参数。
 
接下来只需要让节点runAction这个动作就可以了,下面利用粒子系统做一条尾巴。关于粒子特效的介绍大家可以参考泰然网的一篇文章,地址: http://www.ityran.com/archives/3460
 
 
  1. ParticleSystem* HelloWorld::particleInit() 
  2.     auto _emitter = new ParticleSystemQuad(); 
  3.     _emitter->initWithTotalParticles(50); 
  4.     addChild(_emitter, 10); 
  5.     _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage("point.png")); 
  6.     _emitter->setAnchorPoint(Point(0, 0)); 
  7.     // duration 
  8.     _emitter->setDuration(ParticleSystem::DURATION_INFINITY); 
  9.   
  10.     // radius mode 
  11.     _emitter->setEmitterMode(ParticleSystem::Mode::RADIUS); 
  12.   
  13.     // radius mode: start and end radius in pixels 
  14.     _emitter->setStartRadius(4); 
  15.     _emitter->setStartRadiusVar(1); 
  16.     _emitter->setEndRadius(ParticleSystem::START_RADIUS_EQUAL_TO_END_RADIUS); 
  17.     _emitter->setEndRadiusVar(0); 
  18.   
  19.     // radius mode: degrees per second 
  20.     _emitter->setRotatePerSecond(100); 
  21.     _emitter->setRotatePerSecondVar(0); 
  22.   
  23.     // angle 
  24.     _emitter->setAngle(90); 
  25.     _emitter->setAngleVar(0); 
  26.   
  27.     // emitter position 
  28.     auto size = Director::getInstance()->getWinSize(); 
  29.     _emitter->setPosVar(Point::ZERO); 
  30.   
  31.     // life of particles 
  32.     _emitter->setLife(0.5); 
  33.     _emitter->setLifeVar(0); 
  34.   
  35.     // spin of particles 
  36.     _emitter->setStartSpin(0); 
  37.     _emitter->setStartSpinVar(0); 
  38.     _emitter->setEndSpin(0); 
  39.     _emitter->setEndSpinVar(0); 
  40.   
  41.     // color of particles 
  42.     Color4F startColor(0.0f, 0.8f, 0.9f, 1.0f); 
  43.     _emitter->setStartColor(startColor); 
  44.   
  45.     Color4F startColorVar(0, 0, 0, 1.0f); 
  46.     _emitter->setStartColorVar(startColorVar); 
  47.   
  48.     Color4F endColor(1.0f, 1.0f, 1.0f, 0.1f); 
  49.     _emitter->setEndColor(endColor); 
  50.   
  51.     Color4F endColorVar(0, 0, 0, 0.1f); 
  52.     _emitter->setEndColorVar(endColorVar); 
  53.     Color4F setStartColor(Color4F(Color4B(50, 50, 50, 50))); 
  54.     Color4F setEndColor(Color4F(Color4B(0, 0, 0, 0))); 
  55.     // size, in pixels 
  56.     _emitter->setStartSize(20); 
  57.     _emitter->setStartSizeVar(1); 
  58.     _emitter->setEndSize(0); 
  59.   
  60.     // emits per second 
  61.     _emitter->setEmissionRate(_emitter->getTotalParticles() / _emitter->getLife()); 
  62.   
  63.     // additive 
  64.     _emitter->setBlendAdditive(false); 
  65.   
  66.     return _emitter; 
这里也可以利用粒子特效制作工具来编辑好看的粒子,然后通过plist文件加载粒子。
 
添加按钮btnSprite(这里按钮的描点要设为(0,0)),星星starSprite,尾巴_emitter后,设置星星和尾巴的位置与动作:
 
 
  1. starSprite->setPosition(Point(btnSprite->getPosition().x + btnSprite->getContentSize().height / 2 - 4, btnSprite->getPosition().y));//设置星星的坐标 
  2.     _emitter->setPosition(Point(btnSprite->getPosition().x + btnSprite->getContentSize().height / 2 - 4, btnSprite->getPosition().y + 3));//设置尾巴粒子发射器的坐标 
  3.   
  4.     float X = btnSprite->getContentSize().height / 2; 
  5.     auto path = MyPathFun(X+10, btnSprite->getContentSize().height, btnSprite->getContentSize().width - X * 2 ); //根据按钮的形状调节按钮的动作路径 
  6.   
  7.     starSprite->runAction(path); 
  8.     _emitter->runAction(path->clone()); 
 
这里把星星和尾巴的起点坐标设为上图的originPoint点,因为描点的原因,星星starSprite和尾巴_emitter的位置要做一定的微调。
 
该方法实用于任意圆角矩形,同时适用于矩形,只要把controlX设置为零,W设置为矩形宽就都实现,即:MyPathFun(0, btnSprite2->getContentSize().height, btnSprite2->getContentSize().width );
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值