1. 添加摇杆
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
105
106
107
108
|
OperateLayer.h
class
OperateLayer :
public
cocos2d::Layer
{
public
:
OperateLayer();
~OperateLayer();
virtual
bool
init();
CREATE_FUNC(OperateLayer);
void
onTouchesBegan(
const
std::vector<cocos2d::Touch*>& touches, cocos2d::Event *unused_event);
void
onTouchesMoved(
const
std::vector<cocos2d::Touch*>& touches, cocos2d::Event *unused_event);
void
onTouchesEnded(
const
std::vector<cocos2d::Touch*>& touches, cocos2d::Event *unused_event);
private
:
void
showJoystick(cocos2d::Point pos);
void
hideJoystick();
void
updateJoystick(cocos2d::Point direction,
float
distance);
cocos2d::Sprite *m_pJoystick;
cocos2d::Sprite *m_pJoystickBg;
};
OperateLayer.cpp
OperateLayer::OperateLayer():
m_pJoystick(NULL),
m_pJoystickBg(NULL)
{
}
OperateLayer::~OperateLayer()
{
}
bool
OperateLayer::init()
{
bool
ret =
false
;
do
{
CC_BREAK_IF( !Layer::init() );
m_pJoystick = Sprite::create(
"joystick.png"
);
m_pJoystickBg = Sprite::create(
"joystick_bg.png"
);
this
->addChild(m_pJoystick);
this
->addChild(m_pJoystickBg);
this
->hideJoystick();
auto listener = EventListenerTouchAllAtOnce::create();
listener->onTouchesBegan = CC_CALLBACK_2(OperateLayer::onTouchesBegan,
this
);
listener->onTouchesMoved = CC_CALLBACK_2(OperateLayer::onTouchesMoved,
this
);
listener->onTouchesEnded = CC_CALLBACK_2(OperateLayer::onTouchesEnded,
this
);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener,
this
);
ret =
true
;
}
while
(
false
);
return
ret;
}
void
OperateLayer::showJoystick(Point pos)
{
m_pJoystick->setPosition(pos);
m_pJoystickBg->setPosition(pos);
m_pJoystick->setVisible(
true
);
m_pJoystickBg->setVisible(
true
);
}
void
OperateLayer::hideJoystick()
{
m_pJoystick->setPosition(m_pJoystickBg->getPosition());
m_pJoystick->setVisible(
false
);
m_pJoystickBg->setVisible(
false
);
}
void
OperateLayer::updateJoystick(Point direction,
float
distance)
{
Point start = m_pJoystickBg->getPosition();
if
(distance < 33)
{
m_pJoystick->setPosition(start + (direction * distance));
}
else
if
(distance > 78) {
m_pJoystick->setPosition(start + (direction * 45));
}
else
{
m_pJoystick->setPosition(start + (direction * 33));
}
}
void
OperateLayer::onTouchesBegan(
const
vector<Touch*>& touches, Event *unused_event)
{
Size winSize = Director::getInstance()->getWinSize();
vector<Touch*>::const_iterator touchIter = touches.begin();
while
(touchIter != touches.end())
{
Touch *pTouch = (Touch*)(*touchIter);
Point p = pTouch->getLocation();
if
(p.x <= winSize.width / 2)
{
this
->showJoystick(p);
}
else
{
}
++ touchIter;
}
}
void
OperateLayer::onTouchesMoved(
const
vector<Touch*>& touches, Event *unused_event)
{
Size winSize = Director::getInstance()->getWinSize();
std::vector<Touch*>::const_iterator touchIter = touches.begin();
Touch *pTouch = (Touch*)(*touchIter);
Point start = pTouch->getStartLocation();
if
(start.x > winSize.width / 2)
{
return
;
}
Point dest = pTouch->getLocation();
float
distance = start.getDistance(dest);
Point direction = (dest - start).normalize();
this
->updateJoystick(direction, distance);
}
void
OperateLayer::onTouchesEnded(
const
vector<Touch*>& touches, Event *unused_event)
{
this
->hideJoystick();
}
|
要注意的是cocos2d-x 3.0监听屏幕触摸事件跟以前版本改动了很多,用了c++ 11新特性std::bind和std::function,参数也由之前的set改成了vector。这里来解释下updateJoystick函数,根据手指或者鼠标滑动的距离来更新摇杆,看下面三个图:
33是中间摇杆的半径,45是摇杆底盘的半径,当摇杆内边缘要移出底盘外圆时,需要把摇杆中心固定在底盘外圆边缘。可以利用摇杆位于底盘的内圆边缘或外圆边缘来设置英雄是慢走还是疾走。
为了简便,这里把屏幕分成两半, 当点击在左半部分则显示摇杆,当点击右半部分时则触发英雄攻击(暂时先不实现)。
现在把OperateLayer添加到游戏场景中去,修改GameScene.cpp,在createScene函数中添加下面的代码:
1
2
|
auto operateLayer = OperateLayer::create();
scene->addChild(operateLayer, 1);
|
编译运行项目,效果如下:
现在摇杆有了,可以让英雄听话了,让英雄动起来吧!
2. 控制英雄移动
给精灵添加速度属性,修改BaseSprite类,BaseSprite.h中添加下面的代码:
1
2
|
CC_SYNTHESIZE(cocos2d::Point, m_fVelocity, Velocity);
std::function<
void
(
void
)> attack;
|
在Hero.h中添加:
1
2
|
std::function<
void
(cocos2d::Point,
float
)> walk;
std::function<
void
(
void
)> stop;
|
修改OperateLayer类,让它能控制英雄,OperateLayer.h中添加:
1
|
CC_SYNTHESIZE(Hero*, m_pHero, Hero);
|
OperateLayer.cpp中以下函数中添加执行英雄的相关函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
void
OperateLayer::onTouchesBegan(
const
vector<Touch*>& touches, Event *unused_event)
{
//.....
if
(p.x <= winSize.width / 2)
{
this
->showJoystick(p);
}
else
{
m_pHero->attack();
}
++ touchIter;
}
}
void
OperateLayer::onTouchesMoved(
const
vector<Touch*>& touches, Event *unused_event)
{
//.....
m_pHero->walk(direction, distance);
}
void
OperateLayer::onTouchesEnded(
const
vector<Touch*>& touches, Event *unused_event)
{
//.....
m_pHero->stop();
}
|
在GameLayer.h中添加:
1
2
3
4
5
6
7
8
|
void
onHeroWalk(cocos2d::Point direction,
float
distance);
void
onHeroAttack();
void
onHeroStop();
void
update(
float
dt);
void
updateHero(
float
dt);
float
m_fTileWidth;
float
m_fTileHeight;
|
这几个onHeroXXX函数是在OperateLayer类中执行XXX函数时调用,在GameLayer.cpp中进行绑定。m_fTileWidth和m_fTileHeight表示地图每个瓦片的宽高,用来控制英雄的行走区域。
在GameLayer.cpp的init函数中添加:
1
2
3
4
5
6
7
8
9
|
Size tileSize = m_pTiledMap->getTileSize();
m_fTileWidth = tileSize.width;
m_fTileHeight = tileSize.height;
m_pHero->attack = CC_CALLBACK_0(GameLayer::onHeroAttack,
this
);
m_pHero->stop = CC_CALLBACK_0(GameLayer::onHeroStop,
this
);
m_pHero->walk = CC_CALLBACK_2(GameLayer::onHeroWalk,
this
);
this
->scheduleUpdate();
|
实现GameLayer类新增加的几个函数:
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
|
void
GameLayer::onHeroWalk(Point direction,
float
distance)
{
m_pHero->setFlippedX(direction.x < 0 ?
true
:
false
);
m_pHero->runWalkAction();
Point velocity = direction * (distance < 78 ? 1 : 3);
m_pHero->setVelocity(velocity);
}
void
GameLayer::onHeroAttack()
{
m_pHero->runAttackAction();
}
void
GameLayer::onHeroStop()
{
m_pHero->runIdleAction();
}
void
GameLayer::update(
float
dt)
{
this
->updateHero(dt);
}
void
GameLayer::updateHero(
float
dt)
{
if
(m_pHero->getCurrActionState() == ACTION_STATE_WALK)
{
float
halfHeroFrameHeight = (m_pHero->getDisplayFrame()->getRect().size.height) / 2;
Point expectP = m_pHero->getPosition() + m_pHero->getVelocity();
Point actualP = expectP;
//can not walk on the wall or out of map
if
(expectP.y < halfHeroFrameHeight || expectP.y > (m_fTileHeight * 3 + halfHeroFrameHeight) )
{
actualP.y = m_pHero->getPositionY();
}
float
mapWidth = m_pTiledMap->getContentSize().width;
float
halfWinWidth = m_fScreenWidth / 2;
float
halfHeroFrameWidth = (m_pHero->getDisplayFrame()->getRect().size.width) / 2;
if
(expectP.x > halfWinWidth && expectP.x <= (mapWidth - halfWinWidth))
{
this
->setPositionX(
this
->getPositionX() - m_pHero->getVelocity().x);
}
else
if
(expectP.x < halfHeroFrameWidth || expectP.x >= mapWidth - halfHeroFrameWidth)
{
actualP.x = m_pHero->getPositionX();
}
m_pHero->setPosition(actualP);
m_pHero->setZOrder(m_fScreenHeight - m_pHero->getPositionY());
}
}
|
onHeroWalk是英雄行走时调用的,setFlippedX实现切换英雄的朝向,并根据摇杆的位置改变英雄的速度。updateHero函数对英雄的行走范围进行了控制,不能走出屏幕,也不能走到墙上去。最后修改GameScene.cpp的createScene函数,添加:
operateLayer->setHero(gameLayer->getHero());
好的,编译运行项目,英雄终于听话了,效果如下图:
现在屏幕上就只有一个英雄,感觉太孤独了,下一篇来给它创造几个敌人吧。