『cocos2d-x』贪食蛇实现


对照了网上某位大神的贪食蛇作品,自己重新练了一次手,继续熟练cocos2d-x。


主要游戏场景代码记录如下:

#include "GameScene.h"
#include "Settings.h"
#include "MainMenuScene.h"
#include "SimpleAudioEngine.h"


USING_NS_CC;
using namespace CocosDenshion;

#define PTM_RATIO 32

CCScene* GameScene::scene()
{
	CCScene *scene = CCScene::create();
	GameScene *layer = GameScene::create();
	scene->addChild(layer);

	return scene;
}

bool GameScene::init()
{
	bool bRet = false;
	do 
	{
		if(!CCLayer::init())
		{
			return false;
		}
		Settings::load();
		background = CCSprite::create("background.png");
		CC_BREAK_IF(!background);
		background->setPosition(ccp(Settings::screenWidth/2, Settings::screenHeight/2));
		this->addChild(background, 0);

		ccDrawLine(ccp(0, 416), ccp(320, 416));

		btnPause = CCSprite::create("buttons.png", CCRectMake(64, 128, 64, 64));
		CC_BREAK_IF(!btnPause);
		btnPause->setPosition(ccp(32, Settings::screenHeight - 32));
		btnPause->setVisible(false);
		this->addChild(btnPause, 1);

		btnClose = CCSprite::create("buttons.png", CCRectMake(0, 128, 64, 64));
		CC_BREAK_IF(!btnClose);
		btnClose->setPosition(ccp(Settings::screenWidth/2, 232));
		btnClose->setVisible(false);
		this->addChild(btnClose, 1);

		btnLeft = CCSprite::create("buttons.png", CCRectMake(64, 64, 64, 64));
		CC_BREAK_IF(!btnLeft);
		btnLeft->setPosition(ccp(32, 32));
		btnLeft->setVisible(false);
		this->addChild(btnLeft, 1);

		btnRight = CCSprite::create("buttons.png", CCRectMake(0, 64, 64, 64));
		CC_BREAK_IF(!btnRight);
		btnRight->setPosition(ccp(Settings::screenWidth-32, 32));
		btnRight->setVisible(false);
		this->addChild(btnRight, 1);

		readyMenu = CCSprite::create("ready.png");
		CC_BREAK_IF(!readyMenu);
		readyMenu->setPosition(ccp(Settings::screenWidth/2, Settings::screenHeight-148));
		this->addChild(readyMenu, 1);

		pauseMenu=CCSprite::create("pausemenu.png");
		CC_BREAK_IF(!pauseMenu);
		pauseMenu->setPosition(ccp(Settings::screenWidth/2, Settings::screenHeight-148));
		pauseMenu->setVisible(false);
		this->addChild(pauseMenu, 1);

		gameoverMenu=CCSprite::create("gameover.png");
		CC_BREAK_IF(!gameoverMenu);
		gameoverMenu->setPosition(ccp(Settings::screenWidth/2, Settings::screenHeight-125));
		gameoverMenu->setVisible(false);
		this->addChild(gameoverMenu, 1);

		//初始化游戏数据
		world = new World();
		CC_BREAK_IF(!world);
		spriteBatch = CCSpriteBatchNode::create("snake.png");
		CC_BREAK_IF(!spriteBatch);
		this->addChild(spriteBatch);
		CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("snake.plist");

		Snake* snake = world->snake;
		CC_BREAK_IF(!snake);
		CCArray* partArr = snake->partArr;
		CC_BREAK_IF(!partArr);
		int len = partArr->count();
		snakeArr = CCArray::createWithCapacity(len);
		snakeArr->retain();
		SnakePart* part = (SnakePart*)partArr->objectAtIndex(0);
		CCSprite* headSprite = CCSprite::createWithSpriteFrameName("headup.png");
		CC_BREAK_IF(!headSprite);
		headSprite->setPosition(ccp(part->x*PTM_RATIO+PTM_RATIO/2, Settings::screenHeight-part->y*PTM_RATIO+PTM_RATIO/2));
		headSprite->setVisible(false);
		snakeArr->addObject(headSprite);
		spriteBatch->addChild(headSprite);

		for(int i=1; i<len; i++)
		{
			part = (SnakePart*)world->snake->partArr->objectAtIndex(i);
			CCSprite* partSprite = CCSprite::createWithSpriteFrameName("tail.png");
			partSprite->setPosition(ccp(part->x*PTM_RATIO+PTM_RATIO/2, Settings::screenHeight-part->y*PTM_RATIO-PTM_RATIO/2)); //??
			partSprite->setVisible(false);
			snakeArr->addObject(partSprite);
			spriteBatch->addChild(partSprite);
		}

		//食物来啦stain
		spStain = CCSprite::createWithSpriteFrameName("stain1.png");
		CC_BREAK_IF(!spStain);
		spStain->setPosition(ccp(world->stain->x*PTM_RATIO + PTM_RATIO/2, Settings::screenHeight - world->stain->y*PTM_RATIO-PTM_RATIO/2));//??
		spriteBatch->addChild(spStain);

		//分数
		memset(score, 0x00, 16);
		sprintf_s(score, 16, "score: %d", 0);				//学习了
		lbScore = CCLabelTTF::create(score, "Arial", 32);
		CC_BREAK_IF(!lbScore);
		lbScore->setPosition(ccp(Settings::screenWidth/2, 32));
		this->addChild(lbScore, 1);

		//音效
		SimpleAudioEngine::sharedEngine()->preloadEffect("click.mp3");
		SimpleAudioEngine::sharedEngine()->preloadEffect("eat.mp3");

		gs = kReady;
		oldScore = 0;
		gapTime = 0;

		//按钮区域
		btnPauseBounds = CCRectMake(0, Settings::screenHeight-64, 64, 64);
		btnCloseBounds = CCRectMake(Settings::screenWidth/2-32, 200, 64, 64);
		btnLeftBounds = CCRectMake(0, 0, 64, 64);
		btnRightBounds = CCRectMake(Settings::screenWidth-64, 0, 64, 64);
		resumeBounds = CCRectMake(Settings::screenWidth/2-80, Settings::screenHeight-148, 160, 48);
		quitBounds = CCRectMake(Settings::screenWidth/2-80, Settings::screenHeight-196, 160, 48);


		this->setTouchEnabled(true);
		this->scheduleUpdate();

		bRet = true;
	} while (0);
	
	return bRet;
}

