主场景的设计,核心是一个10x10的矩阵,从中我们可以实现同族消去。就如同popstar中那样。具体的算法参考了极客学院关于popstar的讲解。自己也做了许多改动。从功能上看,无需实现右边向左边靠拢,从具体实现上看,也做了较大改动。其中数据检测部分参考较多。原网站如下:http://www.jikexueyuan.com/course/60_1.html?ss=2
下面简单说下算法:
1初始化矩阵
随机生成数字1到118,分别对应元素。大于118的部分对应十字道具。
void challengeModel::autoCreateelementSprite(cocos2d::Size size)
{
int maxX=125;
int minX=1;
int rangeX=maxX-minX;
int actualX=0;
elementSprite* element=new elementSprite();
srand(((unsigned)time(NULL)));
int lon=(size.width-28)/10;
for(int j=0;j<10;j++)
for(int i=0;i<10;i++)
{
actualX=(rand()%rangeX)+minX;
if (actualX >=119)
actualX = 119;
element=elementSprite::createElementSprite(actualX,90, 90, lon*j+28 , lon*i+size.height/6+20 );
element->setElementX(j);
element->setElementY(i);
element->setNumberAndGroup(actualX);
element->setAnchorPoint(ccp(0,0));
element->setPosition(lon*j + 28, lon*i +size.height / 6 + 20);
//element->setElementSpriteX(lon*j);
//element->setElementSpriteY(lon*i + size.height / 6);
//Size sizeOfImage=element->getContentSize();
element->setScale(0.75);
addChild(element);
elementSpriteMatrix[j][i]=element;
}
}
2.检测相同的方块
(1)先检测一个方块上下左右的相同的方块,将其加入数组
(2)之后对数组中每一个方块进行迭代检测,并判断检测到的方块是否为新的方块,如果为新的就加入到数组中,如果不是就不加入。
cocos2d::__Array* challengeModel::checkElementUDLR(elementSprite* ele)
{
cocos2d::__Array *arr = cocos2d::__Array::create();
bool isDoubleGroup = false;
//检测该位置以上的相同数目
int up = ele->getElementY();
for (int yu = up + 1; yu < 10; yu++)
{
if (elementSpriteMatrix[ele->getElementX()][yu]== NULL)break;
if (elementSpriteMatrix[ele->getElementX()][yu]->getGroup()== elementSpriteMatrix[ele->getElementX()][up]->getGroup())
{
arr->addObject(elementSpriteMatrix[ele->getElementX()][yu]);
isDoubleGroup = true;
}
else
break;
}
int down = ele->getElementY();
for (int yd = down - 1; yd >= 0; yd--)
{
if (elementSpriteMatrix[ele->getElementX()][yd]== NULL) break;
if (elementSpriteMatrix[ele->getElementX()][yd]->getGroup()== elementSpriteMatrix[ele->getElementX()][down]->getGroup())
{
arr->addObject(elementSpriteMatrix[ele->getElementX()][yd]);
isDoubleGroup = true;
}
else
break;
}
int left = ele->getElementX();
for (int xl = left - 1; xl >= 0; xl--)
{
if (elementSpriteMatrix[xl][ele->getElementY()] == NULL)break;
if (elementSpriteMatrix[xl][ele->getElementY()]->getGroup() == elementSpriteMatrix[left][ele->getElementY()]->getGroup())
{
arr->addObject(elementSpriteMatrix[xl][ele->getElementY()]);
isDoubleGroup = true;
}
else
break;
}
int right = ele->getElementX();
for (int xr = right+ 1; xr < 10; xr++)
{
if (elementSpriteMatrix[xr][ele->getElementY()] == NULL)break;
if (elementSpriteMatrix[xr][ele->getElementY()]->getGroup() == elementSpriteMatrix[right][ele->getElementY()]->getGroup())
{
arr->addObject(elementSpriteMatrix[xr][ele->getElementY()]);
isDoubleGroup = true;
}
else
break;
}
arr->addObject(ele);
return arr;
}
//检测该序列中是否有新的精灵,如果有的话
void challengeModel::checkElementISNEW(cocos2d::__Array *arrold)
{
cocos2d::__Array *arrnew = cocos2d::__Array::create();
for (int i = 0; i < arrold->count(); i++)
{
elementSprite* ele = (elementSprite*)arrold->getObjectAtIndex(i);
cocos2d::__Array *arrnews = checkElementUDLR(ele);
for (int j = 0; j < arrnews->count(); j++)
{
bool isSave = false;
elementSprite* elenew = (elementSprite*)arrnews->getObjectAtIndex(j);
for (int k = 0; k < elementarr->count(); k++)
{
elementSprite* eleold = (elementSprite*)arrold->getObjectAtIndex(k);
if ((elenew->getElementX() == eleold->getElementX())&& (elenew->getElementY() == eleold->getElementY()))
{
isSave = true;
break;
}
}
if (!isSave)
{
arrnew->addObject(elenew);
}
}
}
if (arrnew->count()>0)
{
for (int q = 0; q < arrnew->count(); q++)
{
elementarr->addObject(arrnew->getObjectAtIndex(q));
}
checkElementISNEW(elementarr);
}
else
{
clearElementArr();
calculateScore();
reDissElement();
}
}
3.上面元素相向下面移动
依次检测每列,从上到下移动。
对于每列而言,从上到下检测前面的空位,之后从上往下依次下沉
//上面的精灵向下填补空白
void challengeModel::reDissElement()
{
for (int i = 0; i < 10; i++)
{
reDissElementRun(i);
}
}
//每一列上面的精灵向下填补空白
void challengeModel::reDissElementRun(int x)
{
int number = 0;
int numberOfSpace[10] = {0};
for (int i = 0; i < 10; i++)
{
if (elementSpriteMatrix[x][i]==NULL)
{
number++;
}
numberOfSpace[i] = number;
}
if (number>0)
{
for (int y = 0; y < 10; y++)
{
if ((numberOfSpace[y]>0)&&(elementSpriteMatrix[x][y] != NULL))
{
elementSprite* ele = elementSpriteMatrix[x][y];
ele->runAction(MoveTo::create(1, pointOfItem(x, y -numberOfSpace[y])));
elementSpriteMatrix[x][y - numberOfSpace[y]] = ele;
ele->setElementY(y - numberOfSpace[y]);
}
if (y >=(10 - number))
{
elementSpriteMatrix[x][y] = NULL;
}
}
}
}
4.分数的计算及十字道具的使用
暂时先这样,按照消去的数目加分。
int challengeModel::calculateScore()
{
if (elementarr->count() < 2)
return 0;
else
{
switch (elementarr->count())
{
case 2:score = score + 3;
break;
case 3:score = score + 5;
break;
case 4:score = score + 7;
break;
case 5:score = score + 10;
default:
score = score + 10 + elementarr->count();
break;
}
}
updateScore();
return score;
}
void challengeModel::clearCrossing(elementSprite* ele)
{
for (int i = 0; i < 10; i++)
{
if (elementSpriteMatrix[ele->getElementX()][i] != NULL)
{
elementarr->addObject(elementSpriteMatrix[ele->getElementX()][i]);
}
}
for (int i = 0; i < 10; i++)
{
if (elementSpriteMatrix[i][ele->getElementY()] != NULL)
{
elementarr->addObject(elementSpriteMatrix[i][ele->getElementY()]);
}
}
elementarr->addObject(ele);
clearElementArr();
//calculateScore();
calculateAddition();
}
5.检测是否游戏结束
主要是检测是否有同组的方块,isGameOver为游戏结束的标志,isLevelOver为关数结束的标志。其中很重要的一部分是进行场景的切换和关数的更新,这个部分在下一部分详细探讨。
bool challengeModel::reGameOver()
{
bool isLevelOver = true;
bool isGameOver = false;
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
if (elementSpriteMatrix[i][j] != NULL && (checkElementUDLR(elementSpriteMatrix[i][j])->count()>1))
{
isLevelOver = false;
break;
}
}
if (isLevelOver == false)
break;
}
if (isLevelOver)
{
if (score < goal)
isGameOver = true;
setScoreGoalLevelOfXml(score,goal+100,level+1);
setFirstTime(false);
auto director = Director::getInstance();
director->replaceScene(challengeModel::createScene());
}
if (isGameOver)
{
setHeightestScore();
setFirstTime(true);
setScoreGoalLevelOfXml(0,100,1);
auto director = Director::getInstance();
director->replaceScene(menuScene::createScene());
}
return isGameOver;
}
6.关数的更新方法和数据的存储
(1)关数的更新:使用一bool型变量isFirstTime标示是否为第一关,如果是,初始化各个数据如goal、score和level为初始值。否则读取储存的各项数据,更新关数(level)。下面摘录其中一个函数。
(2)数据的存储,可以参考http://www.apkbus.com/android-142408-1-1.html一个关于打飞机的游戏。
bool challengeModel::setScoreGoalLevelOfXml(int score, int goal, int level)
{
CCUserDefault::sharedUserDefault()->setIntegerForKey("score", score);
CCUserDefault::sharedUserDefault()->setIntegerForKey("goal", goal);
CCUserDefault::sharedUserDefault()->setIntegerForKey("level", level);
CCUserDefault::sharedUserDefault()->flush();
return true;
}
bool challengeModel::getScoreGoalLevelOfXml()
{
if (isHaveSavedFile())
{
score = CCUserDefault::sharedUserDefault()->getIntegerForKey("score", 0);
goal = CCUserDefault::sharedUserDefault()->getIntegerForKey("goal", 100);
level = CCUserDefault::sharedUserDefault()->getIntegerForKey("level", 1);
return true;
}
return false;
}
7.关于分享操作
现在sharesdk已经在这一方面做得很好,也是免费的。具体的集成方法在其官网上有详解,可以参考http://wiki.mob.com/cocos2d-x-3-x快速集成指南/
注意如果用的cocos2d版本为3.x,则参考上面网址,如果用的是2.x,则参考http://blog.csdn.net/song_hui_xiang/article/details/32131833
void shareResultHandler(C2DXResponseState state,C2DXPlatType platType, __Dictionary *shareInfo, __Dictionary *error)
{
CCLog("shareResultHandler");
switch (state)
{
case C2DXResponseStateSuccess:
C2DXShareSDK::toast("分享成功");
break;
case C2DXResponseStateFail:
C2DXShareSDK::toast("分享失败");
break;
default:
C2DXShareSDK::toast("分享取消");
break;
}
}
void challengeModel::shareMenuItemClick(CCObject* pSender)
{
__Dictionary *content = __Dictionary::create();
//Dictionary可用的Key如下,如果需要用到其它字段,可自行参考Sample中的代码实现:
// (并不是所有平台都有这些字段,需要参考文档http://wiki.mob.com/Android_%E4%B8%8D%E5%90%8C%E5%B9%B3%E5%8F%B0%E5%88%86%E4%BA%AB%E5%86%85%E5%AE%B9%E7%9A%84%E8%AF%A6%E7%BB%86%E8%AF%B4%E6%98%8E)
content ->setObject(String::create("我在元素消消看中取得了不错的成绩,你敢来试一试吗?"), "content"); //要分享的内容,注意在文档中content对应的是text字段
// content ->setObject(String::create("http://img0.bdstatic.com/img/image/shouye/systsy-11927417755.jpg"),"image"); //可以是本地路径(如:/sdcard/a.jpg)或是一个URL
content ->setObject(String::create("元素消消看"), "title");
// content -> setObject(String::create("测试描述"), "description");
//content ->setObject(String::create("http://sharesdk.cn"), "url");
content ->setObject(String::createWithFormat("%d", C2DXContentTypeNews), "type");
//content -> setObject(String::create("http://sharesdk.cn"),"siteUrl");
// content ->setObject(String::create("ShareSDK"), "site");
// content ->setObject(String::create("http://mp3.mwap8.com/destdir/Music/2009/20090601/ZuiXuanMinZuFeng20090601119.mp3"),"musicUrl");
content ->setObject(String::create("extInfo"), "extInfo"); //微信分享应用时传给应用的附加信息
C2DXShareSDK::showShareMenu(NULL,content, cocos2d::Point(100, 100), C2DXMenuArrowDirectionLeft, shareResultHandler);
//C2DXShareSDK::showShareView(C2DXPlatTypeSinaWeibo,content, shareResultHandler);
}
8.安卓监听返回键和菜单键及场景的切换
进行移动平台开发不能忘了监听返回键和菜单键
详细参考http://blog.csdn.net/we000636/article/details/8597540
而进行场景的操作,可以参考http://blog.csdn.net/tonny_guan/article/details/27570063
void challengeModel::onKeyReleased(EventKeyboard::KeyCodekeyCode, Event* event)
{
switch(keyCode)
{
case EventKeyboard::KeyCode::KEY_ESCAPE:
setHeightestScore();
Director::getInstance()->pushScene(menuScene::createScene());
//Director::getInstance()->popScene();
break; //检测返回键
case EventKeyboard::KeyCode::KEY_MENU:
setHeightestScore();
break;
}
}
9.小结
这里主要讲了游戏场景的搭建,代码可以从下面地址下载:http://download.csdn.net/download/u012483487/8463729