cocos2dx之旋转的button

****************************************************************************

时间:2015-04-06

作者:Sharing_Li

转载注明出处:http://blog.csdn.net/sharing_li/article/details/44903971

****************************************************************************

 

       一般游戏的主界面按钮的摆放,都是中心垂直对齐,如果弄得稍微炫一点,就是下面的这种效果,也就是本篇要讲解的内容:

 

先分析一下功能需求:

1、一共四个按钮,只有点击了最前面的按钮,按钮的响应事件才能触发,点击了其他按钮,则旋转到最前面。

2、点击了除最前面的按钮外的动画效果,和左右滑动时的动画效果。(大小,透明度,运动轨迹)

3、左右滑动到途中时松手时的动画调整。

4、按钮Z序的调整

5、滑动区域的限制

 

接下来看看代码怎么写:

定义一个类BtnTurn,来看头文件

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #ifndef __BTN_TURN_H__  
  2. #define __BTN_TURN_H__  
  3.   
  4. #include "cocos2d.h"  
  5.   
  6. USING_NS_CC;  
  7.   
  8. enum BtnPos  
  9. {  
  10.     Pos_Bottom = 1,  
  11.     Pos_Left,  
  12.     Pos_Top,  
  13.     Pos_Right,  
  14. };  
  15.   
  16.   
  17. class BtnTurn : public cocos2d::Layer  
  18. {  
  19. public:  
  20.     BtnTurn();  
  21.     ~BtnTurn();  
  22.   
  23.     virtual bool init();  
  24.     CREATE_FUNC(BtnTurn)  
  25.   
  26. protected:  
  27.     virtual bool onTouchBegan(Touch* touch, Event* pEvent);  
  28.     virtual void onTouchMoved(Touch *pTouch, Event *pEvent);  
  29.     virtual void onTouchEnded(Touch *pTouch, Event *pEvent);  
  30.   
  31.     //点击按钮之后的动画  
  32.     void runTouchedAmt(Sprite * btn);  
  33.     //滑动界面的动画  
  34.     void runSlidedAmt(bool isLeft,float ratio,float judgePosX);  
  35. private:  
  36.     Sprite * m_btn1;  
  37.     Sprite * m_btn2;  
  38.     Sprite * m_btn3;  
  39.     Sprite * m_btn4;  
  40.   
  41.     Vec2 m_posBottom;  
  42.     Vec2 m_posLeft;  
  43.     Vec2 m_posTop;  
  44.     Vec2 m_posRight;  
  45.   
  46.     Point m_firstPos;  
  47.       
  48.     Size m_winSize;  
  49.     bool m_valid;//先点击有效区域  
  50.     bool m_invalid;//先点击无效区域  
  51.       
  52. };  
  53.   
  54. #endif  


这里最主要的核心代码就是沿椭圆轨迹旋转效果,之前有篇文章讲解了沿椭圆运动的动画,可以像用系统的MoveTo等使用runAction,参考地址:

http://blog.csdn.net/sharing_li/article/details/43268877

本篇将不采用链接中说的方法来实现。

首先,我们定义一些全局数据:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. const float RUNTIME = 0.3; //动画运行时间  
  2. float A;//椭圆长半径  
  3. float Bd;//下椭圆短半径  
  4. float Bu;//上椭圆短半径  
  5. float Cx;//椭圆中心X坐标  
  6. float Cy;//椭圆中心Y坐标  