void GameScene::update(float dt)
{
	if(gs == kReady)
		updateReady(dt);
	if(gs == kRunning)
		updateRunning(dt);
	if(gs == kPause)
		updatePause(dt);
	if(gs == kGameOver)
		updateGameOver(dt);
	gapTime += dt;
	if(gapTime > 0.2f)
	{
		if((GetAsyncKeyState(VK_LEFT) & 0x8000) == 0x8000)
		{
			world->snake->turnLeft();
			gapTime = 0.0f;
		}
		if((GetAsyncKeyState(VK_RIGHT) & 0x8000) == 0x8000)
		{
			world->snake->turnRight();
			gapTime = 0.0f;
		}
	}
}
	

void GameScene::updateReady(float dt)
{
	btnPause->setVisible(false);
	btnClose->setVisible(false);
	btnLeft->setVisible(false);
	btnRight->setVisible(false);
	readyMenu->setVisible(true);
	pauseMenu->setVisible(false);
	gameoverMenu->setVisible(false);
	this->setSnakeVisible(false);
}

void GameScene::updateRunning(float dt)
{
	btnPause->setVisible(true);
	btnClose->setVisible(false);
	btnLeft->setVisible(true);
	btnRight->setVisible(true);
	readyMenu->setVisible(false);
	pauseMenu->setVisible(false);
	gameoverMenu->setVisible(false);
	this->setSnakeVisible(true);
	this->drawStain();
	this->drawSnake();
	world->update(dt);
	if(world->bGameOver)
	{
		gs = kGameOver;
	}
}

void GameScene::updatePause(float dt)
{
	btnPause->setVisible(false);
	btnClose->setVisible(false);
	btnLeft->setVisible(false);
	btnRight->setVisible(false);
	readyMenu->setVisible(false);
	pauseMenu->setVisible(true);
	gameoverMenu->setVisible(false);
	this->setSnakeVisible(false);
}

void GameScene::updateGameOver(float dt)
{
	btnPause->setVisible(false);
	btnClose->setVisible(true);
	btnLeft->setVisible(false);
	btnRight->setVisible(false);
	readyMenu->setVisible(false);
	pauseMenu->setVisible(false);
	gameoverMenu->setVisible(true);
	this->setSnakeVisible(false);

}

