引言
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 动画;
  • 等等。

了解更多可查看官网信息: http://www.cocos2d-x.org/reference/native-cpp/V3.0alpha0/db/d61/classcocos2d11_action.html

其中以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,而沿着左右两边的曲线运动呢?曲线!首先想到的应该是贝塞尔曲线吧,先来了解一下贝塞尔曲线。

贝赛尔曲线的每一个顶点都有两个控制点,用于控制在该顶点两侧的曲线的弧度。它是应用于二维图形应用程序的数学曲线。曲线的定义有四个点:起始点、终止点(也称锚点)以及两个相互分离的中间点。滑动两个中间点,贝塞尔曲线的形状会发生变化。

在引擎中是这样子定义一个贝塞尔曲线的配置信息的:

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;

描述结构体中三个点分别是:曲线的目的结束点,控制点1和控制点2, Bezier的起始点则是它当前的位置坐标。贝塞尔曲线的两个控制点的位置将决定曲线的形状,不清楚的可以在Photoshop等有贝塞尔曲线的软件中去调。以圆角矩形左边的曲线为例,我调了一条曲线,如下图所示:

从图中可以看到,只要Point1与起始点连成的直线和Point2与结束点连成的直线平行 && 两个控制点都在以起点和终点为连线的同一个面 && 两个控制点的连线与起始点终点的连线平行时,就可以确定一条相对标准的曲线,可以近似圆角矩形的圆角部分。左右移动Point1和Point2的位置可调节这条曲线的形状,如图:

理清思路以后,上代码“画”上图的贝塞尔曲线:

   ccBezierConfig bezier;
bezier.controlPoint_1 = Point(-controlX, );   //控制点1
bezier.controlPoint_2 = Point(-controlX, controlY); //控制点2
bezier.endPosition = Point(, controlY);   //目的地(终点)
auto bezierBy = BezierBy::create(1.0f, bezier);     //创建动作
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(重复执行动作)了。下面就是抽象出来的方法,返回值为一个能绕圆角矩形运动的循环动作:

RepeatForever* HelloWorld::MyPathFun(float controlX, float controlY, float w)
{
ccBezierConfig bezier1;
bezier1.controlPoint_1 = Point(-controlX, );
bezier1.controlPoint_2 = Point(-controlX, controlY);
bezier1.endPosition = Point(, controlY);
auto bezierBy1 = BezierBy::create(0.8f, bezier1); auto move1 = MoveBy::create(0.8f, Point(w, )); ccBezierConfig bezier2;
bezier2.controlPoint_1 = Point(controlX, );
bezier2.controlPoint_2 = Point(controlX, -controlY);
bezier2.endPosition = Point(, -controlY);
auto bezierBy2 = BezierBy::create(0.8f, bezier2);
auto move2 = MoveBy::create(0.8f, Point(-w, ));
auto path = RepeatForever::create(Sequence::create(bezierBy1, move1, bezierBy2, move2, NULL));
return path;
}

函数MyPathFun()的参数分别是控制点1的X分量、控制点2的Y分量(圆角矩形的高)、直线移动的x分量,如上图。因为参数controlX和w都不是确定的值,所以传参时要特别注意,要根据按钮的形状来调节这两个参数。

接下来只需要让节点runAction这个动作就可以了,下面利用粒子系统做一条尾巴。关于粒子特效的介绍大家可以参考泰然网的一篇文章,地址: http://www.tairan.com/archives/3460

