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
78 //移动距离
79 std::unordered_map
80 std::unordered_map
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 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 3 4 //初始化地图 5 for (int i = 0; i 6 vector 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 3 std::unordered_map 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条)