void GameScene::setSnakeVisible(bool bVisible)
{
	int len = snakeArr->count();
	for(int i=0;i<len;i++)
	{
		CCSprite *snakeSprite = (CCSprite*)snakeArr->objectAtIndex(i);
		snakeSprite->setVisible(bVisible);
	}
}

void GameScene::ccTouchesEnded(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent)
{
	CCTouch* pTouch = (CCTouch*)pTouches->anyObject();
	CCPoint touchPoint = CCDirector::sharedDirector()->convertToGL(pTouch->getLocationInView());

	if(gs == kReady)
	{
		gs = kRunning;
	}
	if(gs == kRunning)
	{
		if(btnLeftBounds.containsPoint(touchPoint))
		{
			world->snake->turnLeft();
			SimpleAudioEngine::sharedEngine()->playEffect("click.mp3",false);
		}
		if(btnRightBounds.containsPoint(touchPoint))
		{
			world->snake->turnRight();
			SimpleAudioEngine::sharedEngine()->playEffect("click.mp3",false);
		}
		if(btnPauseBounds.containsPoint(touchPoint))
		{
			gs = kPause;
		}
	}
	if(gs == kPause)
	{
		if(resumeBounds.containsPoint(touchPoint))
		{
			gs = kRunning;
		}
		if(quitBounds.containsPoint(touchPoint))
		{
			gs = kGameOver;
			this->snakeArr->removeAllObjects();
			free(world);
			CCDirector::sharedDirector()->replaceScene(CCTransitionFade::create(1.0f, MainMenuScene::scene()));
			return;
		}
	}
	if(gs == kGameOver)
	{
		if(btnCloseBounds.containsPoint(touchPoint))
		{
			if(oldScore!=world->score)
			{
				oldScore=world->score;
				sprintf_s(score, 16, "score: %d", oldScore);
				SimpleAudioEngine::sharedEngine()->playEffect("eat.mp3",false);
				lbScore->setString(score);
				if(oldScore > Settings::highScores[4])
				{
					Settings::highScores[4] = oldScore;
					for(int i=4; i>0; i--)
					{
						for(int j=i-1; j>=0; j--)
						{
							if(Settings::highScores[j] < Settings::highScores[i])
							{
								int tmp = Settings::highScores[i];
								Settings::highScores[i] = Settings::highScores[j];
								Settings::highScores[j] = tmp;
							}
						}
					}
					Settings::save();
				}
			}
			this->snakeArr->removeAllObjects();
			free(world);
			world = new World();
			gs = kReady;
		}
	}

}

void GameScene::drawSnake()
{
	Snake* snake = world->snake;
	CCArray* partArr = snake->partArr;
	int len = partArr->count();
	int slen = snakeArr->count();

	if(oldScore!=world->score)
	{
		oldScore=world->score;
		sprintf_s(score, 16, "score: %d", oldScore);
		SimpleAudioEngine::sharedEngine()->playEffect("eat.mp3",false);
		lbScore->setString(score);
	}

	if(slen < len)
	{
		CCSprite* spTail = CCSprite::createWithSpriteFrameName("tail.png");
		snakeArr->addObject(spTail);
		spriteBatch->addChild(spTail);
	}

	SnakePart* head = (SnakePart*)partArr->objectAtIndex(0);
	CCSprite* spHead = (CCSprite*)snakeArr->objectAtIndex(0);
	if(snake->direction == Snake::UP) //蛇脑袋的方向不一样,图也不一样哦
	{
		CCSpriteFrame *frame=CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName("headup.png");
		spHead->setDisplayFrame(frame);
	}
	else if(snake->direction==Snake::DOWN)
	{
		CCSpriteFrame *frame=CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName("headdown.png");
		spHead->setDisplayFrame(frame);
	}
	else if(snake->direction==Snake::LEFT)
	{
		CCSpriteFrame *frame=CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName("headleft.png");
		spHead->setDisplayFrame(frame);
	}
	else if(snake->direction==Snake::RIGHT)
	{
		CCSpriteFrame *frame=CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName("headright.png");
		spHead->setDisplayFrame(frame);
	}

	for(int i=0;i<slen;i++)
	{
		SnakePart* part = (SnakePart*)partArr->objectAtIndex(i);
		CCSprite* spPart = (CCSprite*)snakeArr->objectAtIndex(i);
		spPart->setPosition(ccp(part->x*PTM_RATIO+PTM_RATIO/2, Settings::screenHeight-part->y*PTM_RATIO-PTM_RATIO/2));
	}
}

