cocos引擎—类神经猫三消游戏《Rabbit Escape》03:让兔子动起来

cocos引擎—类神经猫三消游戏《Rabbit Escape》03:让兔子动起来


2024年3月7日发(作者:下载qq游戏大厅手机版)

类神经猫三消游戏《Rabbit Escape》03:让兔子动起来

一、前言

经过上一讲的讲解,我们已经可以将UI响应用户的交互操作了(添加石头),但是这都只是在UI界面上的一些操作,今天我们会讲一讲如何创建逻辑层,并将UI层和游戏逻辑层进行联系,以及实现简单的让兔子动起来。

二、创建GameMap

首先在我们项目中要新建一个名叫GameCtrl的类,来管理游戏的逻辑。

.h文件:

1 #ifndef __RabbitTech__GameCtrl__

2 #define __RabbitTech__GameCtrl__

3

4 #include

5 #include

6 #include

7 using namespace std;

8

9 const int NUM_MAPROWANDCOW = 9; //表示行列数

10 const int VAL_MAX = 0x0FFFFFFF; //表示最大值

11

12 //方向枚举

13 enum class Dir{

14 up,

15 down,

16 left,

17 right,

18 upLeft,

19 upRight,

20 downLeft,

21 downRight,

22 none

23 };

24

25 //地图矢量

26 struct Vec{

27 int hor; //横向

28 int ver; //纵向

29 Vec(){

30 hor = 0;

31 ver = 0;

32 }

33 Vec(int _ver,int _hor){

34 hor = _hor;

35 ver = _ver;

36 }

37 bool operator == (const Vec& vec ){

38 return ==hor&&==ver;

39 }

40

41 Vec operator + (const Vec& vec){

42 return Vec(+ver,+hor);

43 }

44

45 };

46

47 //地图位置

48 struct Pos{

49 //是否是障碍;

50 bool isObt;

51 //位置信息

52 Vec vec;

53

54 Pos(){

55 = 0;

56 = 0;

57 isObt = false;

58 }

59 Pos(int _ver,int _hor){

60 = _ver;

61 = _hor;

62 isObt = false;

63 }

64

65 };

66 class GameCtrl{

67 public:

68 GameCtrl();

69 ~GameCtrl();

70 public:

71 //添加石头

72 void addObtacle(int tag);

73 //移动兔子

74 int rabbitMove();

75 private:

76 //游戏地图

77 vector>m_gameMap;

78 //移动距离

79 std::unordered_map m_moveVec;

80 std::unordered_map m_moveDirs;

81 //初始化地图移动矢量

82 void initMoveVec();

83 //初始花地图信息

84 void initMapInfo();

85

86 Vec m_rabbitPos; //兔子的位置

87 };

88

89

90 #endif /* defined(__RabbitTech__GameCtrl__) */

.cpp文件:

1 #include "GameCtrl.h"

2 GameCtrl::GameCtrl(){

3 this->initMoveVec();

4 this->initMapInfo();

5 }

6

7 void GameCtrl::initMoveVec(){

8 //创建移动HASH

9 m_moveVec[(int)Dir::up] = new Vec(-1,0);

10 m_moveVec[(int)Dir::down] = new Vec(1,0);

11 m_moveVec[(int)Dir::left] = new Vec(0,-1);

12 m_moveVec[(int)Dir::right] = new Vec(0,1);

13 m_moveVec[(int)Dir::upLeft] = new Vec(-1,-1);

14 m_moveVec[(int)Dir::upRight] = new Vec(-1,1);

15 m_moveVec[(int)Dir::downLeft] = new Vec(1,-1);

16 m_moveVec[(int)Dir::downRight]= new Vec(1,1);

17

18 //创建映射hash

19 m_moveDirs[m_moveVec[(int)Dir::up]]=(int)Dir::up;

20 m_moveDirs[m_moveVec[(int)Dir::down]]=(int)Dir::down;

21 m_moveDirs[m_moveVec[(int)Dir::left]]=(int)Dir::left;

22 m_moveDirs[m_moveVec[(int)Dir::right]]=(int)Dir::right;

23 m_moveDirs[m_moveVec[(int)Dir::upLeft]]=(int)Dir::upLeft;

24 m_moveDirs[m_moveVec[(int)Dir::upRight]]=(int)Dir::upRight;

25 m_moveDirs[m_moveVec[(int)Dir::downLeft]]=(int)Dir::downLeft;

26 m_moveDirs[m_moveVec[(int)Dir::downRight]]=(int)Dir::downRight;

27 }

