实现了个类似blood brothers中的转轴特效

最近比较忙,有一段时间没写东西了,今天算是忙里偷闲,分享点东西。
在美区有个游戏叫bloodbrothers,玩了玩,感觉还不错,其中有一个页面,可以选择人物的奴仆,页面布局是中间是主人,四周是奴仆,以一个椭圆形围着主人转圈,仆人的数目可以增减,觉得这个页面挺有意思,我就试着实现了一个。
blood brothers的效果:


玩家滑动屏幕,两个骷髅兵会围着中间的老大按照椭圆轨迹转圈。

我实现的效果:


实现了个类似blood <wbr>brothers中的转轴特效

中间的老大我没做,滑动屏幕,四周的机器人图片会以椭圆轨迹旋转,个人感觉效果尚可,如果美工跟得上
我觉得效果不会比人家的差-_-!

废话不多说了,上代码
cwMainLayer.h

#ifndef _cwMainLayer_h_
#define _cwMainLayer_h_

#include "cocos2d.h"
using namespace cocos2d;

#include <string>
#include <vector>
using namespace std;

class cwShowSprite : public CCSprite
{
public:
      cwShowSprite() : m_fAngle(0) {}
      ~cwShowSprite() {}
     
public:
      staticcwShowSprite* create(const char *pszFileName);

private:
      //当前角度
      CC_SYNTHESIZE(float, m_fAngle, Angle);

};

class cwMainLayer : public CCLayer
{
public:
      cwMainLayer() : m_pArrShow(NULL), m_fAngleStep(0),m_fScaleStart(1.0f), m_fScaleEnd(0.6f) {}
      ~cwMainLayer();

      //增加显示图片
      voidaddShow(const char* pcName);
      //排列图片
      voidarrange();

private:
      //计算图片角度
      voidarrangeAngle();
      //计算图片位置
      voidarrangePosition();
      //计算图片zorder
      voidarrangeZOrder();
      //计算图片缩放值
      voidarrangeScale();

      //根据y坐标排序(由小到大)
      vector<cwShowSprite*>orderByY();
      //计算那个图片被点击
      cwShowSprite* clickSprite(CCPoint& pt);

      //将图片移动指定角度
      voidmoveShow(float angle);

public:
      CREATE_FUNC(cwMainLayer);

      boolinit();

      virtual voidonEnter();
      virtual voidonExit();

      virtual boolccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
      virtual voidccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
      virtual voidccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);

private:
      //存储图片数组
      CCArray*m_pArrShow;

      //圆环中点
      CC_SYNTHESIZE_PASS_BY_REF(CCPoint, m_nCenter, Center);
      //椭圆长短轴
      CC_SYNTHESIZE(float, m_fLongA, LongA);
      CC_SYNTHESIZE(float, m_fShortB, ShortB);

      //缩放范围
      CC_SYNTHESIZE(float, m_fScaleStart, ScaleStart);
      CC_SYNTHESIZE(float, m_fScaleEnd, ScaleEnd);

      //图片的起始zorder
      CC_SYNTHESIZE(int, m_iZStart, ZStart);

      //图片在圆环上排列的角度步长
      floatm_fAngleStep;
      //图片Y值区间,也就是椭圆短轴y坐标区域
      floatm_fYMin;
      floatm_fYMax;

      //fortouch
      CCPointm_nTouchBegin;
      CCPointm_nTouchMoving;

      //background
      CCSprite*m_pBackGround;

};

#endif

cwMainLayer.cpp

#include "cwMainLayer.h"

#include <list>
#include <vector>
#include <algorithm>
using namespace std;

cwShowSprite* cwShowSprite::create(const char *pszFileName)
{
      cwShowSprite* pSprite = new cwShowSprite();
      if(pSprite&&pSprite->initWithFile(pszFileName)) {
            pSprite->autorelease();
            returnpSprite;
      }

      CC_SAFE_DELETE(pSprite);
      returnNULL;
}

cwMainLayer::~cwMainLayer()
{
      CC_SAFE_RELEASE_NULL(m_pArrShow);
}