ParticleSystem* HelloWorld::particleInit()
{
auto _emitter = new ParticleSystemQuad();
_emitter->initWithTotalParticles();
addChild(_emitter, );
_emitter->setTexture(Director::getInstance()->getTextureCache()->addImage("point.png"));
_emitter->setAnchorPoint(Point(, ));
// duration
_emitter->setDuration(ParticleSystem::DURATION_INFINITY); // radius mode
_emitter->setEmitterMode(ParticleSystem::Mode::RADIUS); // radius mode: start and end radius in pixels
_emitter->setStartRadius();
_emitter->setStartRadiusVar();
_emitter->setEndRadius(ParticleSystem::START_RADIUS_EQUAL_TO_END_RADIUS);
_emitter->setEndRadiusVar(); // radius mode: degrees per second
_emitter->setRotatePerSecond();
_emitter->setRotatePerSecondVar(); // angle
_emitter->setAngle();
_emitter->setAngleVar(); // emitter position
auto size = Director::getInstance()->getWinSize();
_emitter->setPosVar(Point::ZERO); // life of particles
_emitter->setLife(0.5);
_emitter->setLifeVar(); // spin of particles
_emitter->setStartSpin();
_emitter->setStartSpinVar();
_emitter->setEndSpin();
_emitter->setEndSpinVar(); // color of particles
Color4F startColor(0.0f, 0.8f, 0.9f, 1.0f);
_emitter->setStartColor(startColor); Color4F startColorVar(, , , 1.0f);
_emitter->setStartColorVar(startColorVar); Color4F endColor(1.0f, 1.0f, 1.0f, 0.1f);
_emitter->setEndColor(endColor); Color4F endColorVar(, , , 0.1f);
_emitter->setEndColorVar(endColorVar);
Color4F setStartColor(Color4F(Color4B(, , , )));
Color4F setEndColor(Color4F(Color4B(, , , )));
// size, in pixels
_emitter->setStartSize();
_emitter->setStartSizeVar();
_emitter->setEndSize(); // emits per second
_emitter->setEmissionRate(_emitter->getTotalParticles() / _emitter->getLife()); // additive
_emitter->setBlendAdditive(false); return _emitter;
}

这里也可以利用粒子特效制作工具来编辑好看的粒子,然后通过plist文件加载粒子。
添加按钮btnSprite(这里按钮的描点要设为(0,0)),星星starSprite,尾巴_emitter后,设置星星和尾巴的位置与动作:

starSprite->setPosition(Point(btnSprite->getPosition().x + btnSprite->getContentSize().height /  - , btnSprite->getPosition().y));
//设置星星的坐标
_emitter->setPosition(Point(btnSprite->getPosition().x + btnSprite->getContentSize().height / - , btnSprite->getPosition().y + ));
//设置尾巴粒子发射器的坐标 float X = btnSprite->getContentSize().height / ;
auto path = MyPathFun(X+, btnSprite->getContentSize().height, btnSprite->getContentSize().width - X * );
//根据按钮的形状调节按钮的动作路径 starSprite->runAction(path);
_emitter->runAction(path->clone());

这里把星星和尾巴的起点坐标设为上图的originPoint点,因为描点的原因,星星starSprite和尾巴_emitter的位置要做一定的微调。

该方法实用于任意圆角矩形,同时适用于矩形,只要把controlX设置为零,W设置为矩形宽就都实现,即:

MyPathFun(0, btnSprite2->getContentSize().height, btnSprite2->getContentSize().width );

工程下载地址:https://github.com/renshan/btnTest/tree/master/Test-btn