28 void GameCtrl::initMapInfo(){

29

30 //初始化地图

31 for (int i = 0; i

32 vector curRow;

33 for (int j = 0; j

34 auto pNewPos = new Pos(i,j);

35 _back(pNewPos);

36

37 }

38 m__back(curRow);

39 }

40

41 //初始化兔子信息

42 m_=m_ = NUM_MAPROWANDCOW/2;

43 }

44

45 void GameCtrl::addObtacle(int tag){

46 m_gameMap[tag/NUM_MAPROWANDCOW][tag%NUM_MAPROWANDCOW]->isObt = true;

47 }

48

49 int GameCtrl::rabbitMove() {

50 //假设让兔子一直向上

51 m_rabbitPos=(*m_moveVec[(int)Dir::up])+m_rabbitPos;

52 return m_*NUM_MAPROWANDCOW+m_;

53 }

54

55 GameCtrl::~GameCtrl(){

56 //析构地图信息

57 for (int i = 0; i

58 for (int j = 0; j

59 delete m_gameMap[j];

60 }

61 }

62

63 //析构移动矢量

64 for (auto& it:m_moveVec) {

65 delete ;

66 }

67

68

69 }

PS:因为整个逻辑控制是脱离Cocos2d-x架构的,为了降低耦合,我没有使用Cocos2d-x的头文件。

玩过《围住神经猫》的童鞋应该都知道,它的地图看起来不是很对称,但是其实可以抽象为一个9X9的矩阵(后面称为GameMap)。所以该GameMap一共有9*9=81个位置,所以为了表示每一个位置的信息,我抽象出了 一个叫Vec(地图矢量)的结构来表示。

1 //地图矢量

2 struct Vec{

3 int hor; //横向

4 int ver; //纵向

5 Vec()

6 {

7 hor = 0;

8 ver = 0;

9 }

10 Vec(int _ver,int _hor)

11 {

12 hor = _hor;

13 ver = _ver;

14 }

15

16 bool operator == (const Vec& vec )

17 {

18 return ==hor&&==ver;

19 }

20 Vec operator + (const Vec& vec)

21 {

22 return Vec(+ver,+hor);

23 }

24 };

为了在后面的操作方面我重写了它的两个运算符。

而为了进行后面能够存储表示每个位置在A*寻路算法中的各种信息(下次教程会具体介绍),我将它进行了再次的封装。

1 //地图位置

2 struct Pos

3 {

4 //是否是障碍;

5 bool isObt;

6 //位置信息

7 Vec vec;

8 Pos()

9 {

10 = 0;

11 = 0;

12 isObt = false;

13 }

14 Pos(int _ver,int _hor)

15 {

16 = _ver;

17 = _hor;

18 isObt = false;

19 }

20 };

OK,现在可以创建我们的游戏地图了,这里我用了一个的vector来模拟二维的地图。

1 //游戏地图

2 vector>m_gameMap;

3

4 //初始化地图

5 for (int i = 0; i

6 vector curRow;

7 for (int j = 0; j

8 auto pNewPos = new Pos(i,j);

9 _back(pNewPos);

10 }

11 m__back(curRow);

12 }

如果对vector如何模拟二维动态数组的童鞋,可以百度一下相关内容进行了解。

三、兔子在逻辑层的移动

有些童鞋肯定以为神经猫中的猫一共只有四个移动方向,但是其实事实上仔细观察的话就会发现,其实是有8个方向可以移动的。

所以我做了一个枚举类来表示其移动的方向。

1 //方向枚举

2 enum class Dir{

3 up,

4 down,

5 left,

6 right,

7 upLeft,

8 upRight,

9 downLeft,

10 downRight,

11 none

12 };

有了方向枚举,怎么能够和地图矢量联系起来呢?

相信如果有点数学基础的童鞋,都应该看得懂这张图。这就是用二维的数组来模拟平面的点的情况。

如,当前rabbit的Vec(假设为rabbitVec)的话,在rabbit上面一行的up位置的Vec应该为(-1,)。

我们要用个成员变量记录rabbit位置并且进行一开始初始化:

1 Vec m_rabbitPos;

2 //兔子的位置

3 //初始化兔子信息

4 m_=m_ = NUM_MAPROWANDCOW/2;

一开始将其放在(4,4)的地方,即正中间。

为了以后进行位置变化方便,我这里用了两个hash map来保存移动的信息。

1 //移动距离

2 std::unordered_map m_moveVec;

3 std::unordered_map m_moveDirs;

4

