『cocos2d-x』diamond hunter宝石猎手

哈,山寨又来了。这次用到是pop cap的宝石迷阵资源。写了个简单的玩法,呵呵,其实是因为算法比较简单大笑。还是记录一下。

先下载pop cap的Bejeweled,找到资源。巧妇难为无米之炊,有了资源,就好动手鸟。

我选择了其中的一部分资源,练练手,完全仿出来就算了吧。pop cap的游戏就是好啊,资源不加密的,太方便了,这里我们用到Audio和Graphics里的部分资源。

既然是宝石消除类游戏,当然宝石就是猪脚了。宝石消失,宝石移动,宝石的颜色,这些都需要一堆数据。所以我们这里继承CCSprite创建宝石类CCBlock:

#pragma once
#include "Defines.h"
#include "cocos2d.h"

USING_NS_CC;

class CCBlock : public cocos2d::CCSprite
{
public:
	void removeSelf()
	{
		this->removeFromParentAndCleanup(true);
	}

public:
	CC_SYNTHESIZE(bool, _willRemove, WillRemove);
	CC_SYNTHESIZE(int, _moveStep, MoveStep);
	CC_SYNTHESIZE(JewelType, _type, Type);

	CREATE_FUNC(CCBlock);
	bool init();
};

在cpp中其实就是初始化

#include "CCBlock.h"

bool CCBlock::init()
{
	bool bRet = false;
	do 
	{
		int type = rand()%7 + 1;
		CCString* s = CCString::createWithFormat("Jewel%d.png", type);
		CC_BREAK_IF(!CCSprite::initWithFile(s->getCString()));
		this->setWillRemove(false);
		this->setMoveStep(0);
		this->setType((JewelType)type);
		bRet = true;
	} while (0);

	return bRet;
}

宝石类CCBlock中其实就包含JewelType宝石的种类,宝石是否要消除_willRemove,宝石要移动的步数_moveStep几个属性,我这里初始化种类是随机的。

然后就是简单的开始游戏场景,用了pop cap的资源和自己在网上搜到的图片PS了一下拼装的,比较简陋。


就一个简单的动态图,和play按钮。

按下play,“go”直接到主场景游戏界面吧。


其实也很简陋,哈哈,凑合看吧。

#include "GameLayer.h"
#include "CCBlock.h"
#include "TitleLayer.h"
#include "SimpleAudioEngine.h"

using namespace cocos2d;
using namespace CocosDenshion;

CCScene* GameLayer::scene()
{
	CCScene* scene = NULL;
	do 
	{
		scene = CCScene::create();
		CC_BREAK_IF(!scene);

		GameLayer *layer = GameLayer::create();
		CC_BREAK_IF(!layer);

		scene->addChild(layer);
	} while (0);

	return scene;
}

bool GameLayer::init()
{
	bool bRet = false;
	do 
	{
		CC_BREAK_IF(! CCLayer::init());
		this->setTouchEnabled(true);

		//载入音效
		SimpleAudioEngine::sharedEngine()->preloadBackgroundMusic("Audio/BGM/classic.mp3");
		SimpleAudioEngine::sharedEngine()->preloadEffect("Audio/SE/combo.mp3");
		SimpleAudioEngine::sharedEngine()->preloadEffect("Audio/SE/voice_good.mp3");
		SimpleAudioEngine::sharedEngine()->preloadEffect("Audio/SE/voice_excellent.mp3");
		SimpleAudioEngine::sharedEngine()->preloadEffect("Audio/SE/voice_awesome.mp3");
		SimpleAudioEngine::sharedEngine()->preloadEffect("Audio/SE/voice_spectacular.mp3");
		SimpleAudioEngine::sharedEngine()->preloadEffect("Audio/SE/voice_extraordinary.mp3");
		SimpleAudioEngine::sharedEngine()->preloadEffect("Audio/SE/voice_unbelievable.mp3");
		SimpleAudioEngine::sharedEngine()->preloadEffect("Audio/SE/levelcomplete.mp3");
		SimpleAudioEngine::sharedEngine()->preloadEffect("Audio/SE/gameover.mp3");

		SimpleAudioEngine::sharedEngine()->playBackgroundMusic("Audio/BGM/classic.mp3", true);

		blood = 0.0f;
		score = 60;
		level++;
		levelRatio = 1.8f - level * 0.2f;	

		loadTexture();
		initBlocks();
		bCanTouch = true;

		scheduleUpdate();
		schedule(SEL_SCHEDULE(&GameLayer::updateBlood), 1.0f);

		bRet = true;
	} while (0);

	return bRet;
}

