加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

cocos2dx之抽奖界面与获奖概率的设计(一)

发布时间:2020-12-14 20:17:05 所属栏目:百科 来源:网络整理
导读:**************************************************************************** 时间: 2015-02-01 作者: Sharing_Li 转载出处:http://www.jb51.cc/article/p-fmhyjmnl-rx.html **********************************************************************

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

时间:2015-02-01

作者:Sharing_Li

转载出处:http://www.52php.cn/article/p-fmhyjmnl-rx.html

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

在不同游戏中,经常有各种各样抽奖的环节,比如每次登入游戏的免费抽奖,卡牌游戏中的抽不同颜色的卡牌英雄,不同品质的武器抽奖,十连抽等等。今天给大家讲解一下,比较传统的抽奖方式,就是转转盘的抽奖,包含抽奖界面动画的设计和抽奖概率的设计。由于内容稍微有点多,所以分两篇进行讲解,本篇先介绍转盘抽奖方式的界面设计。废话不多说,先上效果图:

(。。。亮瞎了我的钛合金眼!)

来看看大致的功能需求有哪些:

1、一个转盘,一个指针,可以是转盘转,也可以是指针转,本篇是转盘转。

2、转盘在转的时候,速度是先快后慢,然后停止。

3、转盘在转的时候,各种粒子效果的动画,其中包括圆环状的闪光星星,还有以椭圆轨迹运动的小彗星。

4、抽中奖品后,弹出抽中奖品的动画。

看完功能需求,再来看看代码怎么写:

先看简单的初始化代码:

[cpp] view plain copy
  1. boolLotteryTurnTest::init()
  2. {
  3. if(!Layer::init())
  4. returnfalse;
  5. }
  6. autobgSize=Director::getInstance()->getWinSize();
  7. m_pBg=Sprite::create("LotteryTurn/bg_big.png");
  8. m_pBg->setPosition(Vec2(bgSize.width/2,bgSize.height/2));
  9. this->addChild(m_pBg);
  10. //添加标题
  11. autoplabel=Label::createWithTTF("LotteryTurnTest","fonts/MarkerFelt.ttf",30);
  12. plabel->setPosition(Vec2(bgSize.width/2,bgSize.height*0.9));
  13. m_pBg->addChild(plabel);
  14. //添加转盘
  15. m_turnBg=Sprite::create("LotteryTurn/turn_bg.png");
  16. m_turnBg->setPosition(Vec2(bgSize.width/2,248); line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> m_pBg->addChild(m_turnBg);
  17. //添加指针
  18. autoarrNor=Sprite::create("LotteryTurn/turn_arrow.png");
  19. autoarrSel=Sprite::create("LotteryTurn/turn_arrow.png");
  20. arrSel->setColor(Color3B(190,190,190));
  21. m_turnArr=MenuItemSprite::create(arrNor,arrSel,CC_CALLBACK_1(LotteryTurnTest::onBtnCallback,this));
  22. m_turnArr->setPosition(Vec2(bgSize.width/2,bgSize.height*0.557));
  23. m_turnArr->setScale(0.7);
  24. autopMenu=Menu::createWithItem(m_turnArr);
  25. pMenu->setPosition(Vec2::ZERO);
  26. m_pBg->addChild(pMenu);
  27. //添加中奖之后的简单界面
  28. autoawardLayer=LayerColor::create(Color4B(0,100));
  29. awardLayer->setPosition(Point::ZERO);
  30. awardLayer->setTag(100);
  31. m_pBg->addChild(awardLayer,10);
  32. awardLayer->setVisible(false);
  33. true;
  34. }