5 void GameCtrl::initMoveVec(){

6 //创建移动HASH

7 m_moveVec[(int)Dir::up] = new Vec(-1,0);

8 m_moveVec[(int)Dir::down] = new Vec(1,0);

9 m_moveVec[(int)Dir::left] = new Vec(0,-1);

10 m_moveVec[(int)Dir::right] = new Vec(0,1);

11 m_moveVec[(int)Dir::upLeft] = new Vec(-1,-1);

12 m_moveVec[(int)Dir::upRight] = new Vec(-1,1);

13 m_moveVec[(int)Dir::downLeft] = new Vec(1,-1);

14 m_moveVec[(int)Dir::downRight]= new Vec(1,1);

15

16 //创建映射hash

17 m_moveDirs[m_moveVec[(int)Dir::up]]=(int)Dir::up;

18 m_moveDirs[m_moveVec[(int)Dir::down]]=(int)Dir::down;

19 m_moveDirs[m_moveVec[(int)Dir::left]]=(int)Dir::left;

20 m_moveDirs[m_moveVec[(int)Dir::right]]=(int)Dir::right;

21 m_moveDirs[m_moveVec[(int)Dir::upLeft]]=(int)Dir::upLeft;

22 m_moveDirs[m_moveVec[(int)Dir::upRight]]=(int)Dir::upRight;

23 m_moveDirs[m_moveVec[(int)Dir::downLeft]]=(int)Dir::downLeft;

24 m_moveDirs[m_moveVec[(int)Dir::downRight]]=(int)Dir::downRight;

25 }

一个用于存储移动的Vec,一个用于进行映射(后面会有用到)。这样操作的话会大大减少代码的复杂度。

四、UI层和逻辑层联系起来

讲了肿么多,你可能会问了,你怎么能将你逻辑层的障碍,兔子和你表现在UI上面的兔子和障碍联系起来了呢?

哈哈哈,这里我主要用到了对逻辑标签tag进行数学中的除和取余操作实现的,看了下面这张图也许你就会懂了。

有了上面的代码,关于添加障碍的代码,也应该可以理解了。

1 void GameCtrl::addObtacle(int tag)

2 {

3 m_gameMap[tag/NUM_MAPROWANDCOW][tag%NUM_MAPROWANDCOW]->isObt = true;

4 }

而关于UI层的兔子移动是根据逻辑层的兔子移动后Vec中的ver,hor来反推出tag,然后在UI中找到该tag对应的sprite,将兔子移动到相应的sprite的position。

int GameCtrl::rabbitMove() {

1

//假设让兔子一直向上

2

m_rabbitPos=(*m_moveVec[(int)Dir::up])+m_rabbitPos;

3

return m_*NUM_MAPROWANDCOW+m_;

4

}

5

6

//rabbit move

7

int moveTag = m_ctrl->rabbitMove();

8

if(m_pNodes->getChildByTag(moveTag)){

9

m_pNode_rabbit->runAction(MoveTo::create(0.1, m_pNodes->getChildByTag(moveTag)->getPos10

ition()));

11

}

这里我们先假设兔子是只能向上移动,下次我会讲解如何根据兔子能够沿着正确的方向进行移动。

最后需要修改一下在HelloWorld中添加我们的控制类GameCtrl,以及修改触摸响应函数

1 private:

2 //游戏逻辑控制

3 GameCtrl* m_ctrl;

4

5 bool HelloWorld::onTouchBeganCallBack(Touch *t, Event* event){

6 for (auto it =m_pNodes_();it!=m_pNodes_() ; it++) {

7 if((*it)->getBoundingBox().containsPoint(t->getLocation())&&(!(*it)->isVisible())){

8 //add obt

9 auto pObt = (*it);

10 pObt->setVisible(true);

11 m_ctrl->addObtacle(pObt->getTag());

12

13 //rabbit move

14 int moveTag = m_ctrl->rabbitMove();

15 if(m_pNodes->getChildByTag(moveTag)){

16 m_pNode_rabbit->runAction(MoveTo::create(0.1, m_pNodes->getChildByTag(moveTag)->g17 etPosition()));

18 }

19

20 break;

21 }

22 }

23 return false;

}

别忘了对m_ctrl进行内存管理哦,因为不参与cocos的内存管理,所以需要自己对其进行内存管理。

五、最后说几句

1.因为电脑重装系统了,所以这次上传的东西是用Cocos2d-x v3.4和Cocos Studio 2.1版本。所以如果有的童鞋还用的是3.3版本的话,可能编译的时候Cocos Studio的资源没办法加载。这种情况的话,将ccs用老版的Cocos Studio打开后重新发布下资源,就可以使用了。

2.本教程仅供交流和学习,切勿用于商业开发,转载请注明出处。如果有疑问和更好建议的童鞋可以给我留言或者微博(@Tezika)私信我,我也会在最快做出回复。

3.本次课程资源下载:Rabbit Escape_3


发布者:admin,转转请注明出处:http://www.yc00.com/xitong/1709776493a1657517.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信