使用Cocos2d-x实现微信“天天爱消除”炫耀button特效的更多相关文章

  1. 【Cocos2d-x for WP8 学习整理】(4)CCTableView 实现《天天爱消除》中的得分榜

    接上回 CCScrollView 继续,在GUI 里还有个 CCScrollView 的子类---CCTableView . 这个名字应该是从 IOS 里的 UITableView来的,其实是跟WP8 ...

  2. Android 天天爱消除辅助

    简介 <天天爱消除>是一款移植于手游的消除类益智游戏,该游戏只有通过手机登录QQ跟微信才能进行,这样一来这款游戏必然会大红大紫. 功能 开发Android自动化触屏事件,录制操作脚本,实现 ...

  3. 微信小程序的button按钮设置宽度无效

    亲,你是不是也遇到了微信小程序的button按钮设置宽度无效.让我来告诉你怎么弄 方法1. 样式中加入!important,即:width: 100% !important; wxss代码示例 1 2 ...

  4. MFC+Android模拟器 实现 自动玩“天天爱消除”

    朋友用QT做了个自动玩的,觉得有意思,自己也想用MFC做个试试. 模拟器用的BlueStacks.Android SDK带的那个模拟器不知道是不是设置的问题,开游戏很卡. 用MFC建了对话框工程,配置 ...

  5. 【小程序】微信小程序实现各种特效实例

    写在前面 最近在负责一个微信小程序的前端以及前后端接口的对接的项目,整体上所有页面的布局我都已经搭建完成,里面有一些常用的特效,总结一下,希望对大家和我都能有所帮助 实例1:滚动tab选项卡 先看一下 ...

  6. 微信小程序实现各种特效实例

    写在前面 最近在负责一个微信小程序的前端以及前后端接口的对接的项目,整体上所有页面的布局我都已经搭建完成,里面有一些常用的特效,总结一下,希望对大家和我都能有所帮助 实例1:滚动tab选项卡 先看一下 ...

  7. 微信小程序去除button按钮的边框

    小程序开发记录 小程序开发中, 有时候我们希望button不要有边框, 需要使用button::after来实现, 具体如下: .operations button::after{border:0 n ...

  8. 微信小程序去除Button默认样式

    在小程序开发过程中,使用率蛮高的组件button,因为经常要去除默认样式,然后再自定义样式,所以经常写,自己也总结分享一下简单的实现步骤. (一)实现效果1.实现前(默认样式): 2.实现后(去除默认 ...

  9. 微信小程序组件button

    表单组件button:官方文档 Demo Code: var types=['default', 'primary', 'warn']; var pageObject = { data: { defa ...

随机推荐

  1. android px转换为dip/dp

    /** * 根据手机的分辨率从 dp 的单位 转成为 px(像素) */ public int dipTopx(Context context, float dpValue) { final floa ...

  2. 2、实现不同子网之间的信息交流(互相可以PING通)

    一.环境: 二个不同的虚拟子网 VMnet1: 192.168.155.0/24 VMnet8: 192.168.170.0/24 编辑 --> 虚拟网络编辑器 (查看自己的子网,相应修改就行) ...

  3. 项目自动化建构工具gradle 入门4——javaWeb在浏览器中显示helloWorld

    在java应用中,其实做的最多的还是java web应用.所以现在我们做的就是用gradle构建一个简单的web项目,简单点,直接上代码吧. 1.进入目录D:\work\gradle\web,新建文件 ...

  4. 利用联合双边滤波或引导滤波进行升采样(Upsampling)技术提高一些耗时算法的速度。

    这十年来,在图像处理领域提出了很多新的图像分析和处理方法,包括是自动的以及一些需要有人工参与的,典型的比如stereo depth computations.image colorization.to ...

  5. 前端之JavaScript基础

    前端之JavaScript基础 本节内容 JS概述 JS基础语法 JS循环控制 ECMA对象 BOM对象 DOM对象 1. JS概述 1.1. javascript历史 1992年Nombas开发出C ...

  6. Compiler Error Message: CS0016: Could not write to output file 回绝访问

    Compiler Error Message: CS0016: Could not write to output file 'c:\Windows...dll' 拒绝访问 C:\Windows\Te ...

  7. [LeetCode] Consecutive Numbers 连续的数字

    Write a SQL query to find all numbers that appear at least three times consecutively. +----+-----+ | ...

  8. [LeetCode] Flatten Binary Tree to Linked List 将二叉树展开成链表

    Given a binary tree, flatten it to a linked list in-place. For example,Given 1 / \ 2 5 / \ \ 3 4 6 T ...

  9. 如何在Microsoft Edge浏览器中添加一个Hello World插件

    注:本文提到的代码示例下载地址> How to add a Hello World extension to Microsoft Edge Microsoft Edge 随着Win 10一起推出 ...

  10. functools模块

    from functools import wraps def logged(func):     @wraps(func)     def with_logging(*args, **kwargs) ...