再来看看我们的初始化函数:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. m_winSize = Director::getInstance()->getWinSize();  
  2.     m_posBottom = Vec2(0,0);  
  3.     m_posLeft = Vec2(-m_winSize.width * 0.24,m_winSize.height * 0.15);  
  4.     m_posTop = Vec2(0,m_winSize.height * 0.24);  
  5.     m_posRight = Vec2(m_winSize.width * 0.24,m_winSize.height * 0.15);  
  6.     A = m_posBottom.x - m_posLeft.x;  
  7.     Bu = m_posTop.y - m_posLeft.y;  
  8.     Bd = m_posLeft.y - m_posBottom.y;  
  9.     Cx = m_posBottom.x;  
  10.     Cy = m_posLeft.y;  
  11.   
  12.     Texture2D * pTt2d = Director::getInstance()->getTextureCache()->addImage("BtnTurn/btn.png");  
  13.     m_btn1 = Sprite::createWithTexture(pTt2d);  
  14.     m_btn1->setPosition(m_posBottom);  
  15.     m_btn1->setTag(Pos_Bottom);  
  16.     this->addChild(m_btn1,4);  
  17.   
  18.     m_btn2 = Sprite::createWithTexture(pTt2d);  
  19.     m_btn2->setPosition(m_posLeft);  
  20.     m_btn2->setScale(0.75);  
  21.     m_btn2->setOpacity(100);  
  22.     m_btn2->setTag(Pos_Left);  
  23.     this->addChild(m_btn2,3);  
  24.   
  25.     m_btn3 = Sprite::createWithTexture(pTt2d);  
  26.     m_btn3->setPosition(m_posTop);  
  27.     m_btn3->setScale(0.5);  
  28.     m_btn3->setOpacity(50);  
  29.     m_btn3->setTag(Pos_Top);  
  30.     this->addChild(m_btn3,2);  
  31.   
  32.     m_btn4 = Sprite::createWithTexture(pTt2d);  
  33.     m_btn4->setPosition(m_posRight);  
  34.     m_btn4->setScale(0.75);  
  35.     m_btn4->setOpacity(100);  
  36.     m_btn4->setTag(Pos_Right);  
  37.     this->addChild(m_btn4,3);  
  38.   
  39.     auto listenerT = EventListenerTouchOneByOne::create();  
  40.     listenerT->onTouchBegan = CC_CALLBACK_2(BtnTurn::onTouchBegan,this);  
  41.     listenerT->onTouchMoved = CC_CALLBACK_2(BtnTurn::onTouchMoved,this);  
  42.     listenerT->onTouchEnded = CC_CALLBACK_2(BtnTurn::onTouchEnded,this);  
  43.     listenerT->setSwallowTouches(false);  
  44.     Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listenerT,this);  
  45.   
  46.     return true;  


       这里,初始化全局变量,和四个按钮的初始位置。可以发现这里的按钮并不是按钮,而是sprite,因为只有显示在最前面的按钮才能响应函数,所以定义成sprite方便处理。我们通过判断点击开始和点击结束,这两个点是否是同一个点来确定是否点击了按钮,然后根据按钮的Zorder来判断是否响应函数。我们还给每个按钮设置了tag值,这个tag值并不是不变的,因为按钮的位置会改变,所以tag的值也会改变,以确保我们通过getChildByTag函数,能够正确获取到相应tag值的按钮,比如getChildByTag(Pos_Top),函数返回的一定是处于最上面位置的那个按钮。

       我们来看看onTouchBegan函数:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. bool BtnTurn::onTouchBegan(Touch* touch, Event* pEvent)  
  2. {  
  3.     m_firstPos = touch->getLocation();  
  4.   
  5.     return true;  
  6. }  

很简单,就两行代码,获取点击开始的按钮。然后看看onTouchMoved

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void BtnTurn::onTouchMoved(Touch *pTouch, Event *pEvent)  
  2. {  
  3.     auto movePos = pTouch->getLocation();  
  4.     auto judgePos = this->convertToNodeSpace(movePos);  
  5.     auto box = Rect(-m_winSize.width * 0.5,-m_winSize.height * 0.1,m_winSize.width,m_winSize.height * 0.4);  
  6.     //优化,不能全屏都可以滑,并判断是先点击有效还是无效区域  
  7.     if (!box.containsPoint(judgePos))  
  8.     {  
  9.         if (!m_valid)  
  10.         {  
  11.             m_invalid = true;  
  12.         }  
  13.         return ;  
  14.     }  
  15.     if (!m_invalid)  
  16.     {  
  17.         m_valid = true;  
  18.     }  
  19.     else  
  20.     {  
  21.         return ;  
  22.     }  
  23.     //根据滑动方向来运动  
  24.     auto ratio = fabsf(movePos.x - m_firstPos.x) * 2 / m_winSize.width;  
  25.     if (ratio >= 1)  
  26.     {  
  27.         return ;  
  28.     }  
  29.     this->runSlidedAmt(movePos.x - m_firstPos.x < 0,ratio,fabsf(m_firstPos.x - movePos.x));  
  30. }  

上面代码中box是可以滑动的有效区域,m_valid和m_invalid是用来判断开始触摸屏幕,是点击了有效区域,还是无效区域。