bool cwMainLayer::init()
{
      if(!CCLayer::init()) return false;

      CCSizewinSize =CCDirector::sharedDirector()->getWinSize();

      m_pBackGround = CCSprite::create("bk.png");
      m_pBackGround->setPosition(ccp(winSize.width*0.5,winSize.height*0.5));
      this->addChild(m_pBackGround, -1);

      m_pArrShow =CCArray::create();
      m_pArrShow->retain();

      this->setAnchorPoint(ccp(0, 0));
      setCenter(ccp(winSize.width*0.5, winSize.height*0.7));
      setLongA(winSize.width*0.3);
      setShortB(getLongA()*0.25);

      m_iZStart =1;
      m_fYMin =m_nCenter.y - m_fShortB;
      m_fYMax =m_nCenter.y + m_fShortB;

      returntrue;
}

void cwMainLayer::addShow(const char* pcName)
{
      cwShowSprite* pSprite = cwShowSprite::create(pcName);
      if(!pSprite)return;

      m_pArrShow->addObject(pSprite);

      m_fAngleStep= 360.0f / float(m_pArrShow->count());

      this->addChild(pSprite);
}

void cwMainLayer::arrange()
{
      arrangeAngle();
      arrangePosition();
      arrangeScale();
      arrangeZOrder();
}

void cwMainLayer::arrangeAngle()
{
      if(m_pArrShow->count() == 0) return;

      float fStart=((cwShowSprite*)(m_pArrShow->objectAtIndex(0)))->getAngle();
      int index =0;

      CCObject*pObj;
      CCARRAY_FOREACH(m_pArrShow, pObj) {
            cwShowSprite* pSprite = (cwShowSprite*)pObj;

            pSprite->setAngle(fStart -index*m_fAngleStep);

            index++;
      }
}

void cwMainLayer::arrangePosition()
{
      CCObject*pObj;
      CCARRAY_FOREACH(m_pArrShow, pObj) {
            cwShowSprite* pSprite = (cwShowSprite*)pObj;

            float fAngle= fmod(pSprite->getAngle(), 360.0f);
            float x =cosf(fAngle/180.0*3.14159)*m_fLongA + m_nCenter.x;
            float y =sinf(fAngle/180.0*3.14159)*m_fShortB*0.5f + m_nCenter.y;

            pSprite->setPosition(ccp(x, y));
      }
}

void cwMainLayer::arrangeZOrder()
{
      int iZMax =m_iZStart + m_pArrShow->count();

      vector<cwShowSprite*> v =orderByY();

      vector<cwShowSprite*>::iterator it =v.begin();
      for(;it!=v.end(); ++it) {
            (*it)->removeFromParentAndClean up(false);
            this->addChild((*it), iZMax--);
      }
}

void cwMainLayer::arrangeScale()
{
      CCObject*pObj;
      CCARRAY_FOREACH(m_pArrShow, pObj) {
            cwShowSprite* pSprite = (cwShowSprite*)pObj;

            float fy =pSprite->getPositionY() - m_fYMin;
            if(fy< 0) fy = 0;

            float fScale= fy / (m_fShortB*2);

            pSprite->setScale(1.0 -(m_fScaleStart-m_fScaleEnd)*fScale);
      }
}

void cwMainLayer::moveShow(float angle)
{
      CCObject*pObj;
      CCARRAY_FOREACH(m_pArrShow, pObj) {
            cwShowSprite* pSprite = (cwShowSprite*)pObj;

            pSprite->setAngle(pSprite->getAngle()+angle);
      }
}

void cwMainLayer::onEnter()
{
      CCLayer::onEnter();

      CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,-200, false);
}

void cwMainLayer::onExit()
{
      CCLayer::onExit();

      CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
}

bool comp(const cwShowSprite* p1, const cwShowSprite* p2)
{
      return((CCNode*)(p1))->getPositionY() <((CCNode*)(p2))->getPositionY();
}

