一般游戏的主界面按钮的摆放,都是中心垂直对齐,如果弄得稍微炫一点,就是下面的这种效果,也就是本篇要讲解的内容:
先分析一下功能需求:
1、一共四个按钮,只有点击了最前面的按钮,按钮的响应事件才能触发,点击了其他按钮,则旋转到最前面。
2、点击了除最前面的按钮外的动画效果,和左右滑动时的动画效果。(大小,透明度,运动轨迹)
3、左右滑动到途中时松手时的动画调整。
4、按钮Z序的调整
5、滑动区域的限制
接下来看看代码怎么写:
定义一个类BtnTurn,来看头文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
#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,参考地址
本篇将不采用链接中说的方法来实现。
首先,我们定义一些全局数据:
1
2
3
4
5
6
|
const
float
RUNTIME = 0.3;
//动画运行时间
float
A;
//椭圆长半径
float
Bd;
//下椭圆短半径
float
Bu;
//上椭圆短半径
float
Cx;
//椭圆中心X坐标
float
Cy;
//椭圆中心Y坐标
|
再来看看我们的初始化函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
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函数:
1
2
3
4
5
6
|
bool
BtnTurn::onTouchBegan(Touch* touch, Event* pEvent)
{
m_firstPos = touch->getLocation();
return
true
;
}
|
很简单,就两行代码,获取点击开始的按钮。然后看看onTouchMoved
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
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函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
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函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
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函数。
到这里讲解完毕,免费下载资源