cocos2dx之旋轉的button

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

時間:2015-04-06

作者:Sharing_Li

轉載註明出處:http://blog.csdn.net/sharing_li/article/details/44903971

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

 

       一般遊戲的主界面按鈕的擺放,都是中心垂直對齊,如果弄得稍微炫一點,就是下面的這種效果,也就是本篇要講解的內容:

 

先分析一下功能需求:

1、一共四個按鈕,只有點擊了最前面的按鈕,按鈕的響應事件才能觸發,點擊了其他按鈕,則旋轉到最前面。

2、點擊了除最前面的按鈕外的動畫效果,和左右滑動時的動畫效果。(大小,透明度,運動軌跡)

3、左右滑動到途中時鬆手時的動畫調整。

4、按鈕Z序的調整

5、滑動區域的限制

 

接下來看看代碼怎麼寫:

定義一個類BtnTurn,來看頭文件

#ifndef __BTN_TURN_H__
#define __BTN_TURN_H__

#include "cocos2d.h"

USING_NS_CC;

enum BtnPos
{
	Pos_Bottom = 1,
	Pos_Left,
	Pos_Top,
	Pos_Right,
};


class BtnTurn : public cocos2d::Layer
{
public:
	BtnTurn();
	~BtnTurn();

	virtual bool init();
	CREATE_FUNC(BtnTurn)

protected:
	virtual bool onTouchBegan(Touch* touch, Event* pEvent);
	virtual void onTouchMoved(Touch *pTouch, Event *pEvent);
	virtual void onTouchEnded(Touch *pTouch, Event *pEvent);

	//點擊按鈕之後的動畫
	void runTouchedAmt(Sprite * btn);
	//滑動界面的動畫
	void runSlidedAmt(bool isLeft,float ratio,float judgePosX);
private:
	Sprite * m_btn1;
	Sprite * m_btn2;
	Sprite * m_btn3;
	Sprite * m_btn4;

	Vec2 m_posBottom;
	Vec2 m_posLeft;
	Vec2 m_posTop;
	Vec2 m_posRight;

	Point m_firstPos;
	
	Size m_winSize;
	bool m_valid;//先點擊有效區域
	bool m_invalid;//先點擊無效區域
	
};

#endif


這裏最主要的核心代碼就是沿橢圓軌跡旋轉效果,之前有篇文章講解了沿橢圓運動的動畫,可以像用系統的MoveTo等使用runAction,參考地址:

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

本篇將不採用鏈接中說的方法來實現。

首先,我們定義一些全局數據:

const float RUNTIME = 0.3; //動畫運行時間
float A;//橢圓長半徑
float Bd;//下橢圓短半徑
float Bu;//上橢圓短半徑
float Cx;//橢圓中心X座標
float Cy;//橢圓中心Y座標

再來看看我們的初始化函數:

m_winSize = Director::getInstance()->getWinSize();
	m_posBottom = Vec2(0,0);
	m_posLeft = Vec2(-m_winSize.width * 0.24,m_winSize.height * 0.15);
	m_posTop = Vec2(0,m_winSize.height * 0.24);
	m_posRight = Vec2(m_winSize.width * 0.24,m_winSize.height * 0.15);
	A = m_posBottom.x - m_posLeft.x;
	Bu = m_posTop.y - m_posLeft.y;
	Bd = m_posLeft.y - m_posBottom.y;
	Cx = m_posBottom.x;
	Cy = m_posLeft.y;

	Texture2D * pTt2d = Director::getInstance()->getTextureCache()->addImage("BtnTurn/btn.png");
	m_btn1 = Sprite::createWithTexture(pTt2d);
	m_btn1->setPosition(m_posBottom);
	m_btn1->setTag(Pos_Bottom);
	this->addChild(m_btn1,4);

	m_btn2 = Sprite::createWithTexture(pTt2d);
	m_btn2->setPosition(m_posLeft);
	m_btn2->setScale(0.75);
	m_btn2->setOpacity(100);
	m_btn2->setTag(Pos_Left);
	this->addChild(m_btn2,3);

	m_btn3 = Sprite::createWithTexture(pTt2d);
	m_btn3->setPosition(m_posTop);
	m_btn3->setScale(0.5);
	m_btn3->setOpacity(50);
	m_btn3->setTag(Pos_Top);
	this->addChild(m_btn3,2);

	m_btn4 = Sprite::createWithTexture(pTt2d);
	m_btn4->setPosition(m_posRight);
	m_btn4->setScale(0.75);
	m_btn4->setOpacity(100);
	m_btn4->setTag(Pos_Right);
	this->addChild(m_btn4,3);

	auto listenerT = EventListenerTouchOneByOne::create();
	listenerT->onTouchBegan = CC_CALLBACK_2(BtnTurn::onTouchBegan,this);
	listenerT->onTouchMoved = CC_CALLBACK_2(BtnTurn::onTouchMoved,this);
	listenerT->onTouchEnded = CC_CALLBACK_2(BtnTurn::onTouchEnded,this);
	listenerT->setSwallowTouches(false);
	Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listenerT,this);

	return true;


       這裏,初始化全局變量,和四個按鈕的初始位置。可以發現這裏的按鈕並不是按鈕,而是sprite,因爲只有顯示在最前面的按鈕才能響應函數,所以定義成sprite方便處理。我們通過判斷點擊開始和點擊結束,這兩個點是否是同一個點來確定是否點擊了按鈕,然後根據按鈕的Zorder來判斷是否響應函數。我們還給每個按鈕設置了tag值,這個tag值並不是不變的,因爲按鈕的位置會改變,所以tag的值也會改變,以確保我們通過getChildByTag函數,能夠正確獲取到相應tag值的按鈕,比如getChildByTag(Pos_Top),函數返回的一定是處於最上面位置的那個按鈕。

       我們來看看onTouchBegan函數:

bool BtnTurn::onTouchBegan(Touch* touch, Event* pEvent)
{
	m_firstPos = touch->getLocation();

	return true;
}

很簡單,就兩行代碼,獲取點擊開始的按鈕。然後看看onTouchMoved

void BtnTurn::onTouchMoved(Touch *pTouch, Event *pEvent)
{
	auto movePos = pTouch->getLocation();
	auto judgePos = this->convertToNodeSpace(movePos);
	auto box = Rect(-m_winSize.width * 0.5,-m_winSize.height * 0.1,m_winSize.width,m_winSize.height * 0.4);
	//優化,不能全屏都可以滑,並判斷是先點擊有效還是無效區域
	if (!box.containsPoint(judgePos))
	{
		if (!m_valid)
		{
			m_invalid = true;
		}
		return ;
	}
	if (!m_invalid)
	{
		m_valid = true;
	}
	else
	{
		return ;
	}
	//根據滑動方向來運動
	auto ratio = fabsf(movePos.x - m_firstPos.x) * 2 / m_winSize.width;
	if (ratio >= 1)
	{
		return ;
	}
	this->runSlidedAmt(movePos.x - m_firstPos.x < 0,ratio,fabsf(m_firstPos.x - movePos.x));
}

上面代碼中box是可以滑動的有效區域,m_valid和m_invalid是用來判斷開始觸摸屏幕,是點擊了有效區域,還是無效區域。

然後根據滑動的方向,來調用動畫實現函數runSlidedAmt:

void BtnTurn::runSlidedAmt(bool isLeft,float ratio,float judgePosX)
{
	auto btnTop = this->getChildByTag(Pos_Top);
	auto btnLeft = this->getChildByTag(Pos_Left);
	auto btnRight = this->getChildByTag(Pos_Right);
	auto btnBottom = this->getChildByTag(Pos_Bottom);

	auto deltPosDown = m_posRight - m_posBottom;
	auto deltPosUp = m_posTop - m_posLeft;

	//判斷是否需要調換Z順序
	if (judgePosX > m_winSize.width / 4)
	{
		btnTop->setZOrder(3);
		btnLeft->setZOrder(isLeft ? 2 : 4);
		btnRight->setZOrder(isLeft ? 4 : 2);
		btnBottom->setZOrder(3);
	}
	
	auto B1 = isLeft ? Bu : Bd;//判斷左邊的button沿哪個橢圓運動
	auto B2 = isLeft ? Bd : Bu;//判斷右邊的button沿哪個橢圓運動
	
	int temp = isLeft ? (m_posBottom.x - deltPosDown.x * ratio) : (m_posBottom.x + deltPosDown.x * ratio);
	btnBottom->setPosition(Vec2(temp,sin(-acos((temp - Cx)/A)) * Bd + Cy));
	btnBottom->setScale(1 - 0.25 * ratio);
	btnBottom->setOpacity(255 - 155 * ratio);

	temp = isLeft ? (m_posLeft.y + deltPosUp.y * ratio) : (m_posLeft.y - deltPosDown.y * ratio);
	btnLeft->setPosition(Vec2(-cos(asin((temp - Cy)/B1)) * A + Cx,temp));
	btnLeft->setScale(0.75 - (isLeft ? 0.25 * ratio : -0.25 * ratio));
	btnLeft->setOpacity(100 - (isLeft ? 50 * ratio : -155 * ratio));

	temp = m_posTop.x + (isLeft ? (deltPosUp.x * ratio) : (-1 * deltPosUp.x * ratio));
	btnTop->setPosition(Vec2(temp,sin(acos((temp - Cx)/A)) * Bu + Cy));
	btnTop->setScale(0.5 + 0.25 * ratio);
	btnTop->setOpacity(50 + 50 * ratio);

	temp = m_posRight.y + (isLeft ? (-1 * deltPosDown.y * ratio) : (deltPosUp.y * ratio));
	btnRight->setPosition(Vec2(cos(asin((temp - Cy)/B2)) * A + Cx,temp));
	btnRight->setScale(0.75 + 0.25 * (isLeft ? ratio : -ratio));
	btnRight->setOpacity(100 + (isLeft ? 155 * ratio : -50 * ratio));
	
}


