1. 程式人生 > >消除類遊戲核心演算法

消除類遊戲核心演算法

如今消除類遊戲很多,比如:消滅壽司,消滅星星等。這篇文章我就共享下我這方面的理解,希望對大家有幫助,如有不好的地方也希望大家指出。

                       

這是我用到資源。同種顏色的2個或者2個以上的就可以消除,消除的越多分越高。

首先,我們來定義一些要用到的變數:

<pre class="cpp" name="code">static const int GRID_COUNT_LANDSCAPE = 10;//方塊橫總長度
static const int GRID_COUNT_PORTRAIT = 10;//方塊豎總長度

static const string GRID_PIC_PATHS[GRID_TYPE_COUNT] = {string("hongse.png"), string("huangse.png"), string("lanse.png"), string("lvse.png"), string("zise.png")};
static const int GRID_TYPE_COUNT = 5;
static const int GRID_EMPTY_DATA = GRID_TYPE_COUNT+1;

char m_gridData[GRID_COUNT_LANDSCAPE * GRID_COUNT_PORTRAIT];

std::vector<GridPos> m_promptGrids;//提示的格子
bool m_scaledPromptGrids;//已經縮小的提示格子

 cocos2d::gui::Widget *m_gridContentLayer;





struct GridPos{
    GridPos(int x = 0, int y = 0):m_x(x), m_y(y){
    }
    int m_x;
    int m_y;
};

struct EliminateGrid{
    EliminateGrid(const GridPos &pos, int type):m_gridPos(pos), m_type(type){
    }
    GridPos m_gridPos;
    int m_type;
};

在GaeScene中初始化:

bool GameScene::init(){ 

     do{     

       C_BREAK_IF(!TouchGroup::init());

       for (int i=0;i<GRID_COUNT_PORTRAIT * GRID_COUNT_LANDSCAPE; i++)
        {
            m_gridData[i] = GRID_EMPTY_DATA;
        }

       m_gridContentLayer = Widget::create();
        CC_BREAK_IF(!m_gridContentLayer);
        m_gridContentLayer->retain();
        m_gridContentLayer->setAnchorPoint(ccp(0,0));
        m_gridContentLayer->setPosition(ccp(0, 0));
        this->addWidget(m_gridContentLayer);

        return true;

      }while(0);

        return false;

}

得到螢幕上的畫素座標:

void GameScene::getGridPosition(int x, int y, CCPoint &point){
    point.x = GRID_WIDTH / 2 + GRID_WIDTH * x;
    point.y = GRID_HEIGHT / 2 + GRID_HEIGHT * y;
<span style="color:#000000;">}</span>

把螢幕上的畫素座標轉換成我們的座標

bool GameScene::getPointGridPos(const CCPoint &point, GridPos &pos){
    float x = point.x - GRID_BEGIN_X;
    float y = point.y - GRID_BEGIN_Y;
    if (x < 0 || x > GRID_WIDTH * GRID_COUNT_LANDSCAPE
        || y < 0 || y > GRID_HEIGHT * GRID_COUNT_PORTRAIT)
    {
        return false;
    }
    pos.m_x = ((int)x) / GRID_WIDTH;
    pos.m_y = ((int)y) / GRID_HEIGHT;
    return true;
}

 這裡把螢幕上的畫素方塊座標轉換成我們的座標是為了我們好操作,這裡轉換的座標相當於方塊陣列的下標,後面有轉換的方法。

得到方塊的顏色:

std::string GameScene::getGridPath(int data){
    if (data == GRID_EMPTY_DATA || data >= GRID_TYPE_COUNT)
    {
        return "";
    }else{
        return GAME_SCENE_ROOT_PATH + GRID_PIC_PATHS[data];
    }
}

建立一個10*10的方塊組:

void GameScene::initGridsData(){
    for(int i=0;i<GRID_COUNT_PORTRAIT * GRID_COUNT_LANDSCAPE; i++){
        m_gridData[i] = rand() % (GRID_TYPE_COUNT);
    }
}

得到方塊的下標:

static int getGridIndex(const GridPos &pos){
return getGridIndex(pos.m_x, pos.m_y);}

顯示方塊:

void GameScene::showGrids(float duration){
    this->removeChildByTag(LEVEL_PASS_AMN_TAG, true);
    for (int x = 0; x < GRID_COUNT_LANDSCAPE; x++)
    {
        for (int y=0;y<GRID_COUNT_PORTRAIT;y++)
        {
            int index = x * GRID_COUNT_PORTRAIT + y;
            string path = getGridPath(m_gridData[index]);
            if (path.length() > 0)
            {
                ImageView *pic = ImageView::create();
                pic->loadTexture(path.c_str());
                pic->setAnchorPoint(ccp(0.5f,0.5f));
                CCPoint position;
                getGridPosition(x,y,position);
                pic->setPosition(position);
                pic->setTag(getGridTag(x, y));
                this->m_gridContentLayer->addChild(pic);
            }
        }
    }
    m_gridContentLayer->setPositionY(MyGame::CUR_STANDARD_HEIGHT);
    CCDelayTime *delay = CCDelayTime::create(duration);
    CCMoveTo *moveTo = CCMoveTo::create(BEGIN_GAME_DROP_DOWN_DURATION, ccp(GRID_BEGIN_X, GRID_BEGIN_Y));
    CCAction* action = CCSequence::create(delay, moveTo, NULL);
    m_gridContentLayer->runAction(action);
}

觸控方塊:

bool GameScene::ccTouchBegan(CCTouch *touch, CCEvent *pEvent){
    m_hitChild = TouchGroup::ccTouchBegan(touch, pEvent);
    if(GRID_BEGIN_X != m_gridContentLayer->getPositionX() || GRID_BEGIN_Y != m_gridContentLayer->getPositionY()){//格子整體移動的時候不響應點選事件
        m_hitChild = true;
    }
    if (!m_hitChild)
    {
        bool within = getPointGridPos(touch->getLocation(), m_touchGrid);
        if (!within)//沒有擊中格子,不處理消除
        {
            m_hitChild = true;
        }
    }
    return true;
}
void GameScene::ccTouchEnded(CCTouch *touch, CCEvent *pEvent){
    TouchGroup::ccTouchEnded(touch, pEvent);
    if (!m_hitChild)
    {
        CCPoint pos = touch->getLocation();
        GridPos gridPos;
        bool within = getPointGridPos(pos, gridPos);
        if (within && gridPos.m_x == m_touchGrid.m_x && gridPos.m_y == m_touchGrid.m_y)
        {
            vector<GridPos> grids;
            this->extendPos(gridPos, grids);
            if (0 == this->eliminateGrids(grids))//消除成功,計算是否可以繼續消除
            {
                int score = this->getScore(grids.size());
                this->setScore(m_score + score, true, pos, true);//加上所得分改變分數
                showEliminatePrompt(grids.size(), score);

                clearPromptGrids();
            }
        }
    }
}

下面開始介紹最核心的部分:

//檢測一個方塊周圍是否有和它一樣顏色的 如果有就把它們放到陣列中
void GameScene::extendPos(const GridPos &pos, vector<GridPos> &validGrids) const{
    validGrids.clear();
    validGrids.push_back(pos);//把有方塊的左邊放入validGrids中
    char color = m_gridData[getGridIndex(pos)];
    if (GRID_EMPTY_DATA == color)
    {
        return;
    }

    int checkedGrids[GRID_COUNT_PORTRAIT * GRID_COUNT_LANDSCAPE];
    for (int i=0;i<GRID_COUNT_PORTRAIT * GRID_COUNT_LANDSCAPE; i++)
    {
        checkedGrids[i] = 0;
    }
    unsigned int index = 0;
    while(index < validGrids.size()){
        GridPos checkedPos = validGrids[index];//得到每一個方塊座標
        int checkedIndex = getGridIndex(checkedPos);

        GridPos left(checkedPos.m_x-1, checkedPos.m_y);//檢測左邊
        if (checkSameGrid(left,checkedGrids,color))
        {
            validGrids.push_back(left);
            checkedGrids[getGridIndex(left)]++;
        }
        GridPos right(checkedPos.m_x+1, checkedPos.m_y);
        if (checkSameGrid(right,checkedGrids,color))
        {
            validGrids.push_back(right);
            checkedGrids[getGridIndex(right)]++;
        }
        GridPos up(checkedPos.m_x, checkedPos.m_y-1);
        if (checkSameGrid(up,checkedGrids,color))
        {
            validGrids.push_back(up);
            checkedGrids[getGridIndex(up)]++;
        }
        GridPos down(checkedPos.m_x, checkedPos.m_y+1);
        if (checkSameGrid(down,checkedGrids,color))
        {
            validGrids.push_back(down);
            checkedGrids[getGridIndex(down)]++;
        }
        checkedGrids[checkedIndex]++;
        index++;
    }
}

檢測點選的方塊周圍是否有相同顏色的方塊:

//檢測同種顏色的方塊
bool GameScene::checkSameGrid(GridPos &pos, int *checkedGrids, char color) const{
    if (pos.m_x < 0 || pos.m_x >= GRID_COUNT_LANDSCAPE || pos.m_y < 0 || pos.m_y >= GRID_COUNT_PORTRAIT)//out range
    {
        return false;
    }
    int index = getGridIndex(pos);
    if (0 != checkedGrids[index])//is checked
    {
        return false;
    }
    return color == m_gridData[index];//check color
}

消除方塊:

int GameScene::eliminateGrids(std::vector<GridPos> &eliminateGrids){
    if (eliminateGrids.size() <= 1)
    {
        return 1;
    }
    for (vector<GridPos>::iterator iter = eliminateGrids.begin(); iter != eliminateGrids.end(); iter++)//消除
    {
        GridPos p = *iter;
        int index = getGridIndex(p);
        vector<EliminateGrid>::iterator iterPos = m_waitEliminateGrids.end();
        for (vector<EliminateGrid>::iterator m_iter = m_waitEliminateGrids.begin(); m_iter != m_waitEliminateGrids.end(); m_iter++)
        {
            if ((*m_iter).m_gridPos.m_x > p.m_x || ((*m_iter).m_gridPos.m_x == p.m_x && (*m_iter).m_gridPos.m_y < p.m_y))
            {
                iterPos = m_iter;
                break;
            }
        }
        EliminateGrid eliminateGrid(p,m_gridData[index]);
        m_waitEliminateGrids.insert(iterPos, 1, eliminateGrid);

        m_gridData[index] = GRID_EMPTY_DATA;
    }
    if (eliminateGrids.size() >= SHOW_REWARD_AMN_ELIMINATE_GRID_MIN_COUNT)//消除大於等於5個格子的特效
    {
        CCDirector::sharedDirector()->getScheduler()->scheduleSelector(schedule_selector(GameScene::playEliminateRewardEffect), this, eliminateGrids.size()*ELIMINATE_INTERVAL, false);
    }
    m_finishEliminate=false;
    showEliminateAmn();//消除方塊時播放的動畫
    return 0;
}

消除之後,移動方塊:

void GameScene::eliminateEmptyGrids(float waitTime){
    int index = 0;
    for (int x = 0; x < GRID_COUNT_LANDSCAPE; x++)//落下
    {
        int empty = 0;
        for (int y = 0; y < GRID_COUNT_PORTRAIT; y++)
        {
            if (GRID_EMPTY_DATA == m_gridData[index])
            {
                empty++;
            }else if (empty > 0)
            {
                moveGrid(index, index-empty, 0, -empty * GRID_HEIGHT, DROP_DOWN_DURATION, waitTime);
            }
            index++;
        }
    }
    int empty = 0;
    for (int x = 0; x < GRID_COUNT_LANDSCAPE; x++)//平移
    {
        if (GRID_EMPTY_DATA == m_gridData[this->getGridIndex(x, 0)])
        {
            empty++;
        }else if (empty > 0)
        {
            for (int y = 0; y < GRID_COUNT_PORTRAIT; y++)
            {
                int oldIndex = getGridIndex(x, y);
                if (GRID_EMPTY_DATA != m_gridData[oldIndex])
                {
                    moveGrid(oldIndex, oldIndex - empty * GRID_COUNT_PORTRAIT, -empty * GRID_WIDTH, 0, MOVE_TO_LEFT_DURATION, waitTime + DROP_DOWN_DURATION);
                }
            }
        }
    }
}


void GameScene::moveGrid(int oldIndex, int newIndex, int disX, int disY, float duration, float waitTime){
    m_gridData[newIndex]=m_gridData[oldIndex];//資料移動
    m_gridData[oldIndex] = GRID_EMPTY_DATA;
    int oldTag =  oldIndex;//影象移動
    CCNode* gridSprite = this->m_gridContentLayer->getChildByTag(oldTag);
    int newTag =  newIndex;
    gridSprite->setTag(newTag);
    CCMoveBy *moveBy = CCMoveBy::create(duration, ccp(disX,  disY));
    CCEaseSineIn *moveAction = CCEaseSineIn::create(moveBy);
    CCAction* action = 0;
    if (waitTime > 0)
    {
        CCDelayTime *delay = CCDelayTime::create(waitTime);
        action = CCSequence::create(delay, moveAction, NULL);
    }else{
        action = CCSequence::create(moveAction, NULL);
    }
    gridSprite->runAction(action);
}



無可消除的方塊,結束:

bool GameScene::finish(int remainCount){
    int reward = this->getRewardScore(remainCount);
    this->setScore(m_score + reward, true, ccp(MyGame::CUR_STANDARD_WIDTH/2,MyGame::CUR_STANDARD_HEIGHT/2), true);
    this->showRemainRewardPrompt(remainCount, reward, SHOW_FINISH_INFO_DELAY_TIME);

    m_waitEliminateGrids.clear();
    for (int x = 0; x < GRID_COUNT_LANDSCAPE; x++)
    {
        for (int y = GRID_COUNT_PORTRAIT - 1; y >= 0; y--)
        {
            int index = getGridIndex(x,y);
            if (GRID_EMPTY_DATA != m_gridData[index])
            {
                EliminateGrid eliminateGrid(GridPos(x,y), (int)(m_gridData[index]));
                m_waitEliminateGrids.push_back(eliminateGrid);
                m_gridData[index] = GRID_EMPTY_DATA;
            }
        }
    }
    m_finishEliminate=true;
    CCDirector::sharedDirector()->getScheduler()->scheduleSelector(schedule_selector(GameScene::showEliminateAmn), this, ELIMINATE_INTERVAL, kCCRepeatForever, SHOW_FINISH_INFO_DELAY_TIME + REMAIN_REWARD_MOVE_DURATION, false);
    if (m_score < m_levelPassScore)//分數沒達到要求,遊戲結束
    {
        std::stringstream curLevelSS;
        curLevelSS << m_level;
        MyGame::getInstance()->tJSendLogDataBy3k("850", curLevelSS.str().c_str());
        return false;
    }else{
       //下一關遊戲
    }
}

我們再加點提示可以消除的方塊功能:

計算可以消除的方塊:

void GameScene::calculatePromptGrids(){
    m_promptGrids.clear();
    bool checkedGrids[GRID_COUNT_PORTRAIT * GRID_COUNT_LANDSCAPE];
    for (int i=0;i<GRID_COUNT_PORTRAIT * GRID_COUNT_LANDSCAPE; i++)
    {
        checkedGrids[i] = false;
    }
    vector<GridPos> validGrids;
    GridPos pos;
    for (int x = 0; x < GRID_COUNT_LANDSCAPE; x++)//落下
    {
        for (int y = 0; y < GRID_COUNT_PORTRAIT; y++)
        {
            int index = getGridIndex(x,y);
            if (GRID_EMPTY_DATA == m_gridData[index])
            {
                break;
            }else if (!checkedGrids[index])
            {
                pos.m_x=x;
                pos.m_y=y;
                validGrids.clear();
                extendPos(pos, validGrids);
                //檢測所有方塊,如果有第一個可以消除的放到validGrids,傳給m_promptGrids,然後接著檢測,最後把消除最多的傳給m_promptGrids
                bool changeGrids = validGrids.size() > 1 && validGrids.size()>m_promptGrids.size();
                if (changeGrids)
                {
                    m_promptGrids.clear();
                }
                for (vector<GridPos>::iterator iter = validGrids.begin(); iter != validGrids.end(); iter++)
                {
                    checkedGrids[getGridIndex(*iter)]=true;
                    if (changeGrids)
                    {
                        m_promptGrids.push_back(*iter);
                    }
                }
            }
        }
    }
}
//提示消除方塊動作
void GameScene::showPromptGrids(float interval){
    if (m_promptGrids.size()>0)
    {
        float scale = m_scaledPromptGrids ? 1.0f : PROMPT_GRIDS_MIN_SCALE;
        for (vector<GridPos>::iterator iter=m_promptGrids.begin(); iter!=m_promptGrids.end(); iter++)
        {
            CCNode *gridNode = m_gridContentLayer->getChildByTag(getGridTag(*iter));
            CCScaleTo *scaleTo= CCScaleTo::create(PROMPT_GRIDS_DURATION, scale, scale);
            CCSequence *action = CCSequence::create(scaleTo, NULL);
            gridNode->runAction(action);
        }
        m_scaledPromptGrids=!m_scaledPromptGrids;
    }
}

如果點選提示的格子就清除提示格子

void GameScene::clearPromptGrids(){
    if (m_scaledPromptGrids)
    {
        for (vector<GridPos>::iterator iter=m_promptGrids.begin(); iter!=m_promptGrids.end(); iter++)
        {
            CCNode *gridNode = m_gridContentLayer->getChildByTag(getGridTag(*iter));
            if (gridNode)
            {
                CCScaleTo *scaleTo= CCScaleTo::create(PROMPT_GRIDS_DURATION, 1.0f, 1.0f);
                CCSequence *action = CCSequence::create(scaleTo, NULL);
                gridNode->runAction(action);
            }
        }
    }
    m_promptGrids.clear();
    m_scaledPromptGrids=false;
 }