Cocos2dx 3.0 过渡篇(二十四)死不了的贪食蛇(重力版)

近来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<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);
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;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();*/
}
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个赞!谢谢!~:

Cocos2dx 3.0 过渡篇(二十四)死不了的贪食蛇(重力版)_第1张图片             Cocos2dx 3.0 过渡篇(二十四)死不了的贪食蛇(重力版)_第2张图片

看到这里有的人可能要吐槽了,为什么没有写当蛇撞到尾巴会结束游戏的处理。原因很简单,因为我...懒...。就如开头说的那样,该篇的主要目的是通过一个例子来较为系统的介绍cocos2dx2.0与3.0一些不同的地方。

触摸版链接:http://blog.csdn.net/star530/article/details/24287909

尊重原创,转载请注明来源:http://blog.csdn.net/start530/article/details/23707985

你可能感兴趣的:(cocos2dx,3.0,贪食蛇)