vector<cwShowSprite*>cwMainLayer::orderByY()
{
      vector<cwShowSprite*> v;

      CCObject*pObj;
      CCARRAY_FOREACH(m_pArrShow, pObj) {
            cwShowSprite* pSprite = (cwShowSprite*)pObj;
            v.push_back(pSprite);
      }

      sort(v.begin(), v.end(), comp);

      returnv;
}

cwShowSprite* cwMainLayer::clickSprite(CCPoint&pt)
{
      vector<cwShowSprite*> v =orderByY();

      vector<cwShowSprite*>::iterator it =v.begin();
      for(;it!=v.end(); ++it) {
            if((*it)->boundingBox().containsPoint(pt)) return(*it);
      }

      returnNULL;
}

bool cwMainLayer::ccTouchBegan(CCTouch *pTouch, CCEvent*pEvent)
{
      m_nTouchBegin =this->convertTouchToNodeSpace(pTouch);
      m_nTouchMoving = m_nTouchBegin;

      returntrue;
}

void cwMainLayer::ccTouchMoved(CCTouch *pTouch, CCEvent*pEvent)
{
      CCPoint pt =this->convertTouchToNodeSpace(pTouch);

      float fStep= 1.0;
      if(pt.x< m_nTouchMoving.x)
            fStep =-fStep;

      moveShow(fStep);

      arrange();

      m_nTouchMoving = pt;
}

void cwMainLayer::ccTouchEnded(CCTouch *pTouch, CCEvent*pEvent)
{
      CCPoint pt =this->convertTouchToNodeSpace(pTouch);

      float fDist= cocos2d::ccpDistance(pt, m_nTouchBegin);
      if(fDist> 5.0) return;

      cwShowSprite* pSprite = clickSprite(pt);
      if(!pSprite)return;

      CCLog("click%d", pSprite->m_uID);
}
///

代码量不大,300行左右。
其中cwMainLayer::init()中的“bk.png”是我随手做的背景图片。

原理就是椭圆上每个对象保存与向量(1,0)的夹角,有了这个夹角,就可以根据椭圆公式计算其x,y坐标;
根据y坐标来计算其所在层的zorder,计算zorder的目的是使前面的图片可以盖住后面的图片;还可以根据其y值计算其缩放值,计算缩放值目的是达到近似近大远小的效果。

关键的就是那么几个函数:
void cwMainLayer::arrangeAngle()
这个函数计算每个对象在椭圆上与向量(1,0)。

void cwMainLayer::arrangePosition()
使用椭圆公式,根据对象的夹角计算其位置,关于椭圆方面的数学知识可以参考
http://hi.baidu.com/ejoqsqmrmvakuxr/item/389ed4d5d48b03d9241f402b
的文章。

void cwMainLayer::arrangeZOrder()
计算图片zorder,首先对所有图片根据其y坐标由小到大进行排序,然后重新设置其zorder,y值越小
的zorder值越大,熟悉3D的朋友应该对这算法比较熟悉吧,原理类似于z-buffer,不过细节上比z-buffer
简单。

void cwMainLayer::arrangeScale()
计算对象的缩放值,图片y值越大,scale值越小,这样就得到近大远小的效果。

使用起来也不麻烦
在HelloWorld::init()中添加如下代码:

              cwMainLayer* pMLayer = cwMainLayer::create();
            pMLayer->addShow("show3.png");
            pMLayer->addShow("show4.png");
            pMLayer->addShow("show3.png");
            pMLayer->addShow("show4.png");
            pMLayer->addShow("show3.png");
            pMLayer->addShow("show4.png");
            pMLayer->addShow("show3.png");
            pMLayer->addShow("show4.png");
            pMLayer->addShow("show3.png");
            pMLayer->addShow("show4.png");
            pMLayer->addShow("show3.png");
            pMLayer->addShow("show4.png");

            pMLayer->arrange();

            pMLayer->setPosition(ccp(0,0));
            this->addChild(pMLayer, 10);
“show3.png”与“show4.png”就是那两个机器人图片。

忙活了两个多小时,边听歌边写代码,感觉还不错。
不过现在该去睡觉了,最近偏头痛犯了,得早休息!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值