void GameScene::drawStain()
{
	Stain* st = world->stain;
	if(st->type == Stain::TYPE_0)
	{
		CCSpriteFrame* frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName("stain1.png");
		spStain->setDisplayFrame(frame);
	} else if(st->type == Stain::TYPE_1)
	{
		CCSpriteFrame* frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName("stain2.png");
		spStain->setDisplayFrame(frame);
	} else if(st->type == Stain::TYPE_2)
	{
		CCSpriteFrame* frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName("stain3.png");
		spStain->setDisplayFrame(frame);
	}
	spStain->setPosition(ccp(st->x*PTM_RATIO+PTM_RATIO/2, Settings::screenHeight-st->y*PTM_RATIO-PTM_RATIO/2));
}


参考文章: http://www.linuxidc.com/Linux/2012-08/67901.htm

运行文件和源代码地址

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是用 C++ 和 cocos2d-x 开发的代码示例: 首先,我们需要创建一个 Snake 类,用于管理的运动和状态: ```c++ class Snake : public cocos2d::Node { public: virtual bool init() override; CREATE_FUNC(Snake); void move(); // 移动 void turn(Direction direction); // 转向 bool isDead() const; // 是否死亡 private: std::vector<cocos2d::Sprite*> m_body; // 身体 Direction m_direction; // 运动方向 bool m_isDead; // 是否死亡 int m_gridWidth; // 网格宽度 }; ``` 在 Snake 类中,我们定义了以下成员变量: - m_body:存储身体的精灵数组; - m_direction:表示当前的运动方向; - m_isDead:表示是否已经死亡; - m_gridWidth:网格宽度,即每个格子的大小。 接下来,我们需要实现 Snake 类的成员函数: ```c++ bool Snake::init() { if (!Node::init()) { return false; } // 初始化 m_direction = Direction::RIGHT; m_isDead = false; m_gridWidth = 10; // 添加头 auto head = cocos2d::Sprite::create("snake_head.png"); head->setPosition(cocos2d::Vec2(0, 0)); addChild(head); m_body.push_back(head); // 添加身 for (int i = 1; i < 4; i++) { auto body = cocos2d::Sprite::create("snake_body.png"); body->setPosition(cocos2d::Vec2(-i * m_gridWidth, 0)); addChild(body); m_body.push_back(body); } return true; } void Snake::move() { if (m_isDead) { return; } // 移动身体 for (int i = m_body.size() - 1; i > 0; i--) { auto prevPos = m_body[i - 1]->getPosition(); m_body[i]->setPosition(prevPos); } // 移动头部 auto headPos = m_body[0]->getPosition(); switch (m_direction) { case Direction::UP: headPos.y += m_gridWidth; break; case Direction::DOWN: headPos.y -= m_gridWidth; break; case Direction::LEFT: headPos.x -= m_gridWidth; break; case Direction::RIGHT: headPos.x += m_gridWidth; break; } m_body[0]->setPosition(headPos); } void Snake::turn(Direction direction) { if (m_isDead) { return; } if ((int)direction + (int)m_direction == 0) { return; } m_direction = direction; } bool Snake::isDead() const { return m_isDead; } ``` 在 init 函数中,我们初始化了的状态,并添加了头和身。 在 move 函数中,我们首先移动了身,然后根据当前的运动方向移动了头。 在 turn 函数中,我们检查了新的方向是否与当前方向相反,如果是,则不做任何操作。 在 isDead 函数中,我们简单地返回 m_isDead 的值。 接下来,我们需要创建一个 GameLayer 类,用于管理游戏的逻辑和界面: ```c++ class GameLayer : public cocos2d::Layer { public: virtual bool init() override; CREATE_FUNC(GameLayer); void update(float delta) override; // 帧更新函数 private: void createFood(); // 创建物 bool checkCollision(); // 检查碰撞 Snake* m_snake; // cocos2d::Sprite* m_food; // 物 int m_gridWidth; // 网格宽度 float m_interval; // 更新间隔 }; ``` 在 GameLayer 类中,我们定义了以下成员变量: - m_snake:对象; - m_food:物对象; - m_gridWidth:网格宽度,即每个格子的大小; - m_interval:更新间隔,即每隔多少秒更新一次的状态。 接下来,我们需要实现 GameLayer 类的成员函数: ```c++ bool GameLayer::init() { if (!Layer::init()) { return false; } // 初始化 m_gridWidth = 10; m_interval = 0.1f; // 添加 m_snake = Snake::create(); m_snake->setPosition(cocos2d::Vec2(0, 0)); addChild(m_snake); // 创建物 createFood(); // 注册键盘事件 auto listener = cocos2d::EventListenerKeyboard::create(); listener->onKeyPressed = [=](cocos2d::EventKeyboard::KeyCode keyCode, cocos2d::Event* event) { switch (keyCode) { case cocos2d::EventKeyboard::KeyCode::KEY_UP_ARROW: m_snake->turn(Direction::UP); break; case cocos2d::EventKeyboard::KeyCode::KEY_DOWN_ARROW: m_snake->turn(Direction::DOWN); break; case cocos2d::EventKeyboard::KeyCode::KEY_LEFT_ARROW: m_snake->turn(Direction::LEFT); break; case cocos2d::EventKeyboard::KeyCode::KEY_RIGHT_ARROW: m_snake->turn(Direction::RIGHT); break; default: break; } }; _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this); // 开始更新 scheduleUpdate(); return true; } void GameLayer::update(float delta) { m_interval -= delta; if (m_interval <= 0) { m_snake->move(); // 检查碰撞 if (checkCollision()) { m_snake->stopAllActions(); m_snake->setScale(1.0f); m_snake->runAction(cocos2d::Blink::create(2, 5)); m_snake->setIsDead(true); return; } // 检查是否吃到物 auto headPos = m_snake->getPosition(); auto foodPos = m_food->getPosition(); if (headPos.distance(foodPos) < m_gridWidth) { m_food->removeFromParent(); createFood(); auto body = cocos2d::Sprite::create("snake_body.png"); body->setPosition(m_snake->getChildByName("tail")->getPosition()); m_snake->addChild(body); } m_interval = 0.1f; } } void GameLayer::createFood() { // 随机创建物 auto visibleSize = cocos2d::Director::getInstance()->getVisibleSize(); auto origin = cocos2d::Director::getInstance()->getVisibleOrigin(); int x = (int)origin.x + m_gridWidth + rand() % ((int)visibleSize.width - 2 * m_gridWidth); int y = (int)origin.y + m_gridWidth + rand() % ((int)visibleSize.height - 2 * m_gridWidth); m_food = cocos2d::Sprite::create("snake_food.png"); m_food->setPosition(cocos2d::Vec2(x, y)); addChild(m_food); } bool GameLayer::checkCollision() { // 检查是否碰到边界 auto visibleSize = cocos2d::Director::getInstance()->getVisibleSize(); auto origin = cocos2d::Director::getInstance()->getVisibleOrigin(); auto headPos = m_snake->getPosition(); if (headPos.x < origin.x || headPos.y < origin.y || headPos.x > origin.x + visibleSize.width || headPos.y > origin.y + visibleSize.height) { return true; } // 检查是否碰到自己 for (int i = 1; i < m_snake->getChildrenCount() - 1; i++) { auto bodyPos = m_snake->getChildByTag(i)->getPosition(); if (headPos.distance(bodyPos) < m_gridWidth) { return true; } } return false; } ``` 在 init 函数中,我们初始化了游戏状态,并添加了物。此外,我们还注册了键盘事件,并开始更新。 在 update 函数中,我们首先检查更新间隔是否已经到达,然后移动,并检查是否碰到边界或自己。如果碰到了,则设置为死亡状态,并返回。如果吃到了物,则创建一个新的身体部分,并重新生成物。最后,重置更新间隔。 在 createFood 函数中,我们随机生成一个物,并添加到场景中。 在 checkCollision 函数中,我们检查是否碰到了边界或自己。如果碰到了,则返回 true,否则返回 false。 最后,在场景中添加 GameLayer 类的实例,即可运行游戏。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值