void GameLayer::update(float dt)
{
	if(blood >=1.0f)
	{
		completeSprite->setVisible(true);
		CCMoveBy *moveBy = CCMoveBy::create(1.0f, ccp(0, 300));
		CCFadeOut *fadeOut = CCFadeOut::create(1.0f);
		score = 60;
		blood = 0.0f;
		SimpleAudioEngine::sharedEngine()->stopAllEffects();
		SimpleAudioEngine::sharedEngine()->playEffect("Audio/SE/levelcomplete.mp3");
		CCCallFunc* completeCallBack = CCCallFunc::create(this, callfunc_selector(GameLayer::restartLevel));
		completeSprite->runAction(CCSequence::create(CCSpawn::create(moveBy, fadeOut, NULL), completeCallBack, NULL));
	}
}

void GameLayer::initBlocks()  //初始化宝石的tag和位置
{
	srand(time(NULL));
	for(int i=0; i<BLOCKHEIGHT; i++)
		for(int j=0; j<BLOCKWIDTH; j++)
		{
			CCBlock* block = CCBlock::create();
			block->setPosition(ccp(313 + 60*j, 104 + 60*i));
			block->setTag(1000 + (i+1)*8 + j +1);
			block->retain();
			this->addChild(block, 10);
			BlockArr->addObject(block);
		}
}

void GameLayer::loadTexture()  //ui方面的初始化和时间处理
{
	CCSize winSize = CCDirector::sharedDirector()->getWinSize();
	CCSprite *bg = CCSprite::create(CCString::createWithFormat("Backgrounds/back_%d.jpg", rand()%19 + 1)->getCString());
	bg->setPosition(ccp(winSize.width * 0.5f, winSize.height * 0.5f));
	this->addChild(bg, -11);

	CCSprite *box = CCSprite::create("UI/box.png");
	box->setPosition(ccp(winSize.width * 0.5f + 120, winSize.height * 0.5f));
	this->addChild(box, -9);

	hpSprite = CCSprite::create("UI/bottombar_blue.png");	
	hpSprite->setAnchorPoint(ccp(0, 0));
	hpSprite->setPosition(ccp(309, 49));
	hpSprite->setScaleX(blood);
	this->addChild(hpSprite, -10);

	BlockArr = CCArray::create();
	BlockArr->retain();

	completeSprite = CCSprite::create("UI/levelcomplete.png");
	completeSprite->setPosition(ccp(523, 200));
	this->addChild(completeSprite, 3);
	completeSprite->setVisible(false);

	CCSprite* menuUI = CCSprite::create("UI/window_score.png");
	menuUI->setPosition(ccp(120, 438));
	this->addChild(menuUI, -1);

	labelAtlas = CCLabelAtlas::create("60", "fps_images.png", 12, 32, '.');
	labelAtlas->setPosition(ccp(105, 500));
	this->addChild(labelAtlas, 1);

	labelLevel = CCLabelAtlas::create("1", "fps_images.png", 12, 32, '.');
	labelLevel->setPosition(ccp(113, 468));
	labelLevel->setString(CCString::createWithFormat("%d", level)->getCString());
	this->addChild(labelLevel, 1);

	CCSprite* menuBox = CCSprite::create("UI/menu_box_added.png");
	menuBox->setPosition(ccp(120, 130));
	this->addChild(menuBox, -2);

	CCMenuItemImage *menuItem = CCMenuItemImage::create("UI/menu_d.png",
												"UI/menu_d.png",
												this,
												menu_selector(GameLayer::menuCallBack));
	CCMenu *menu = CCMenu::createWithItem(menuItem);
	menu->setPosition(ccp(120, 130));
	this->addChild(menu, -1);
}

