哈,山寨又来了。这次用到是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下载地址。