cocos2d-x横版格斗游戏教程2

本文介绍了如何在游戏开发中使用摇杆控制英雄角色的移动,通过创建OperateLayer类来实现摇杆功能,并详细解释了如何根据触摸事件更新摇杆位置及控制英雄角色的动作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


上一篇添加了地图和英雄,但是英雄只是傻傻的站着动,而且还不听话,现在我们来控制他,这里采用摇杆的方式来操纵英雄。
为了可以灵活更换操纵方式,这里把操作层抽离出来,创建一个OperateLayer类来实现英雄的操作方式,现在来把摇杆在屏幕上绘制出来:

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());
好的,编译运行项目,英雄终于听话了,效果如下图:

现在屏幕上就只有一个英雄,感觉太孤独了,下一篇来给它创造几个敌人吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值