void GameLayer::ccTouchesBegan(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent){

}

void GameLayer::ccTouchesEnded(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent){
	//每次点击都判断是那个宝石被击中,然后做updateState和updateMoveStep的操作
	if(bCanTouch)
	{
		CCTouch* pTouch = (CCTouch*)pTouches->anyObject();
		CCPoint pos = CCDirector::sharedDirector()->convertToGL(pTouch->getLocationInView());

		int x = (pos.x - 283) / 60 + 1;
		int y = (pos.y - 74) / 60 + 1;

		if(x >= 1 && y >= 1 && x <= 8 && y <= 8)
		{
			bCanTouch = false;
			CCBlock *block = getBlockByTag(1000 + y*8 + x);
			if(block){
				JewelType type = block->getType();
				removeTotal = 0;
				updateState(x, y, type);
				updateMoveStep();
			}
		}
	}
}


void GameLayer::updateState(int x, int y, JewelType type)  //递归遍历周边相同颜色宝石,找到要消失的宝石
{
	if(x<=0 || x>=9 || y<=0 || y>=9)
		return;
	CCBlock *block = getBlockByTag(1000+8*y+x);
	if(block->getWillRemove())
		return;
	if(block && block->getType() == type)
	{
		block->setWillRemove(true);
		removeTotal ++;
		updateState(x-1, y, type);
		updateState(x+1, y, type);
		updateState(x, y-1, type);
		updateState(x, y+1, type);
	}
}

void GameLayer::updateMoveStep()  //根据要消失的宝石计算每个宝石要移动的步数step
{
	for(int i=1; i<9; i++)
	{
		int step = 0;
		for(int j=1; j<9; j++)
		{
			CCBlock *block = getBlockByTag(1000+j*8+i);
			CC_BREAK_IF(!block);
			if(block->getWillRemove())
			{
				step++;
			}
			block->setMoveStep(step);
		}
	}

	int newStep[8] = {0};	//需新建的个数
	for(int i=1065; i<1073; i++)
	{
		CCBlock *block = getBlockByTag(i);
		CC_BREAK_IF(!block);
		newStep[i-1065] = block->getMoveStep();
	}

	for(int i=1009; i<1073; i++)
	{
		CCBlock* block = getBlockByTag(i);
		if(block->getWillRemove())
		{
			CCScaleTo* scaleTo1 = CCScaleTo::create(0.04f, 1.1f);
			CCScaleTo* scaleTo2 = CCScaleTo::create(0.12f, 0.5f);
			block->runAction(CCSequence::create(scaleTo1, 
											scaleTo2, 
											CCCallFuncN::create(this, callfuncN_selector(GameLayer::deleteBlock)),
											NULL));
		}
		else if(block->getMoveStep())
			block->setTag(block->getTag()-block->getMoveStep()*8);
	}

	SimpleAudioEngine::sharedEngine()->playEffect("Audio/SE/combo.mp3");
	if(removeTotal >= 4 && removeTotal <5)
		SimpleAudioEngine::sharedEngine()->playEffect("Audio/SE/voice_good.mp3");
	else if(removeTotal >= 5 && removeTotal <6)
		SimpleAudioEngine::sharedEngine()->playEffect("Audio/SE/voice_excellent.mp3");
	else if(removeTotal >= 6 && removeTotal <7)
		SimpleAudioEngine::sharedEngine()->playEffect("Audio/SE/voice_awesome.mp3");
	else if(removeTotal >= 7 && removeTotal <8)
		SimpleAudioEngine::sharedEngine()->playEffect("Audio/SE/voice_spectacular.mp3");
	else if(removeTotal >= 8 && removeTotal <9)
		SimpleAudioEngine::sharedEngine()->playEffect("Audio/SE/voice_extraordinary.mp3");
	else if(removeTotal >= 9)
		SimpleAudioEngine::sharedEngine()->playEffect("Audio/SE/voice_unbelievable.mp3");

	//分数变化
	switch(removeTotal)
	{
	case 1:
		blood -= 0.03f;
		break;
	case 2:
		blood += 0.01f * levelRatio;
		break;
	case 3:
		blood += 0.02f * levelRatio;
		break;
	case 4:
		createMsg(1);
		blood += 0.03f * levelRatio;
		break;
	case 5:
		createMsg(2);
		blood += 0.04f * levelRatio;
		break;
	case 6:
		createMsg(3);
		blood += 0.05f * levelRatio;
		break;
	case 7:
		createMsg(4);
		blood += 0.06f * levelRatio;
		break;
	case 8:
		createMsg(5);
		blood += 0.07f * levelRatio;
		break;
	case 9:
		createMsg(6);
		blood += 0.08f * levelRatio;
		break;
	default:
		createMsg(6);
		blood += 0.09f * levelRatio;
		break;
	}
	if(blood < 0)
		blood = 0;
	hpSprite->setScaleX(blood);

	for(int i=0; i<8; i++)	//生成新的宝石类,并移动到正确的位置
	{
		for(int j=newStep[i]; j>0; j--)
		{
			CCBlock* block = CCBlock::create();
			block->setPosition(ccp(313 + 60*i, 104 + 420 + 60*j));
			block->setMoveStep(newStep[i]);
			block->setWillRemove(false);
			block->retain();
			this->addChild(block, 10);
			BlockArr->addObject(block);
		}
	}
	hpSprite->setScaleX(blood);
	CCString* scoreStr = CCString::createWithFormat("%d", score);
	labelAtlas->setString(scoreStr->getCString());

	CCObject* pObj;
	int maxStep = 0;
	CCARRAY_FOREACH(BlockArr, pObj)
	{
		CCBlock* block = (CCBlock*)pObj;
		if(int step = block->getMoveStep())
		{
			if(step > maxStep)
				maxStep = step;
			CCDelayTime* delayTime = CCDelayTime::create(0.2f);
			CCMoveBy* moveBy = CCMoveBy::create(0.1f * step, ccp(0, -step * 60));
			block->runAction(CCSequence::create(delayTime, moveBy, NULL));
		}
	}
	this->runAction(CCSequence::create(CCDelayTime::create(maxStep * 0.1f + 0.21f),
						CCCallFunc::create(this, callfunc_selector(GameLayer::touchCallBack)), NULL));
}

