做这个小游戏,看的是兄弟连沈大海老师的视频<<贪吃蛇游戏实现>>,不过沈大海老师的是在Xcode下实现的,自己看着视频在win7下实现:
(做这个游戏主要是熟练cocos2d-x的一些用法,所以游戏逻辑没怎么考虑,只简单实现了蛇吃食物,蛇变大,没有去检测边界碰撞以及自身碰撞)
直接附代码,代码注释在沈大海老师的基础上加了我自己的,已经很详细了:
首先AppDelegate.h:
#ifndef _APP_DELEGATE_H_
#define _APP_DELEGATE_H_
#include "cocos2d.h"
class AppDelegate : private cocos2d::Application
{
public:
AppDelegate();
virtual ~AppDelegate();
virtual bool applicationDidFinishLaunching();
virtual void applicationDidEnterBackground();
virtual void applicationWillEnterForeground();
};
#endif // _APP_DELEGATE_H_
AppDelegate.cpp
#include "AppDelegate.h"
#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
USING_NS_CC;
using namespace CocosDenshion;
AppDelegate::AppDelegate() {
}
AppDelegate::~AppDelegate()
{
}
bool AppDelegate::applicationDidFinishLaunching() {
// initialize director
auto director = Director::getInstance();
auto glview = director->getOpenGLView();
if(!glview) {
glview = GLView::create("My Game");
glview->setFrameSize(480,320);
director->setOpenGLView(glview);
}
// turn on display FPS
director->setDisplayStats(false);
// set FPS. the default value is 1.0/60 if you don't call this
director->setAnimationInterval(1.0 / 60);
// create a scene. it's an autorelease object
auto scene = HelloWorld::createScene();
// run
director->runWithScene(scene);
//开始播放背景音乐
SimpleAudioEngine::getInstance()->playBackgroundMusic("background.mp3");
return true;
}
// This function will be called when the app is inactive. When comes a phone call,it's be invoked too
void AppDelegate::applicationDidEnterBackground() {
Director::getInstance()->stopAnimation();
SimpleAudioEngine::getInstance()->pauseBackgroundMusic();
// if you use SimpleAudioEngine, it must be pause
// SimpleAudioEngine::getInstance()->pauseBackgroundMusic();
}
// this function will be called when the app is active again
void AppDelegate::applicationWillEnterForeground() {
Director::getInstance()->startAnimation();
SimpleAudioEngine::getInstance()->resumeBackgroundMusic();
// if you use SimpleAudioEngine, it must resume here
// SimpleAudioEngine::getInstance()->resumeBackgroundMusic();
}
主场景:HelloWorldScene.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
USING_NS_CC;
class HelloWorld : public Layer
{
public:
static Scene* createScene();//获取欢迎画面的Scene
CREATE_FUNC(HelloWorld);
virtual bool init();
void menuCallback(Ref* object);
};
#endif // __HELLOWORLD_SCENE_H__
HelloWorldScene.cpp
#include "HelloWorldScene.h"
#include "GameScene.h"
#include "GameAbout.h"
#include "SimpleAudioEngine.h"
using namespace CocosDenshion;
USING_NS_CC;
Scene* HelloWorld::createScene()
{
auto scene = Scene::create();
auto layer = HelloWorld::create();
scene->addChild(layer);
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
if ( !Layer::init() )
{
return false;
}
auto size = Director::getInstance()->getWinSize();
//添加背景
auto spriteBK = Sprite::create("menuback.png");
spriteBK->setPosition(Point(size.width/2,size.height/2));//居中放置背景图片
this->addChild(spriteBK);
//添加2个菜单类的条目
auto menuItemStart = MenuItemFont::create("Start",CC_CALLBACK_1(HelloWorld::menuCallback,this));
menuItemStart->setTag(1);
auto menuItemHelp = MenuItemFont::create("Help",CC_CALLBACK_1(HelloWorld::menuCallback,this));
menuItemHelp->setTag(2);
auto menu = Menu::create(menuItemStart,menuItemHelp,NULL);
menu->setPosition(Point::ZERO);
menuItemStart->setPosition(Point(size.width -menuItemStart->getContentSize().width-100,menuItemStart->getContentSize().height+10));
menuItemHelp->setPosition(Point(size.width -menuItemStart->getContentSize().width-10,menuItemHelp->getContentSize().height+10));
this->addChild(menu);
return true;
}
void HelloWorld::menuCallback(Ref* object)
{
auto target = (Node*)object;
Scene* scene;
switch(target->getTag()){
case 1:
scene=Game::createScene();
break;
case 2:
scene = GameHelp::createScene();
break;
default:
break;
}
Director::getInstance()->replaceScene(scene);
}
帮助界面:GameAbout.h
#ifndef __snakegame__GameAbout__
#define __snakegame__GameAbout__
#include "cocos2d.h"
USING_NS_CC; //相当于using namespace cocos2d
//帮助场景
class GameHelp:public Layer
{
public:
static Scene* createScene();
CREATE_FUNC(GameHelp);
virtual bool init();
void menuCallback(Ref* object);
};
#endif
GameAbout.cpp
#include "GameAbout.h"
#include "HelloWorldScene.h"
Scene* GameHelp::createScene(){
auto scene = Scene::create();
auto layer = GameHelp::create();
scene->addChild(layer);
return scene;
}
bool GameHelp::init()
{
if(!Layer::init())
{
return false;
}
auto size = Director::getInstance()->getWinSize();
//添加背景
auto spriteBK = Sprite::create("menuback.png");
spriteBK->setPosition(Point(size.width/2,size.height/2));
spriteBK->setOpacity(85);//????不知道的地方
this->addChild(spriteBK);
//帮助信息
auto labelScore = LabelTTF::create("help information","Helvetica",25);
labelScore->setPosition(Point(size.width-120,size.height-50));
this->addChild(labelScore);
//返回按钮
auto menuItemBack = MenuItemFont::create("BACK",CC_CALLBACK_1(GameHelp::menuCallback,this));
auto menu = Menu::create(menuItemBack,NULL);
menu->setPosition(Point::ZERO);
menuItemBack->setPosition(Point(size.width-menuItemBack->getContentSize().width-100,menuItemBack->getContentSize().height+10));
this->addChild(menu);
return true;
}
void GameHelp::menuCallback(Ref* object)
{
auto scene = HelloWorld::createScene();
Director::getInstance()->replaceScene(scene);
}
游戏实现界面:GameScene.h
//游戏画面
#ifndef __snakegame__GameScene__
#define __snakegame__GameScene__
#include "cocos2d.h"
USING_NS_CC;
//定义一个游戏的枚举类型来标识贪吃蛇的移动方向
enum class ENUM_DIR
{
DIR_UP,
DIR_DOWN,
DIR_LEFT,
DIR_RIGHT,
DIR_STOP
};
//蛇精灵
class SnakeNode:public Sprite//封装节点
{
public:
ENUM_DIR m_dir;//移动方向
int nodeType;//当前类型 1:蛇头2:身体3食物
int m_row, m_col; //当前节点的行列坐标
SnakeNode();
~SnakeNode();
static SnakeNode* create(int type);
virtual bool init(int type);
void setPositionRC(int row, int col);//设置节点的坐标
};
//游戏界面图层
class Game:public Layer{
public:
SnakeNode* spFood;//食物
SnakeNode* spHead;//蛇头
int m_score; //分
Game();
~Game();
Vector<SnakeNode*>allBody;//身体,身体长度是不确定的,不确定的数组利用向量Vector
static Scene* createScene();
CREATE_FUNC(Game);
virtual bool init();
void menuCallBack(Ref* object);
void gameLogic(float);
void newBody();//添加一个新的身体节点
void moveBody();//移动所有的身体节点
};
#endif
GameScene.cpp:
#include "GameScene.h"
#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
using namespace CocosDenshion;
Game::Game(): spFood(NULL),spHead(NULL)
{
}
Game::~Game(){}
Scene* Game::createScene()
{
auto scene = Scene::create();
auto layer = Game::create();
scene->addChild(layer);
return scene;
}
SnakeNode* SnakeNode::create(int type)
{
SnakeNode * pRet = new SnakeNode();
if(pRet && pRet->init(type) )
{
pRet->autorelease();
return pRet;
}
else
{
delete pRet;
pRet = NULL;
return NULL;
}
}
SnakeNode::SnakeNode() : m_row(0),m_col(0)
{
}
SnakeNode::~SnakeNode()
{
}
bool SnakeNode::init(int type)
{
if(!Sprite::init())
{
return false;
}
//根据类型不同初始化不同的纹理
switch(type)
{
case 1://蛇头
{
auto sprite = Sprite::create("redstar.png");
sprite->setAnchorPoint(Point::ZERO);//设置锚点
this->addChild(sprite);
m_dir = ENUM_DIR::DIR_RIGHT;//向右移动
}
break;
case 2://身体
{
auto sprite = Sprite::create("greenstar.png");
sprite->setAnchorPoint(Point::ZERO);
this->addChild(sprite);
}
m_dir = ENUM_DIR::DIR_STOP;
break;
case 3://食物
{
auto sprite = Sprite::create("yellowstar.png");
sprite->setAnchorPoint(Point::ZERO);
this->addChild(sprite);
}
m_dir = ENUM_DIR::DIR_STOP;
break;
}
return true;
}
void SnakeNode::setPositionRC(int row, int col)//设置节点的坐标
{
this->m_row = row;
this->m_col = col;
setPosition(Point(m_col*32,m_row*32));
}
bool Game::init()
{
if(!Layer::init())
{
return false;
}
//添加地图
auto draw = DrawNode::create();
draw->setAnchorPoint(Point::ZERO);
draw->setPosition(Point::ZERO);
this->addChild(draw);
for(int i = 0; i < 11; i++)
{
draw->drawSegment(Point(0,32*i),Point(320,32*i),1,Color4F(1,1,1,1));//添加10条横向的线条
draw->drawSegment(Point(32*i,0),Point(32*i,320),1,Color4F(1,1,1,1));//添加10条纵向的线条
}
//添加蛇头
spHead = SnakeNode::create(1);
spHead->setPositionRC(0,0);
this->addChild(spHead);
CCLog("spHead %d,%d",spHead->m_col,spHead->m_row);
//添加身体
//添加食物
spFood = SnakeNode::create(3);
int row = rand()%10;
int col = rand()%10;
spFood->setPositionRC(row,col);
CCLog(" spFood %d,%d",spFood->m_col,spFood->m_row);
this->addChild(spFood);
auto size = Director::getInstance()->getWinSize();
//添加背景
auto spriteBK = Sprite::create("menuback.png");
spriteBK->setPosition(Point(size.width/2,size.height/2));
spriteBK->setOpacity(75);
this->addChild(spriteBK);
//分数显示
m_score = 0;
auto labelScore = Label::create("Score is 0","宋体",25);
labelScore->setTag(110);
labelScore->setPosition(Point(size.width-80,size.height -50 ));
this->addChild(labelScore);
//返回按钮
auto menuItemBack=MenuItemFont::create("Back", CC_CALLBACK_1(Game::menuCallBack,this));
auto menu=Menu::create(menuItemBack,NULL);
menu->setPosition(Point::ZERO);
menuItemBack->setPosition(Point(size.width-menuItemBack->getContentSize().width-50,menuItemBack->getContentSize().height+10));
this->addChild(menu);
//计划任务
this->schedule(schedule_selector(Game::gameLogic),0.5);
//加入用户触摸事件监听
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);//不向下触摸,简单点来说,比如有两个sprite ,A 和 B,A在上B在下(位置重叠),触摸A的时候,B不会受到影响
//listener->setSwallowTouches(false)反之,向下传递触摸,触摸A也等于触摸了B
listener->onTouchBegan= [&](Touch* t, Event* e){
//改变贪吃蛇移动的方向
int col = t->getLocation().x/32;
int row = t->getLocation().y/32;
CCLog("your touchbegin %f,%f", t->getLocation().x,t->getLocation().y);
int spHeadCol = spHead->getPositionX()/32;//列
int spHeadRow = spHead->getPositionY()/32;//行
if(abs(spHeadCol - col)> abs(spHeadRow - row))//如果点击一个位置后该位置与蛇头相比列的差值大于行的差值,就执行左右移动
{
if (spHeadCol < col)//列影响的是左右移动
{
spHead->m_dir = ENUM_DIR::DIR_RIGHT;
}
else
{
spHead->m_dir = ENUM_DIR::DIR_LEFT;
}
}
else//行的差值大于列的差值
{
if(spHeadRow < row)//行影响的是上下的移动
{
spHead->m_dir = ENUM_DIR::DIR_UP;
}
else
{
spHead->m_dir = ENUM_DIR::DIR_DOWN;
}
}
return true;
};
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener,this);
return true;
}
void Game::gameLogic(float t)
{
moveBody();//移动身体所有节点
//蛇头移动
switch (spHead->m_dir)
{
case ENUM_DIR::DIR_RIGHT:
spHead->runAction(MoveBy::create(0.3,Point(32,0)));//向右移动32个像素,也就是向右移动一个单位
spHead->m_col++;
break;
case ENUM_DIR::DIR_LEFT:
spHead->runAction(MoveBy::create(0.3, Point(-32,0)));//向左移动32个像素
spHead->m_col--;
break;
case ENUM_DIR::DIR_DOWN:
spHead->runAction(MoveBy::create(0.3, Point(0,-32)));
spHead->m_row--;
break;
case ENUM_DIR::DIR_UP:
spHead->runAction(MoveBy::create(0.3, Point(0,32)));
spHead->m_row++;
break;
default:
break;
}
//碰撞检测
if(spHead->m_row==spFood->m_row&&
spHead->m_col==spFood->m_col)
{ //音效的播放
SimpleAudioEngine::getInstance()->playEffect("eat.wav");
//分数增加
this->m_score+=100;
Label * label=(Label *)this->getChildByTag(110);
char strscore[20];
sprintf(strscore, "Score is:%d",m_score);
label->setString(strscore);
//食物产生新的位置
int row=rand()%10;
int col=rand()%10;
spFood->setPositionRC(row,col);
//添加节点
newBody();
}
}
void Game::newBody()
{
auto bodynode = SnakeNode::create(2);//新建身体节点
//设置这个节点的方向和坐标
if(allBody.size()>0)//有身体节点
{
//最后一个身体的节点
auto lastbody = allBody.at(allBody.size()-1);
bodynode->m_dir = lastbody->m_dir;
switch (bodynode->m_dir)
{
case ENUM_DIR::DIR_UP:
bodynode->setPositionRC(lastbody->m_row-1,lastbody->m_col);
break;
case ENUM_DIR::DIR_DOWN:
bodynode->setPositionRC(lastbody->m_row+1, lastbody->m_col);
break;
case ENUM_DIR::DIR_LEFT:
bodynode->setPositionRC(lastbody->m_row, lastbody->m_col+1);
break;
case ENUM_DIR::DIR_RIGHT:
bodynode->setPositionRC(lastbody->m_row, lastbody->m_col-1);
break;
default:
break;
}
}else
{
bodynode->m_dir = spHead->m_dir;
switch (bodynode->m_dir)
{
case ENUM_DIR::DIR_UP:
bodynode->setPositionRC(spHead->m_row-1, spHead->m_col);
break;
case ENUM_DIR::DIR_DOWN:
bodynode->setPositionRC(spHead->m_row+1, spHead->m_col);
break;
case ENUM_DIR::DIR_LEFT:
bodynode->setPositionRC(spHead->m_row, spHead->m_col+1);
break;
case ENUM_DIR::DIR_RIGHT:
bodynode->setPositionRC(spHead->m_row, spHead->m_col-1);
break;
default:
break;
}
}
//添加节点到当前图层
this->addChild(bodynode);
//添加节点到集合中
allBody.pushBack(bodynode);
}
void Game::moveBody()//移动所有的身体节点
{
if(allBody.size() == 0)
{
return;
}
for (auto bodynode:allBody)
{
switch (bodynode->m_dir)
{
case ENUM_DIR::DIR_RIGHT:
bodynode->runAction(MoveBy::create(0.3, Point(32,0)));
bodynode->m_col++;
break;
case ENUM_DIR::DIR_LEFT:
bodynode->runAction(MoveBy::create(0.3, Point(-32,0)));
bodynode->m_col--;
break;
case ENUM_DIR::DIR_DOWN:
bodynode->runAction(MoveBy::create(0.3, Point(0,-32)));
bodynode->m_row--;
break;
case ENUM_DIR::DIR_UP:
bodynode->runAction(MoveBy::create(0.3, Point(0,32)));
bodynode->m_row++;
break;
default:
break;
}
}
//移动完成后,改变每个body的方向
for(int i = allBody.size()-1;i > 0; i--)
{
//每个节点的方向调整为它前一个节点的方向
allBody.at(i)->m_dir = allBody.at(i-1)->m_dir;
}
allBody.at(0)->m_dir = spHead->m_dir;
}
void Game::menuCallBack(Ref* object)
{
auto scene = HelloWorld::createScene();
Director::getInstance()->replaceScene(scene);
}
刚开始看着沈大海老师的视频,实现后蛇头与食物碰撞后没有反应,后面找到原因是蛇头没有初始化,但是沈老师的视频里面用的Xcode的确没有初始化,可能默认为0,而VS编译器检查严格一点,所以没有实现效果,要自己写出构造函数初始化它拥有的成员变量!!!