近来cocos2dx的论坛刮起了一阵制作贪食蛇的潮流,我也打算来凑个热闹。不过主要目的还是因为在写3.0过渡篇的系列博客时讲的都是理论,缺少实践。这次就用贪食蛇的例子较为系统的介绍3.0与2.0的一些不同之处。(当然了,有的人肯定会说我是冲沈大海老师的书来了,这种事坚决不能忍!,我慎重说明:我的收货地址是...)
------------------
贪食蛇嘛,大家都懂的,就是那条又长又细、可伸缩自如外加弹性还OK的..蛇啦。
首先介绍下游戏制作流程:
1、游戏中有三个场景,分别是主菜单界面(HelloWorld),帮助界面(GameHelp),游戏界面(GameLayer)。
2、进入游戏场景要处理的事件有:
a、开启重力感应,在onAcceleration()回调函数里判断蛇应该往哪个方向移动;
b、用draw()方法来自定义图层显示内容,如界面中的格子,蛇头,身体,食物等;
c、通过update定时器来实时更新蛇的位置
3、请继续往下看.......
代码实现如下:
先看.h头文件:
头文件的注释描述的还算清楚,所以大家看完后应该会对游戏的流程有了一定的概念,继续往下走。
1、创建主菜单界面的主要代码如下:
2、帮助界面其实就一menu,所以我就不啰嗦介绍了,直接看下游戏界面的代码实现:
1)首先在onEnter()中打开重力感应
2)在init()中初始化蛇头和食物的坐标,并开启定时器实时更新蛇的坐标
3)通过draw()绘制游戏界面的格子与食物等
4)通过重力的回调函数来更新蛇的移动方向
------------------
贪食蛇嘛,大家都懂的,就是那条又长又细、可伸缩自如外加弹性还OK的..蛇啦。
首先介绍下游戏制作流程:
1、游戏中有三个场景,分别是主菜单界面(HelloWorld),帮助界面(GameHelp),游戏界面(GameLayer)。
2、进入游戏场景要处理的事件有:
a、开启重力感应,在onAcceleration()回调函数里判断蛇应该往哪个方向移动;
b、用draw()方法来自定义图层显示内容,如界面中的格子,蛇头,身体,食物等;
c、通过update定时器来实时更新蛇的位置
3、请继续往下看.......
代码实现如下:
先看.h头文件:
- #ifndef __HELLOWORLD_SCENE_H__
- #define __HELLOWORLD_SCENE_H__
- #include "cocos2d.h"
- USING_NS_CC;
- //枚举类型DIR_DEF,分别标识贪食蛇的移动方向
- typedef enum {
- UP=1,
- DOWN,
- LEFT,
- RIGHT
- }DIR_DEF;
- //蛇每个节点都有自己的移动方向,因此,在节点类SnakeNode的定义中包含了行、列和方向3个成员
- class SnakeNode :public cocos2d::Ref
- {
- public:
- int row;//行
- int col;//列
- int dir;//方向
- };
- //游戏欢迎画面,这个大家很熟的
- class HelloWorld : public cocos2d::Layer
- {
- public:
- static cocos2d::Scene* createScene();//获取欢迎画面的Scene
- virtual bool init();
- virtual void onEnter();
- virtual void onExit();
- CREATE_FUNC(HelloWorld);
- };
- //游戏帮助画面
- class GameHelp :public cocos2d::Layer
- {
- public :
- virtual bool init();
- virtual void onEnter();
- virtual void onExit();
- static cocos2d::Scene * createScene();//获取帮助画面
- CREATE_FUNC(GameHelp);
- };
- //游戏画面
- class GameLayer :public cocos2d::Layer
- {
- public :
- static cocos2d::Scene * createScene();//获取游戏画面
- virtual bool init();
- virtual void onEnter();
- virtual void onExit();
- virtual void draw(Renderer *renderer, const kmMat4 &transform, bool transformUpdated) override;//实现当前Layer的定义
- void onAcceleration(Acceleration* acc, Event* event);//重力事件的回调
- void logic01(float t);//update的回调
- CREATE_FUNC(GameLayer);
- protected:
- SnakeNode *sHead; //贪食蛇px py
- SnakeNode *sFood; //食物
- cocos2d::Vector<Ref*> allBody;//蛇的身体,放到容器Vector中
- // cocos2d::Texture2D * chead;
- };
- #endif // __HELLOWORLD_SCENE_H__
头文件的注释描述的还算清楚,所以大家看完后应该会对游戏的流程有了一定的概念,继续往下走。
1、创建主菜单界面的主要代码如下:
- //添加项菜单进入游戏游戏、帮助、退出游戏的按钮
- auto labelstart = LabelTTF::create("startGame","宋体",24);
- auto labelhelp = LabelTTF::create("GameHelp","宋体",24);
- auto labelexit = LabelTTF::create("exitGame","宋体",24);
- //进入游戏按钮
- auto mi01 = MenuItemLabel::create(labelstart,[](Ref* sender)
- {
- CCLOG("go to game");
- Director::getInstance()->replaceScene(GameLayer::createScene());//跳转到游戏场景
- });
- mi01->setPosition(Point(100,200));
- //帮助按钮
- auto mi02 = MenuItemLabel::create(labelhelp,[](Ref* sender)
- {
- CCLOG("go to help");
- Director::getInstance()->replaceScene(GameHelp::createScene());//跳转到帮助场景
- });
- mi02->setPosition(Point(100,150));
- //结束游戏
- auto mi03 = MenuItemLabel::create(labelexit,[](Ref* sender)
- {
- CCLOG("exit the game");
- Director::getInstance()->end();//退出游戏
- });
- mi03->setPosition(Point(100,50));
- auto pMenu = Menu::create(mi01,mi02,mi03, NULL);
- pMenu->setPosition(Point::ZERO);
- this->addChild(pMenu, 1);
1)首先在onEnter()中打开重力感应
- void GameLayer::onEnter()
- {
- Layer::onEnter();
- CCLOG("GameLayer onEnter");
- Device::setAccelerometerEnabled(true);//打开设备的重力感应
- auto listener = EventListenerAcceleration::create(CC_CALLBACK_2(HelloWorld::onAcceleration, this));//创建一个重力监听事件
- _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);//将listener放到事件委托中
- }
- bool GameLayer::init()
- {
- if ( !Layer::init() )
- {
- return false;
- }
- auto labhelp = LabelTTF::create("this is game","宋体",15);
- labhelp->setPosition(Point(0,340));
- this->addChild(labhelp);
- auto labback = LabelTTF::create("MainMenu","宋体",15);
- auto miback = MenuItemLabel::create(labback,[](Ref* sender)
- {
- Director::getInstance()->replaceScene(HelloWorld::createScene());
- });
- miback->setPosition(Point(360,200));
- //chead=::CCTextureCache::sharedTextureCache()->addImage("head.png");
- //初始化蛇头坐标和食物的坐标,用下面这种方法随机出来的坐标每次运行时都是一样一样的......
- sHead = new SnakeNode();
- sHead->row = rand()%10;
- sHead->col = rand()%10;
- //初始化食物的坐标
- sFood = new SnakeNode();
- sFood->row = rand()%10;
- sFood->col = rand()%10;
- //执行定时任务
- this->schedule(schedule_selector(GameLayer::logic01),0.5);
- return true;
- }
- //定时器
- void GameLayer::logic01(float t)
- {
- //移动蛇的身体
- for(int i = allBody.size()-1; i>=0; i--)
- {
- SnakeNode * sn = (SnakeNode *)allBody.at(i);//获取蛇身体上的某个节点
- if(i>0)
- {
- //如果该节点不是第一个节点,那么该节点的下一个坐标就是其前一个点的坐标(这里不用多解释,玩过蛇的都懂)
- SnakeNode * snpre = (SnakeNode *)allBody.at(i-1);//获取前一个节点,把前一个节点的方向,坐标传给当前节点
- sn->dir = snpre->dir;
- sn->row = snpre->row;
- sn->col = snpre->col;
- }
- else if(i==0)
- {
- //如果i=0则是第一个节点,蛇头的坐标便是该节点的坐标
- sn->dir = sHead->dir;
- sn->row = sHead->row;
- sn->col = sHead->col;
- }
- }
- //移动蛇头,根据dir来判断蛇头的移动方向
- switch(sHead->dir)
- {
- case DIR_DEF::UP:
- sHead->row++;//上移
- if(sHead->row >= 10)
- {
- sHead->row=0;//超过顶部边界后便从底部出来
- }
- break;
- case DIR_DEF::DOWN:
- sHead->row--;
- if(sHead->row < 0)
- {
- sHead->row=9;
- }
- break;
- case DIR_DEF::LEFT:
- sHead->col--;
- if(sHead->col < 0)
- {
- sHead->col=9;
- }
- break;
- case DIR_DEF::RIGHT:
- sHead->col++;
- if(sHead->col >= 10)
- {
- sHead->col=0;
- }
- break;
- };
- //碰撞检测
- //如果蛇头的横、列位置一样,说明蛇吃到了这个食物
- if(sHead->row == sFood->row && sHead->col == sFood->col)
- {
- //食物从当前位置消失,随机出现在下一个坐标
- sFood->row = rand()%10;
- sFood->col = rand()%10;
- //添加身体到集合
- SnakeNode * sn = new SnakeNode();//创建一个新的节点(也就是吃掉的那个食物),将其放到蛇的尾巴上
- SnakeNode * lastNode = NULL;
- //获取蛇的最后一个节点,如果allBody的size()为0,则说明蛇是第一次捕食,那么它的最后一个节点也就是蛇头啦。
- if(allBody.size()>0)
- lastNode = (SnakeNode *)allBody.back();
- else
- lastNode = sHead;//最后一个节点是蛇头
- //通过最后一个节点的方向来个新的节点初始化横、列坐标
- switch(lastNode->dir)
- {
- case DIR_DEF::UP:
- sn->row = lastNode->row-1;
- sn->col = lastNode->col;
- break;
- case DIR_DEF::DOWN:
- sn->row = lastNode->row+1;
- sn->col = lastNode->col;
- break;
- case DIR_DEF::LEFT:
- sn->row = lastNode->row;
- sn->col = lastNode->col+1;
- break;
- case DIR_DEF::RIGHT:
- sn->row=lastNode->row;
- sn->col=lastNode->col-1;
- break;
- }
- this->allBody.pushBack(sn);//将新的节点加入到蛇的身体中。
- }
- }
- void GameLayer::draw(Renderer *renderer, const kmMat4 &transform, bool transformUpdated)
- {
- ///绘制形状
- ::glLineWidth(2);//设定画线的宽度
- for(int i=0;i<11;i++)
- {
- DrawPrimitives::drawLine(Point(0,i*32),Point(320,i*32));//绘制条横线
- DrawPrimitives::drawLine(Point(i*32,0),Point(i*32,320));//绘制条竖线
- }
- // RGBA
- //DrawPrimitives::drawColor4B(ccc4(255,0,0,255));//设定画线的颜色
- //绘制蛇头
- DrawPrimitives::drawSolidRect(Point(sHead->col*32+2,sHead->row*32+2),
- Point(sHead->col*32+32,sHead->row*32+32),
- Color4F(Color3B(255,0,0)));
- //绘制食物
- DrawPrimitives::drawSolidRect(Point(sFood->col*32+2,sFood->row*32+2),
- Point(sFood->col*32+32,sFood->row*32+32),
- Color4F(Color3B(0,0,255)));
- //绘制身体
- for(int i=0;i<allBody.size();i++)
- {
- SnakeNode * node=(SnakeNode *)allBody.at(i);
- DrawPrimitives::drawSolidRect(Point(node->col*32+2,node->row*32+2),
- Point(node->col*32+32,node->row*32+32),
- Color4F(Color3B(0,0,255)));
- }
- /*Rect r(340,0,57,57);
- chead->drawInRect(r);
- Layer::draw();*/
- }
- void GameLayer::onAcceleration(Acceleration* acc, Event* event)
- {
- //0.5这东西很微妙的说
- if(acc->x<=-0.5)
- {
- sHead->dir=DIR_DEF::LEFT;
- log("LEFT");
- }
- else if(acc->x>=0.5)
- {
- sHead->dir=DIR_DEF::RIGHT;
- log("RIGHT");
- }
- else if(acc->y<=-0.5)
- {
- sHead->dir=DIR_DEF::DOWN;
- log("DOWN");
- }
- else if(acc->y>=0.5)
- {
- sHead->dir = DIR_DEF::UP;
- log("UP");
- }
- else
- {
- ;
- }
- }
恩,差不都就是这样了。最后附上游戏截图,如果觉得游戏画面您还满意的话,请给我点32个赞!谢谢!~:
看到这里有的人可能要吐槽了,为什么没有写当蛇撞到尾巴会结束游戏的处理。原因很简单,因为我...懒...。就如开头说的那样,该篇的主要目的是通过一个例子来较为系统的介绍cocos2dx2.0与3.0一些不同的地方。