void GameLayer::touchCallBack()  //每次消除后,重新设定宝石的tag等状态
{
	CCObject* pObj;	
 	CCARRAY_FOREACH(BlockArr, pObj)
 	{
 		CCBlock* block = (CCBlock*)pObj;
 		CCPoint pos = block->getPosition();	
		int x = (pos.x - 283) / 60 + 1;
		int y = (pos.y - 74) / 60 + 1;
 		block->setMoveStep(0);
		block->setTag(1000 + y*8 + x);
 		block->setWillRemove(false);
 	}
	bCanTouch = true;
}

void GameLayer::menuCallBack(CCObject *pObj)
{
	CCDirector::sharedDirector()->replaceScene(CCTransitionFade::create(1.0f, TitleLayer::scene()));
	level = 0;
}

void GameLayer::updateBlood(float dt)
{
	score -= 1;
	if(score <=0)
	{
		score = 0;
		if(blood < 1.0f)
		{
			this->unschedule(schedule_selector(GameLayer::updateBlood));
			level = 0;
			SimpleAudioEngine::sharedEngine()->stopAllEffects();
			SimpleAudioEngine::sharedEngine()->playEffect("Audio/SE/gameover.mp3");
			CCSprite *gameOver = CCSprite::create("UI/gameover.png");
			gameOver->setPosition(ccp(523, 200));
			this->addChild(gameOver, 1);
			CCMoveBy *moveBy = CCMoveBy::create(1.0f, ccp(0, 300));
			CCFadeOut *fadeOut = CCFadeOut::create(1.0f);
			CCSpawn *spn = CCSpawn::create(moveBy, fadeOut, NULL);
			CCDelayTime* delayTime = CCDelayTime::create(0.2f);
			CCCallFunc* callBack = CCCallFunc::create(this, callfunc_selector(GameLayer::restartLevel));
			gameOver->runAction(CCSequence::create(spn, delayTime, callBack, NULL));			
		}
	}
	labelAtlas->setString(CCString::createWithFormat("%d", score)->getCString());
}