這裏,我們通過橢圓的非標準方程,根據已知的x座標或者y座標,求出對應的y座標或x座標。假如橢圓的圓心座標爲(Cx,Cy),那麼根據方程:

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

      然後根據數學函數庫的反三角函數等,就可以求出相應的值了。這裏需要注意的是三角函數和反三角函數的值域。
      接着,我們來看看onTouchEnded函數:

void BtnTurn::onTouchEnded(Touch *pTouch, Event *pEvent)
{
	if (m_invalid)
	{
		m_invalid = false;
		return;
	}
	auto endPos = pTouch->getLocation();
	auto delX = endPos.x - m_firstPos.x;
	auto delY = endPos.y - m_firstPos.y;
	//如果是點擊操作
	if (fabsf(delX) < 0.0001 && fabsf(delY) < 0.0001)
	{
		endPos = this->convertToNodeSpace(endPos);
		auto box1 = m_btn1->getBoundingBox();
		auto box2 = m_btn2->getBoundingBox();
		auto box3 = m_btn3->getBoundingBox();
		auto box4 = m_btn4->getBoundingBox();
		if (box1.containsPoint(endPos))
		{
			if (m_btn1->getZOrder() == 4)
			{
				log("******************Btn1 CallBack***************");
			}
			else
			{
				
				this->runTouchedAmt(m_btn1);
			}
		}
		else if (box2.containsPoint(endPos))
		{
			if (m_btn2->getZOrder() == 4)
			{
				log("******************Btn2 CallBack***************");
			}
			else
			{
				this->runTouchedAmt(m_btn2);
			}
		}
		else if (box3.containsPoint(endPos))
		{
			if (m_btn3->getZOrder() == 4)
			{
				log("******************Btn3 CallBack***************");
			}
			else
			{
				this->runTouchedAmt(m_btn3);
			}
		}
		else if (box4.containsPoint(endPos))
		{
			if (m_btn4->getZOrder() == 4)
			{
				log("******************Btn4 CallBack***************");
			}
			else
			{
				this->runTouchedAmt(m_btn4);
			}
		}
	}
	else//滑動操作
	{
		auto adjustPos = pTouch->getLocation();
		//判斷滑動方向
		if (adjustPos.x - m_firstPos.x < 0)//向左滑動
		{
			auto tmpBtn = (Sprite *)this->getChildByTag(Pos_Right);
			this->runTouchedAmt(tmpBtn);
		}
		else if (adjustPos.x - m_firstPos.x > 0)
		{
			auto tmpBtn = (Sprite *)this->getChildByTag(Pos_Left);
			this->runTouchedAmt(tmpBtn);
		}
	}
	m_valid = false;
}


首先判斷是否是點擊操作,如果是,再來判斷點擊了哪個button,如果是最前面的button,就響應函數,如果不是則調用動畫效果;如果不是點擊操作,那就是滑動操作,然後根據滑動方向調用調整動畫。我們來看看runTouchedAmt函數:

void BtnTurn::runTouchedAmt(Sprite * btn)
{
	auto tag = btn->getTag();
	switch (tag)
	{
	case Pos_Left :
		{
			btn->runAction(Spawn::create(ScaleTo::create(RUNTIME,1),
										Sequence::createWithTwoActions(MoveTo::create(RUNTIME / 2,m_posBottom),
												MoveTo::create(RUNTIME / 2,m_posBottom)),
										FadeIn::create(RUNTIME),NULL));
			btn->setZOrder(4);
			
			auto topBtn = (Sprite *)(this->getChildByTag(Pos_Top));
			topBtn->runAction(Spawn::create(ScaleTo::create(RUNTIME,0.75),
											MoveTo::create(RUNTIME,m_posLeft),
											FadeTo::create(RUNTIME,100),NULL));
			topBtn->setZOrder(3);

			auto rightBtn = (Sprite *)this->getChildByTag(Pos_Right);
			rightBtn->runAction(Spawn::create(ScaleTo::create(RUNTIME,0.5),
								MoveTo::create(RUNTIME,m_posTop),
								FadeTo::create(RUNTIME,50),NULL));
			rightBtn->setZOrder(2);

			auto bottomBtn = (Sprite *)this->getChildByTag(Pos_Bottom);
			bottomBtn->runAction(Spawn::create(ScaleTo::create(RUNTIME,0.75),
												MoveTo::create(RUNTIME,m_posRight),
												FadeTo::create(RUNTIME,100),NULL));
			bottomBtn->setZOrder(3);

			btn->setTag(Pos_Bottom);
			topBtn->setTag(Pos_Left);
			rightBtn->setTag(Pos_Top);
			bottomBtn->setTag(Pos_Right);
		}
		break;
	case Pos_Top :
		{
			btn->runAction(Spawn::create(ScaleTo::create(RUNTIME,1),
				Sequence::createWithTwoActions(MoveTo::create(RUNTIME/2,m_posLeft),MoveTo::create(RUNTIME/2,m_posBottom)),
				FadeIn::create(0.2),NULL));
			btn->setZOrder(4);

			auto rightBtn = (Sprite *)this->getChildByTag(Pos_Right);
			rightBtn->runAction(Spawn::create(Sequence::createWithTwoActions(ScaleTo::create(RUNTIME/2,0.5),ScaleTo::create(RUNTIME/2,0.75)),
				Sequence::createWithTwoActions(MoveTo::create(RUNTIME/2,m_posTop),MoveTo::create(RUNTIME/2,m_posLeft)),
				Sequence::createWithTwoActions(FadeTo::create(RUNTIME/2,50),FadeTo::create(RUNTIME/2,100)),NULL));
			rightBtn->setZOrder(3);

			auto bottomBtn = (Sprite *)this->getChildByTag(Pos_Bottom);
			bottomBtn->runAction(Spawn::create(ScaleTo::create(RUNTIME,0.5),
				Sequence::createWithTwoActions(MoveTo::create(RUNTIME/2,m_posRight),MoveTo::create(RUNTIME/2,m_posTop)),
				FadeTo::create(RUNTIME,50),NULL));
			bottomBtn->setZOrder(2);

			auto leftBtn = (Sprite *)this->getChildByTag(Pos_Left);
			leftBtn->runAction(Spawn::create(Sequence::createWithTwoActions(ScaleTo::create(RUNTIME/2,1),ScaleTo::create(RUNTIME/2,0.75)),
				Sequence::createWithTwoActions(MoveTo::create(RUNTIME/2,m_posBottom),MoveTo::create(RUNTIME/2,m_posRight)),
				Sequence::createWithTwoActions(FadeIn::create(RUNTIME/2),FadeTo::create(RUNTIME/2,100)),NULL));
			leftBtn->setZOrder(3);

			btn->setTag(Pos_Bottom);
			leftBtn->setTag(Pos_Right);
			rightBtn->setTag(Pos_Left);
			bottomBtn->setTag(Pos_Top);

		}
		break;
	case Pos_Right :
		{
			btn->runAction(Spawn::create(ScaleTo::create(RUNTIME,1),
				MoveTo::create(RUNTIME,m_posBottom),
				FadeIn::create(RUNTIME),NULL));
			btn->setZOrder(4);

			auto topBtn = (Sprite *)this->getChildByTag(Pos_Top);
			topBtn->runAction(Spawn::create(ScaleTo::create(RUNTIME,0.75),
				MoveTo::create(RUNTIME,m_posRight),
				FadeTo::create(RUNTIME,100),NULL));
			topBtn->setZOrder(3);

			auto leftBtn = (Sprite *)this->getChildByTag(Pos_Left);
			leftBtn->runAction(Spawn::create(ScaleTo::create(RUNTIME,0.5),
				MoveTo::create(RUNTIME,m_posTop),
				FadeTo::create(RUNTIME,50),NULL));
			leftBtn->setZOrder(2);

			auto bottomBtn = (Sprite *)this->getChildByTag(Pos_Bottom);
			bottomBtn->runAction(Spawn::create(ScaleTo::create(RUNTIME,0.75),
				MoveTo::create(RUNTIME,m_posLeft),
				FadeTo::create(RUNTIME,100),NULL));
			bottomBtn->setZOrder(3);

			btn->setTag(Pos_Bottom);
			topBtn->setTag(Pos_Right);
			leftBtn->setTag(Pos_Top);
			bottomBtn->setTag(Pos_Left);

		}
		break;
	}

}

代碼好像有點多,其實也就是分別處理點擊了除最前面按鈕的動畫效果。這裏簡單起見,沒有用橢圓效果,用的moveto函數。

到這裏講解完畢,免費下載資源


 

發佈了116 篇原創文章 · 獲贊 102 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章