然后根据滑动的方向,来调用动画实现函数runSlidedAmt:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void BtnTurn::runSlidedAmt(bool isLeft,float ratio,float judgePosX)  
  2. {  
  3.     auto btnTop = this->getChildByTag(Pos_Top);  
  4.     auto btnLeft = this->getChildByTag(Pos_Left);  
  5.     auto btnRight = this->getChildByTag(Pos_Right);  
  6.     auto btnBottom = this->getChildByTag(Pos_Bottom);  
  7.   
  8.     auto deltPosDown = m_posRight - m_posBottom;  
  9.     auto deltPosUp = m_posTop - m_posLeft;  
  10.   
  11.     //判断是否需要调换Z顺序  
  12.     if (judgePosX > m_winSize.width / 4)  
  13.     {  
  14.         btnTop->setZOrder(3);  
  15.         btnLeft->setZOrder(isLeft ? 2 : 4);  
  16.         btnRight->setZOrder(isLeft ? 4 : 2);  
  17.         btnBottom->setZOrder(3);  
  18.     }  
  19.       
  20.     auto B1 = isLeft ? Bu : Bd;//判断左边的button沿哪个椭圆运动  
  21.     auto B2 = isLeft ? Bd : Bu;//判断右边的button沿哪个椭圆运动  
  22.       
  23.     int temp = isLeft ? (m_posBottom.x - deltPosDown.x * ratio) : (m_posBottom.x + deltPosDown.x * ratio);  
  24.     btnBottom->setPosition(Vec2(temp,sin(-acos((temp - Cx)/A)) * Bd + Cy));  
  25.     btnBottom->setScale(1 - 0.25 * ratio);  
  26.     btnBottom->setOpacity(255 - 155 * ratio);  
  27.   
  28.     temp = isLeft ? (m_posLeft.y + deltPosUp.y * ratio) : (m_posLeft.y - deltPosDown.y * ratio);  
  29.     btnLeft->setPosition(Vec2(-cos(asin((temp - Cy)/B1)) * A + Cx,temp));  
  30.     btnLeft->setScale(0.75 - (isLeft ? 0.25 * ratio : -0.25 * ratio));  
  31.     btnLeft->setOpacity(100 - (isLeft ? 50 * ratio : -155 * ratio));  
  32.   
  33.     temp = m_posTop.x + (isLeft ? (deltPosUp.x * ratio) : (-1 * deltPosUp.x * ratio));  
  34.     btnTop->setPosition(Vec2(temp,sin(acos((temp - Cx)/A)) * Bu + Cy));  
  35.     btnTop->setScale(0.5 + 0.25 * ratio);  
  36.     btnTop->setOpacity(50 + 50 * ratio);  
  37.   
  38.     temp = m_posRight.y + (isLeft ? (-1 * deltPosDown.y * ratio) : (deltPosUp.y * ratio));  
  39.     btnRight->setPosition(Vec2(cos(asin((temp - Cy)/B2)) * A + Cx,temp));  
  40.     btnRight->setScale(0.75 + 0.25 * (isLeft ? ratio : -ratio));  
  41.     btnRight->setOpacity(100 + (isLeft ? 155 * ratio : -50 * ratio));  
  42.       
  43. }  


这里,我们通过椭圆的非标准方程,根据已知的x坐标或者y坐标,求出对应的y坐标或x坐标。假如椭圆的圆心坐标为(Cx,Cy),那么根据方程:

x = A * cosβ + Cx;y = B * sinβ + Cy;

      然后根据数学函数库的反三角函数等,就可以求出相应的值了。这里需要注意的是三角函数和反三角函数的值域。
      接着,我们来看看onTouchEnded函数:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void BtnTurn::onTouchEnded(Touch *pTouch, Event *pEvent)  
  2. {  
  3.     if (m_invalid)  
  4.     {  
  5.         m_invalid = false;  
  6.         return;  
  7.     }  
  8.     auto endPos = pTouch->getLocation();  
  9.     auto delX = endPos.x - m_firstPos.x;  
  10.     auto delY = endPos.y - m_firstPos.y;  
  11.     //如果是点击操作  
  12.     if (fabsf(delX) < 0.0001 && fabsf(delY) < 0.0001)  
  13.     {  
  14.         endPos = this->convertToNodeSpace(endPos);  
  15.         auto box1 = m_btn1->getBoundingBox();  
  16.         auto box2 = m_btn2->getBoundingBox();  
  17.         auto box3 = m_btn3->getBoundingBox();  
  18.         auto box4 = m_btn4->getBoundingBox();  
  19.         if (box1.containsPoint(endPos))  
  20.         {  
  21.             if (m_btn1->getZOrder() == 4)  
  22.             {  
  23.                 log("******************Btn1 CallBack***************");  
  24.             }  
  25.             else  
  26.             {  
  27.                   
  28.                 this->runTouchedAmt(m_btn1);  
  29.             }  
  30.         }  
  31.         else if (box2.containsPoint(endPos))  
  32.         {  
  33.             if (m_btn2->getZOrder() == 4)  
  34.             {  
  35.                 log("******************Btn2 CallBack***************");  
  36.             }  
  37.             else  
  38.             {  
  39.                 this->runTouchedAmt(m_btn2);  
  40.             }  
  41.         }  
  42.         else if (box3.containsPoint(endPos))  
  43.         {  
  44.             if (m_btn3->getZOrder() == 4)  
  45.             {  
  46.                 log("******************Btn3 CallBack***************");  
  47.             }  
  48.             else  
  49.             {  
  50.                 this->runTouchedAmt(m_btn3);  
  51.             }  
  52.         }  
  53.         else if (box4.containsPoint(endPos))  
  54.         {  
  55.             if (m_btn4->getZOrder() == 4)  
  56.             {  
  57.                 log("******************Btn4 CallBack***************");  
  58.             }  
  59.             else  
  60.             {  
  61.                 this->runTouchedAmt(m_btn4);  
  62.             }  
  63.         }  
  64.     }  
  65.     else//滑动操作  
  66.     {  
  67.         auto adjustPos = pTouch->getLocation();  
  68.         //判断滑动方向  
  69.         if (adjustPos.x - m_firstPos.x < 0)//向左滑动  
  70.         {  
  71.             auto tmpBtn = (Sprite *)this->getChildByTag(Pos_Right);  
  72.             this->runTouchedAmt(tmpBtn);  
  73.         }  
  74.         else if (adjustPos.x - m_firstPos.x > 0)  
  75.         {  
  76.             auto tmpBtn = (Sprite *)this->getChildByTag(Pos_Left);  
  77.             this->runTouchedAmt(tmpBtn);  
  78.         }  
  79.     }  
  80.     m_valid = false;  
  81. }  