点击按钮,获取一个随机的旋转角度,转盘开始转,注意的是,转盘在转的时候,按钮要被设置成无效状态,以免多次点击。

    //防止多次点击
  1. m_turnArr->setEnabled( srand(unsigned(time(NULL)));
  2. floatangleZ=rand()%720+720;
  3. autopAction=EaseExponentialOut::create(RotateBy::create(4,Vec3(0,angleZ)));
  4. m_turnBg->runAction(Sequence::create(pAction,CallFunc::create(CC_CALLBACK_0(LotteryTurnTest::onTurnEnd,153); background-color:inherit; font-weight:bold">this)),NULL));

这里,我们用的EaseExponentialOut来控制转盘旋转的速度。

当然,转盘在转的时候,各种粒子效果开始行动啦,这里放到文章后面讲解,先看看中奖之后的动画:

    //弹出抽中奖品
  1. ((LayerColor*)m_pBg->getChildByTag(100))->setVisible(true);
  2. autoaward=Sprite::create("LotteryTurn/award.png");
  3. award->setAnchorPoint(Vec2(0.5,0));
  4. award->setPosition(Vec2(m_pBg->getPositionX(),m_pBg->getPositionY()*2));
  5. this->addChild(award);
  6. autobounce=EaseBounceOut::create(MoveBy::create(2,Vec2(0,-m_pBg->getPositionX()*2)));
  7. award->runAction(Sequence::createWithTwoActions(bounce,CallFuncN::create([=](Node*node){
  8. award->removeFromParentAndCleanup(true);
  9. m_turnArr->setEnabled( })));


再来看看咱们转盘中的粒子效果,有两种,第一种圆环的星星闪烁效果比较简单,设置下离中心的距离就好了,这里主要讲解以椭圆轨迹旋转的小彗星粒子效果。

既然以椭圆为轨迹,其实也就是实时更新下粒子的位置,但是椭圆的坐标怎么计算呢?想必部分人都忘记了吧(我也忘记了。。。),直接去问度娘吧:

咱们椭圆的中心即是转盘的中心,所以是一个标准的椭圆方程:

,对应的参数方程就是:

那么答案就出来啦,只要我们实时改变参数φ的值,那么椭圆上的坐标就会实时更新。知道原理了,我们再来看看怎么设计这样一个椭圆类。既然沿椭圆轨迹运动,那么为什么不把这一种动作设计成跟cocos2dx引擎中的动作Action一样呢?在使用的时候,我们只需要调用runAction就可以了。我们可以参考cocos2dx引擎动作类的设计。

来看头文件:

    #ifndef_ELLIPSEBY_H_
  1. #define_ELLIPSEBY_H_
  2. #include"cocos2d.h"
  3. USING_NS_CC;
  4. #definePI3.14159
  5. //椭圆的参数信息
  6. structEllipseConfig
  7. //椭圆a的长度
  8. floatellipseA;
  9. //椭圆b的长度
  10. floatellipseB;
  11. //椭圆的中心坐标
  12. Vec2cenPos;
  13. //是否逆时针旋转
  14. boolisAntiClockwise;
  15. //目标开始旋转的位置,默认位置是在椭圆长轴右方,即值为0
  16. floatstartAngle;
  17. //目标自身的角度
  18. floatselfAngle;
  19. };
  20. classEllipseBy:publicActionInterval
  21. public:
  22. EllipseBy();
  23. ~EllipseBy();
  24. //初始化函数,参数t为持续时间,config为椭圆参数
  25. staticEllipseBy*create(floatt,constEllipseConfig&config);
  26. boolinitWithDuration(constEllipseConfig&config);
  27. //每帧更新当前椭圆坐标
  28. virtualvoidupdate(floattime)override;
  29. //在动作开始前调用
  30. voidstartWithTarget(Node*target)override;
  31. //动作的拷贝
  32. virtualEllipseBy*clone()constoverride;
  33. //动作的逆序
  34. virtualEllipseBy*reverse()protected:
  35. //获得椭圆上当前点坐标
  36. inlineVec2&getPosWithEllipse(floatt)
  37. {
  38. floatangle=2*PI*((m_config.isAntiClockwise?t:(1-t))+m_config.startAngle/360);
  39. returnVec2(m_config.ellipseA*cos(angle),m_config.ellipseB*sin(angle));
  40. private:
  41. EllipseConfigm_config;
  42. };
  43. #endif

我们定义了一个椭圆参数的结构体EllipseConfig,前面4个比较好理解,后面2个:startAngle是开始旋转粒子出现的位置,值为角度值。比如下面例图所示:

selfAngle是指把整个椭圆当成一个整体,这个整体的角度,类似于精灵的rotation属性。比如下面例图所示:

接着再来看看从父类继承来的三个函数:startWithTarget、clone、reverse

startWithTarget是用来设置是谁要执行动作,在动作开始前调用;后面两个,一个是动作的拷贝,一个是动作的逆序,因为在父类中是纯虚函数,所以要继承实现。

再来看看函数getPosWithEllipse,这个是利用椭圆的参数方程,获得当前目标所处椭圆上的位置。因为要不停的调用,所以声明为内联函数。

最后看看cpp文件的部分实现代码:

    EllipseBy*EllipseBy::clone()const
  1. autopAction=newEllipseBy();
  2. pAction->initWithDuration(_duration,m_config);
  3. pAction->autorelease();
  4. returnpAction;
  5. }
  6. EllipseBy*EllipseBy::reverse() EllipseConfigresConfig=m_config;
  7. resConfig.isAntiClockwise=!m_config.isAntiClockwise;
  8. returnEllipseBy::create(_duration,m_config);
  9. voidEllipseBy::startWithTarget(Node*target)
  10. ActionInterval::startWithTarget(target);
  11. voidEllipseBy::update(floattime)
  12. if(_target)
  13. Vec2curPos=this->getPosWithEllipse(time);
  14. floattmpAngle=m_config.selfAngle/180*PI;
  15. floatnewX=curPos.x*cos(tmpAngle)+curPos.y*sin(tmpAngle);
  16. floatnewY=curPos.y*cos(tmpAngle)-curPos.x*sin(tmpAngle);
  17. _target->setPosition(m_config.cenPos+Vec2(newX,newY));
  18. 其中最重要的部分就是update函数啦,getPosWithEllipse获得的坐标curPos是selfAngle为0时的坐标,如果我们设置了椭圆自身的角度,就要调整下curPos的坐标。有以下公式:

    如果椭圆自身旋转了β,即selfAngle =β那么之后的坐标是:newX = xcosβ+ ysinβ,newY = ycosβ- xsinβ

    这里给大家简单的分析一下公式,先看图:


    这里黑色的椭圆是没有设置selfAngle时的样子,当设置selfAngle为β后,就变成蓝色的椭圆。由于两个椭圆的中心都是圆心,所以椭圆上同一位置上的点到圆心的距离以一样,也就是上图中红线和绿线的长度相等,那么利用勾股定理,就是下面:

    a^2 + b^2 = x^2 + y^2 = x^2 * 1 + y^2 * 1

    因为cos& * cos& + sin& * sin& = 1,所以:

    上面公式 = x^2 * (cos(β)^2 + sin(β)^2) + y^2 * (cos(β)^2 + sin(β)^2)

    然后分解合并,就可以得到下面的公式啦:

    a^2 + b^2 = (xcosβ+ ysinβ)^2 + (ycosβ- xsinβ)^2

    所以:a = xcosβ+ ysinβ,b = ycosβ- xsinβ

    最后,我们只需要如下调用,就可以像使用引擎的动作一样:

      //椭圆旋转
    1. EllipseConfigconfig;
    2. config.ellipseA=100;
    3. config.ellipseB=50;
    4. config.cenPos=m_turnBg->getPosition();
    5. config.isAntiClockwise= config.startAngle=0;
    6. config.selfAngle=45;
    7. m_pElliRtt_1->runAction(RepeatForever::create(EllipseBy::create(2.5,config)));


    到这里,本篇上部分内容已经讲解完了,下一篇将讲解抽奖概率的设计。

    资源下载处:http://download.csdn.net/detail/sharing_li/8414387

    本篇第二篇地址:http://www.52php.cn/article/p-mvooejfu-rx.html

    (编辑:李大同)

    【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读