近来cocos2dx的论坛刮起了一阵制作贪食蛇的潮流,我也打算来凑个热闹。不过主要目的还是因为在写3.0过渡篇的系列博客时讲的都是理论,缺少实践。这次就用贪食蛇的例子较为系统的介绍3.0与2.0的一些不同之处。(当然了,有的人肯定会说我是冲沈大海老师的书来了,这种事坚决不能忍!,我慎重说明:我的收货地址是…)
贪食蛇嘛,大家都懂的,就是那条又长又细、可伸缩自如外加弹性还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 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);
```
2、帮助界面其实就一menu,所以我就不啰嗦介绍了,直接看下游戏界面的代码实现:
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放到事件委托中
}
```
2)在init()中初始化蛇头和食物的坐标,并开启定时器实时更新蛇的坐标
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);//将新的节点加入到蛇的身体中。
}
}
```
3)通过draw()绘制游戏界面的格子与食物等
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;icol*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();*/
}
```
4)通过重力的回调函数来更新蛇的移动方向
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一些不同的地方。