首先判断是否是点击操作,如果是,再来判断点击了哪个button,如果是最前面的button,就响应函数,如果不是则调用动画效果;如果不是点击操作,那就是滑动操作,然后根据滑动方向调用调整动画。我们来看看runTouchedAmt函数:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void BtnTurn::runTouchedAmt(Sprite * btn)  
  2. {  
  3.     auto tag = btn->getTag();  
  4.     switch (tag)  
  5.     {  
  6.     case Pos_Left :  
  7.         {  
  8.             btn->runAction(Spawn::create(ScaleTo::create(RUNTIME,1),  
  9.                                         Sequence::createWithTwoActions(MoveTo::create(RUNTIME / 2,m_posBottom),  
  10.                                                 MoveTo::create(RUNTIME / 2,m_posBottom)),  
  11.                                         FadeIn::create(RUNTIME),NULL));  
  12.             btn->setZOrder(4);  
  13.               
  14.             auto topBtn = (Sprite *)(this->getChildByTag(Pos_Top));  
  15.             topBtn->runAction(Spawn::create(ScaleTo::create(RUNTIME,0.75),  
  16.                                             MoveTo::create(RUNTIME,m_posLeft),  
  17.                                             FadeTo::create(RUNTIME,100),NULL));  
  18.             topBtn->setZOrder(3);  
  19.   
  20.             auto rightBtn = (Sprite *)this->getChildByTag(Pos_Right);  
  21.             rightBtn->runAction(Spawn::create(ScaleTo::create(RUNTIME,0.5),  
  22.                                 MoveTo::create(RUNTIME,m_posTop),  
  23.                                 FadeTo::create(RUNTIME,50),NULL));  
  24.             rightBtn->setZOrder(2);  
  25.   
  26.             auto bottomBtn = (Sprite *)this->getChildByTag(Pos_Bottom);  
  27.             bottomBtn->runAction(Spawn::create(ScaleTo::create(RUNTIME,0.75),  
  28.                                                 MoveTo::create(RUNTIME,m_posRight),  
  29.                                                 FadeTo::create(RUNTIME,100),NULL));  
  30.             bottomBtn->setZOrder(3);  
  31.   
  32.             btn->setTag(Pos_Bottom);  
  33.             topBtn->setTag(Pos_Left);  
  34.             rightBtn->setTag(Pos_Top);  
  35.             bottomBtn->setTag(Pos_Right);  
  36.         }  
  37.         break;  
  38.     case Pos_Top :  
  39.         {  
  40.             btn->runAction(Spawn::create(ScaleTo::create(RUNTIME,1),  
  41.                 Sequence::createWithTwoActions(MoveTo::create(RUNTIME/2,m_posLeft),MoveTo::create(RUNTIME/2,m_posBottom)),  
  42.                 FadeIn::create(0.2),NULL));  
  43.             btn->setZOrder(4);  
  44.   
  45.             auto rightBtn = (Sprite *)this->getChildByTag(Pos_Right);  
  46.             rightBtn->runAction(Spawn::create(Sequence::createWithTwoActions(ScaleTo::create(RUNTIME/2,0.5),ScaleTo::create(RUNTIME/2,0.75)),  
  47.                 Sequence::createWithTwoActions(MoveTo::create(RUNTIME/2,m_posTop),MoveTo::create(RUNTIME/2,m_posLeft)),  
  48.                 Sequence::createWithTwoActions(FadeTo::create(RUNTIME/2,50),FadeTo::create(RUNTIME/2,100)),NULL));  
  49.             rightBtn->setZOrder(3);  
  50.   
  51.             auto bottomBtn = (Sprite *)this->getChildByTag(Pos_Bottom);  
  52.             bottomBtn->runAction(Spawn::create(ScaleTo::create(RUNTIME,0.5),  
  53.                 Sequence::createWithTwoActions(MoveTo::create(RUNTIME/2,m_posRight),MoveTo::create(RUNTIME/2,m_posTop)),  
  54.                 FadeTo::create(RUNTIME,50),NULL));  
  55.             bottomBtn->setZOrder(2);  
  56.   
  57.             auto leftBtn = (Sprite *)this->getChildByTag(Pos_Left);  
  58.             leftBtn->runAction(Spawn::create(Sequence::createWithTwoActions(ScaleTo::create(RUNTIME/2,1),ScaleTo::create(RUNTIME/2,0.75)),  
  59.                 Sequence::createWithTwoActions(MoveTo::create(RUNTIME/2,m_posBottom),MoveTo::create(RUNTIME/2,m_posRight)),  
  60.                 Sequence::createWithTwoActions(FadeIn::create(RUNTIME/2),FadeTo::create(RUNTIME/2,100)),NULL));  
  61.             leftBtn->setZOrder(3);  
  62.   
  63.             btn->setTag(Pos_Bottom);  
  64.             leftBtn->setTag(Pos_Right);  
  65.             rightBtn->setTag(Pos_Left);  
  66.             bottomBtn->setTag(Pos_Top);  
  67.   
  68.         }  
  69.         break;  
  70.     case Pos_Right :  
  71.         {  
  72.             btn->runAction(Spawn::create(ScaleTo::create(RUNTIME,1),  
  73.                 MoveTo::create(RUNTIME,m_posBottom),  
  74.                 FadeIn::create(RUNTIME),NULL));  
  75.             btn->setZOrder(4);  
  76.   
  77.             auto topBtn = (Sprite *)this->getChildByTag(Pos_Top);  
  78.             topBtn->runAction(Spawn::create(ScaleTo::create(RUNTIME,0.75),  
  79.                 MoveTo::create(RUNTIME,m_posRight),  
  80.                 FadeTo::create(RUNTIME,100),NULL));  
  81.             topBtn->setZOrder(3);  
  82.   
  83.             auto leftBtn = (Sprite *)this->getChildByTag(Pos_Left);  
  84.             leftBtn->runAction(Spawn::create(ScaleTo::create(RUNTIME,0.5),  
  85.                 MoveTo::create(RUNTIME,m_posTop),  
  86.                 FadeTo::create(RUNTIME,50),NULL));  
  87.             leftBtn->setZOrder(2);  
  88.   
  89.             auto bottomBtn = (Sprite *)this->getChildByTag(Pos_Bottom);  
  90.             bottomBtn->runAction(Spawn::create(ScaleTo::create(RUNTIME,0.75),  
  91.                 MoveTo::create(RUNTIME,m_posLeft),  
  92.                 FadeTo::create(RUNTIME,100),NULL));  
  93.             bottomBtn->setZOrder(3);  
  94.   
  95.             btn->setTag(Pos_Bottom);  
  96.             topBtn->setTag(Pos_Right);  
  97.             leftBtn->setTag(Pos_Top);  
  98.             bottomBtn->setTag(Pos_Left);  
  99.   
  100.         }  
  101.         break;  
  102.     }  
  103.   
  104. }  

代码好像有点多,其实也就是分别处理点击了除最前面按钮的动画效果。这里简单起见,没有用椭圆效果,用的moveto函数。

到这里讲解完毕,免费下载资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值