void GameLayer::restartLevel()
{
	if(level == 0)
		CCDirector::sharedDirector()->replaceScene(CCTransitionFade::create(1.0f, TitleLayer::scene()));
	else
		CCDirector::sharedDirector()->replaceScene(CCTransitionFade::create(1.0f, GameLayer::scene()));
}

void GameLayer::createMsg(int msg)  //good,excellent等信息的处理。
{
	CCSprite* msgSprite = CCSprite::create();

	switch(msg)
	{
	case 1:
		msgSprite = CCSprite::create("Complements/good.png");
		break;
	case 2:
		msgSprite = CCSprite::create("Complements/excellent.png");
		break;
	case 3:
		msgSprite = CCSprite::create("Complements/awesome.png");
		break;
	case 4:
		msgSprite = CCSprite::create("Complements/spectacular.png");
		break;
	case 5:
		msgSprite = CCSprite::create("Complements/extraordinary.png");
		break;
	case 6:
		msgSprite = CCSprite::create("Complements/unbelievable.png");
		break;
	default:
		break;
	}

	msgSprite->setPosition(ccp(120, 200));
	msgSprite->setScale(0.2f);
	this->addChild(msgSprite, 1);
	CCMoveBy *moveBy = CCMoveBy::create(1.0f, ccp(0, 200));
	CCFadeOut *fadeOut = CCFadeOut::create(1.0f);
	CCSpawn *spn = CCSpawn::create(moveBy, fadeOut, NULL);
	msgSprite->runAction(CCSequence::create(spn, 
		CCCallFuncN::create(this, callfuncN_selector(GameLayer::deleteSelf)),
		NULL));
	
}

void GameLayer::deleteSelf(CCNode* node)
{
	CCSprite* s = (CCSprite*)node;
	s->removeFromParentAndCleanup(true);
}

void GameLayer::deleteBlock(CCNode* node)
{
	CCSprite* s = (CCSprite*)node;
	s->removeFromParentAndCleanup(true);
	CCBlock* block = (CCBlock*)node;
	BlockArr->removeObject(block, true);
}

CCBlock* GameLayer::getBlockByTag(int tag)
{
	if(BlockArr->count()==0)
		return NULL;
	CCObject *pObj;
	CCARRAY_FOREACH(BlockArr, pObj)
	{
		CCBlock* block = (CCBlock*)pObj;
		if(block->getTag() == tag)
			return block;
	}
	return NULL;
}

注释写的不怎多。说说我写的大致思路:

1.首先初始化宝石的位置和tag,当然tag和位置有这密切的位置,是对应的,在知道位置的情况是可以计算出tag,反之亦然。

2.在点击屏幕后,根据位置判断是否有宝石被点击到。如果点击到,先得到宝石的种类,其实就颜色了,然后遍历他周边4个是否是一样的颜色,如果是一样的,那么改变他的_willRemove为true,即是准备删除掉,在遍历他周边4个宝石,这里我用的是递归函数,见updateState函数。

3.在知道哪些宝石是要删除后,我们计算出其他宝石要掉落的步数step,和上方要重新要生成新宝石的数量,在函数updateMoveStep中有详细的方法。

4.然后让要删除的宝石做消失的动作,并从主场景中remove,并且从CCArray中remove。接着宝石生成,让他们根据要移动的step做移动的action。

5.完成这些后,我们在根据宝石的位置在重新初始化下一下他们的属性,主要是重新设定tag。


至于时间和血量这个就比较简单了,这个根据自己的喜好设定了,哈哈,你可以搞的很变态虐待自己哦。

这是我的方法,主要根据tag来做运算,如果哪位大神有更好的方法,希望能指点一下,个人还是觉得自己这个方法有点笨。


demo下载地址


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值