成人午夜激情影院,小视频免费在线观看,国产精品夜夜嗨,欧美日韩精品一区二区在线播放

使用Cocos2d-x開(kāi)發(fā)2048游戲教程(Part2)

2014-07-15 17:40:49來(lái)源:Cocos2d-x作者:

上一節(jié),我們學(xué)習(xí)了如何創(chuàng)建一個(gè)新工程,并創(chuàng)建自己的游戲場(chǎng)景。2048游戲場(chǎng)景已經(jīng)創(chuàng)建好,但是還缺少玩家交互,游戲得分,結(jié)束等邏輯。下面我們一起來(lái)完善。

上一節(jié),我們學(xué)習(xí)了如何創(chuàng)建一個(gè)新工程,并創(chuàng)建自己的游戲場(chǎng)景。2048游戲場(chǎng)景已經(jīng)創(chuàng)建好,但是還缺少玩家交互,游戲得分,結(jié)束等邏輯。下面我們一起來(lái)完善。

游戲操作

游戲中需要加入touch事件來(lái)處理,上下左右移動(dòng)數(shù)字卡片的判斷。 玩家可以選擇上下左右四個(gè)方向,若棋盤(pán)內(nèi)的數(shù)字出現(xiàn)位移或合并,視為有效移動(dòng);玩家選擇的方向上若有相同的數(shù)字則合并,每次有效移動(dòng)可以同時(shí)合并,但不可以連續(xù)合并;合并所得的所有新生成數(shù)字相加即為該步的有效得分;玩家選擇的方向行或列前方有空格則出現(xiàn)位移;每有效移動(dòng)一步,棋盤(pán)的空位(無(wú)數(shù)字處)隨機(jī)出現(xiàn)一個(gè)數(shù)字(依然可能為2或4)。 直到棋盤(pán)內(nèi)的數(shù)字卡片數(shù)值達(dá)到2048,玩家勝利。或棋盤(pán)填滿數(shù)字,無(wú)法合并移動(dòng)。游戲結(jié)束。

注冊(cè)觸摸事件響應(yīng)

首先我們需要讓Layer能接收Touch事件。所以我們繼承Layer處理事件的回調(diào)虛函數(shù),并重寫(xiě),下面是實(shí)現(xiàn)過(guò)程:

1、在GameScene.h文件中聲明成員函數(shù)

//觸摸事件監(jiān)聽(tīng)回調(diào)函數(shù)
virtual bool onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event);
virtual void onTouchMoved(cocos2d::Touch* touch, cocos2d::Event* event);
virtual void onTouchEnded(cocos2d::Touch* touch, cocos2d::Event* event);

2、在GameScene.cpp文件的init函數(shù)中創(chuàng)建綁定觸摸事件

//設(shè)置觸摸事件監(jiān)聽(tīng)
auto touchListener = EventListenerTouchOneByOne::create();
touchListener->onTouchBegan = CC_CALLBACK_2(GameScene::onTouchBegan, this);
touchListener->onTouchMoved = CC_CALLBACK_2(GameScene::onTouchMoved, this);
touchListener->onTouchEnded = CC_CALLBACK_2(GameScene::onTouchEnded, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);

3、實(shí)現(xiàn)觸摸事件回調(diào)函數(shù)

bool GameScene::onTouchBegan(Touch* touch, Event* event)
{
Point beginTouch = touch->getLocation();   
recognizer->beginPoint(beginTouch.x, beginTouch.y);
return true;
}
void GameScene::onTouchMoved(Touch* touch, Event* event)
{
Point pos = touch->getLocation();
recognizer->movePoint(pos.x, pos.y);
}
void GameScene::onTouchEnded(Touch* touch, Event* event)
{
SimpleGestures rtn = recognizer->endPoint();
switch (rtn) {
    case SimpleGesturesLeft:
        doLeft();
        doCheck();
        setScore(score);
        break;
    case SimpleGesturesRight:
        doRight();
        doCheck();
        setScore(score);
        break;
    case SimpleGesturesUp:
        doUp();
        doCheck();
        setScore(score);
        break;
    case SimpleGesturesDown:
        doDown();
        doCheck();
        setScore(score);
        break; 
    case SimpleGesturesNotSupport:
    case SimpleGesturesError:
        log("not support or error touch,use geometricRecognizer!!");
        break; 
    default:
        break;
}
}

手勢(shì)判斷

在此我封裝了一個(gè)簡(jiǎn)單的手勢(shì)識(shí)別類SimpleRecognizer,來(lái)處理玩家的滑動(dòng)手勢(shì)。代碼中對(duì)滑動(dòng)距離大于50以上的才處理為有效手勢(shì)。關(guān)鍵代碼如下:

// be called in onTouchBegan
void SimpleRecognizer::beginPoint(cocos2d::Point point)
{
    this->result = SimpleGesturesError;
    points.clear();
    points.push_back(point);
}
 
void SimpleRecognizer::movePoint(cocos2d::Point point)
{
    points.push_back(point);
}
 
SimpleGestures SimpleRecognizer::endPoint(cocos2d::Point point)
{
    points.push_back(point);
 
    if (this->points.size() < 3) {
        return SimpleGesturesError;
    }
 
    SimpleGestures newRtn = SimpleGesturesError;
    int len = this->points.size();
    //每當(dāng)觸點(diǎn)移動(dòng)時(shí),在當(dāng)前觸點(diǎn)和之前觸點(diǎn)之間計(jì)算不同的x坐標(biāo)和y坐標(biāo)
    double dx = this->points[len - 1].x - this->points[0].x;
    double dy = this->points[len - 1].y - this->points[0].y;
 
    if (abs(dx) > abs(dy)) {
        //在這種情況下,運(yùn)動(dòng)趨勢(shì)的觸點(diǎn)在x軸方向
        if (dx > 50) {
            newRtn = SimpleGesturesRight;
        } else if ( dx < -50 ) {
            newRtn = SimpleGesturesLeft;
        }
    } else {
        //在這種情況下,運(yùn)動(dòng)趨勢(shì)的觸點(diǎn)在y軸方向
        if (dy > 50) {
            newRtn = SimpleGesturesUp;
        } else if ( dy < -50 ) {
            newRtn = SimpleGesturesDown;
        }
    }
 
    // first set result
    if (result == SimpleGesturesError) {
        result = newRtn;
    }
 
    // if diretcory change, not support Recongnizer
    if (result != newRtn) {
        result = SimpleGesturesNotSupport;
    }
    return result;
}

根據(jù)手勢(shì)處理數(shù)字卡片的移動(dòng)或合并

根據(jù)手勢(shì)方向,判斷數(shù)字卡片是否可以移動(dòng)和合并。更新分?jǐn)?shù),移動(dòng)完成后判斷是否在空白處生成新的數(shù)字卡片。

游戲中我們并沒(méi)有真正的數(shù)字卡片移動(dòng),如果卡片可以沿著一個(gè)方向移動(dòng),比如左移,是循環(huán)遍歷4*4的矩陣,將card[x+1][y]的數(shù)字賦給card[x][y],將card[x+1][y]的數(shù)字賦為0實(shí)現(xiàn)的。如果相鄰兩數(shù)字卡片card[x+1][y]和card[x][y]的數(shù)字相同則進(jìn)行合并操作,card[x][y]的數(shù)字為合并后所得的數(shù)字。將card[x+1][y]的數(shù)字賦為0實(shí)現(xiàn)的。得分為合并所得數(shù)字。

四個(gè)方向的移動(dòng)處理代碼類似,下面來(lái)看左移代碼實(shí)現(xiàn)如下:

//左滑動(dòng)
bool GameScene::doLeft()
{
    //判斷有沒(méi)有發(fā)生移動(dòng)
    bool isMove = false;
    for (int y = 0; y < 4; y++)
    {
        for (int x = 0; x < 4; x++)
        {
            for (int x1 = x+1; x1<4; x1++)
            {
                if (cardArr[x1][y]->getNumber() > 0)
                {
                    if (cardArr[x][y]->getNumber() <= 0)
                    {
                        cardArr[x][y]->setNumber(cardArr[x1][y]->getNumber());
                        cardArr[x1][y]->setNumber(0);
                        x--;
                        isMove = true;
                    }
                    else if(cardArr[x][y]->getNumber() == cardArr[x1][y]->getNumber())
                    {
                        cardArr[x][y]->setNumber(cardArr[x][y]->getNumber() * 2);
                        cardArr[x1][y]->setNumber(0);
 
                        //改變分?jǐn)?shù)
                        score += cardArr[x][y]->getNumber();
                        isMove = true;
                    }
                    break;
                }
            }
        }
    }
 
    return isMove;
}

在移動(dòng)、合并和得分處理完成后,我們需要判斷為空白矩陣位置加入新的數(shù)字卡片。實(shí)際上是,查找4*4的矩陣區(qū)域中數(shù)字為0的卡片,隨機(jī)賦值為2或4.新的數(shù)字卡片加入后,我們進(jìn)行判斷游戲是否結(jié)束。

游戲結(jié)束的邊界,4*4的數(shù)字卡片中沒(méi)有數(shù)字0且相鄰數(shù)字卡片沒(méi)有相等的數(shù)字。

void GameScene::doCheck()
{
    bool isGameOver = true;
 
    //結(jié)束邊界  4*4的card數(shù)值>0 且  相鄰card沒(méi)有相同數(shù)值
    //4*4的card數(shù)值>0 不能在創(chuàng)建Number
    //判斷每一個(gè)的上下左右和自己是否相同
    for (int y = 0; y < 4; y++)
    {
        for (int x = 0; x < 4; x++)
        {
            if (cardArr[x][y]->getNumber() == 0 ||
                (x<3 && cardArr[x][y]->getNumber() == cardArr[x+1][y]->getNumber()) ||
                (x>0 && cardArr[x][y]->getNumber() == cardArr[x-1][y]->getNumber()) ||
                (y<3 && cardArr[x][y]->getNumber() == cardArr[x][y+1]->getNumber()) ||
                (y>0 && cardArr[x][y]->getNumber() == cardArr[x][y-1]->getNumber()) )
            {
                isGameOver = false;
            }
        }
    }
 
    if (isWin()) {
 
        successLayer = LayerColor::create(Color4B(0, 0, 0, 180));
        Size winSize = Director::getInstance()->getWinSize();
        Point centerPos = Point(winSize.width / 2, winSize.height / 2);
        auto gameOverTitle = Label::createWithSystemFont("YOU WIN","Consolas",80);
        gameOverTitle->setPosition(centerPos);
        successLayer->addChild(gameOverTitle);
 
        addChild(successLayer,1);
 
        scheduleOnce(SEL_SCHEDULE(&GameScene::removeSuccessLayer), 2);
        return;
    }
 
    //isGameOver = true;
    if (isGameOver)
    {
        log("game over");
        UserDefault::getInstance()->setBoolForKey("history", false);
 
        HighScore::getInstance()->setScore(score);
        GameOverLayer *gameoverLayer = GameOverLayer::create(Color4B(0, 0, 0, 180));
        addChild(gameoverLayer,1);
 
        Director::getInstance()->pause();
    }
    else
    {
        if (shouldCreateCardNumber()) {
            createCardNumber();
 
            saveStatus();
        }
    }
 
}

數(shù)字卡片數(shù)字達(dá)到2048,玩家win的判斷

bool GameScene::isWin()
{
    bool win = false;
    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            if( 2048 == cardArr[i][j]->getNumber() )
            {
                win = true;
                break;
            }
        }
    }
 
    return win;
}

PopLayer

游戲中需要提示玩家勝利或游戲結(jié)束,這類的提示都可以使用popLayer實(shí)現(xiàn)。新建一個(gè)新的Layer對(duì)象,add到當(dāng)前場(chǎng)景上。 下面我們來(lái)添加游戲的暫停和game over的popLayer。他們都是LayerColor的子類。

1、game over層顯示GameOver標(biāo)題和得分、最高分和重玩菜單。關(guān)鍵代碼如下:

bool GameOverLayer::initWithColor(const Color4B& color)
{
if (!LayerColor::initWithColor(color)) {
    return false;
}
 
Size winSize = Director::getInstance()->getWinSize();
Point centerPos = Point(winSize.width / 2, winSize.height / 2);
 
auto gameOverTitle = Label::createWithSystemFont("GAME OVER","Consolas",80);
gameOverTitle->setPosition(Point(centerPos.x, centerPos.y + 150));
addChild(gameOverTitle);
 
char score[64];
sprintf(score, "%d", HighScore::getInstance()->getScore());
auto scoreTitle = Label::createWithSystemFont(score,"Consolas",60);
scoreTitle->setPosition(Point(centerPos.x, centerPos.y + 50));
addChild(scoreTitle);
 
char temp[64];
sprintf(temp, "BEST:%d", HighScore::getInstance()->getHighScore());
auto highScoreTitle = Label::createWithSystemFont(temp,"Consolas",40);
highScoreTitle->setPosition(Point(centerPos.x, centerPos.y - 50));
addChild(highScoreTitle);
 
MenuItemFont::setFontName("Consolas");
MenuItemFont::setFontSize(80);
auto menuItemRestart = MenuItemFont::create("RESTART", CC_CALLBACK_1(GameOverLayer::onRestart, this));
 
auto menu = Menu::create(menuItemRestart, NULL);
addChild(menu);
menu->setPosition(Point(centerPos.x, centerPos.y - 150));
 
//設(shè)置觸摸事件監(jiān)聽(tīng)
auto touchListener = EventListenerTouchOneByOne::create();
touchListener->onTouchBegan = CC_CALLBACK_2(GameOverLayer::onTouchBegan, this);
touchListener->onTouchMoved = CC_CALLBACK_2(GameOverLayer::onTouchMoved, this);
touchListener->onTouchEnded = CC_CALLBACK_2(GameOverLayer::onTouchEnded, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
touchListener->setSwallowTouches(true);
 
return true;
}

2、暫停層顯示繼續(xù)、重玩、退出菜單。關(guān)鍵代碼如下:

bool PopLayer::initWithColor(const Color4B& color)
{
if (!LayerColor::initWithColor(color)) {
    return false;
}
 
Size winSize = Director::getInstance()->getWinSize();
Point centerPos = Point(winSize.width / 2, winSize.height / 2);
 
MenuItemFont::setFontName("Consolas");
MenuItemFont::setFontSize(80);
 
auto menuItemContinue = MenuItemFont::create("CONTINUE", CC_CALLBACK_1(PopLayer::onContinue, this));
auto menuItemRestart = MenuItemFont::create("RESTART", CC_CALLBACK_1(PopLayer::onRestart, this));
auto menuItemExit = MenuItemFont::create("EXIT", CC_CALLBACK_1(PopLayer::onExit, this));
 
auto menu = Menu::create(menuItemContinue, menuItemRestart, menuItemExit, NULL);
menu->alignItemsVertically();
addChild(menu);
menu->setPosition(centerPos);
 
return true;
}

對(duì)比你會(huì)發(fā)現(xiàn)他們之間的實(shí)現(xiàn)方式都差不多。都是Label/Menu組合而成的。

暫停效果圖:

\

gameover效果圖:

\

3、layer屏蔽touch事件

此處有個(gè)問(wèn)題,在popLayer層上觸摸,你會(huì)發(fā)現(xiàn)底層的GameScene會(huì)響應(yīng)。這就需要對(duì)popLayer進(jìn)行touch事件處理屏蔽,不應(yīng)該傳遞到底層。

在inin方法中注冊(cè)touch事件監(jiān)聽(tīng)

//設(shè)置觸摸事件監(jiān)聽(tīng)
auto touchListener = EventListenerTouchOneByOne::create();
touchListener->onTouchBegan = CC_CALLBACK_2(PopLayer::onTouchBegan, this);
touchListener->onTouchMoved = CC_CALLBACK_2(PopLayer::onTouchMoved, this);
touchListener->onTouchEnded = CC_CALLBACK_2(PopLayer::onTouchEnded, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
// 設(shè)置是否吞沒(méi)事件,在 onTouchBegan 方法返回 true 時(shí)吞沒(méi)
touchListener->setSwallowTouches(true);

空實(shí)現(xiàn)touch事件的監(jiān)聽(tīng)函數(shù)

bool GameOverLayer::onTouchBegan(Touch* touch, Event* event)
{
return true;
}
void GameOverLayer::onTouchMoved(Touch* touch, Event* event)
{
}
void GameOverLayer::onTouchEnded(Touch* touch, Event* event)
{
}

數(shù)據(jù)存儲(chǔ)與狀態(tài)恢復(fù)

游戲中需要對(duì)玩家的數(shù)據(jù)進(jìn)行存儲(chǔ)。Cocos2d-x為我們提供了UserDefaults來(lái)管理數(shù)據(jù)存儲(chǔ)。數(shù)據(jù)是通過(guò)指定的Key 字串,按鍵值對(duì)存儲(chǔ)的。游戲中存儲(chǔ)了最高分和玩家的當(dāng)前游戲數(shù)據(jù),以便能從下次游戲恢復(fù)。

我們可以使用如下方法往文件中存儲(chǔ)數(shù)據(jù),下面是簡(jiǎn)單的以4*4矩陣的行列作為key,存儲(chǔ)該數(shù)字卡片的數(shù)值。

//存儲(chǔ)最高分
UserDefault::getInstance()->setIntegerForKey(KHIGHSCORE, score);
//
char temp[10];
//4*4
for (int i = 0; i<4; i++) {
    for(int j = 0; j<4; j++)
    {
        sprintf(temp,"%d%d",i,j);
        UserDefault::getInstance()->setIntegerForKey(temp, cardArr[i][j]->getNumber());
    }
}

通過(guò)如下方法從文件中取出數(shù)據(jù),以4*4矩陣的行列作為key,取出當(dāng)前數(shù)字卡片的數(shù)值。

char temp[10];
//4*4
for (int i = 0; i<4; i++) {
    for(int j = 0; j<4; j++)
    {
        sprintf(temp,"%d%d",i,j);
        int number = UserDefault::getInstance()->getIntegerForKey(temp);
        cardArr[i][j]->setNumber(number);
    }
}

Build && Run

此處,我們是通過(guò)xcode作為開(kāi)發(fā)工具進(jìn)行開(kāi)發(fā)和編譯運(yùn)行調(diào)試。但是xcode不支持Android的開(kāi)發(fā),那么如何進(jìn)行Android的編譯與運(yùn)行呢?

在Cocos2d-x v3.0 加入了新的cocos命令。按照上一章節(jié)開(kāi)始處講的建立cocos命令環(huán)境。在命令行窗口執(zhí)行下命令,查看cocos 相關(guān)的命令使用:

IvenYangtekiMacBook-Pro-2:~ zeroyang$ cocos --help
 
/Users/zeroyang/Documents/work/cocos2d-x-3.0/tools/cocos2d-console/bin/cocos.py 0.2 - cocos console: A command line tool for cocos2d
 
Available commands:
    compile      Compiles the current project to binary
    new          Creates a new project
    run          Compiles & deploy project and then runs it on the target
    jscompile    minifies and/or compiles js files
    deploy       Deploy a project to the target
 
Example:
    /Users/zeroyang/Documents/work/cocos2d-x-3.0/tools/cocos2d-console/bin/cocos.py new --help
    /Users/zeroyang/Documents/work/cocos2d-x-3.0/tools/cocos2d-console/bin/cocos.py run --help

按提示,我們直接執(zhí)行

$ cocos run -p android

你會(huì)發(fā)現(xiàn)build不通過(guò),仔細(xì)檢查錯(cuò)誤信息,

jni/../../Classes/AppDelegate.cpp:31: error: undefined reference to 'GameScene::createScene()'
collect2: error: ld returned 1 exit status
make: *** [obj/local/armeabi/libcocos2dcpp.so] Error 1
make: Leaving directory `/Users/zeroyang/Documents/2048/proj.android'
Error running command, return code: 2

你會(huì)發(fā)現(xiàn),新建的class文件夾下的cpp沒(méi)被包含進(jìn)android的編譯文件,修改proj.android/jni/android.mk 加入。修改如下:

LOCAL_SRC_FILES := hellocpp/main.cpp \
                   ../../Classes/AppDelegate.cpp \
                   ../../Classes/Card.cpp \
                   ../../Classes/GameOverLayer.cpp \
                   ../../Classes/GameScene.cpp \
                   ../../Classes/HighScore.cpp \
                   ../../Classes/PopLayer.cpp \
                   ../../Classes/SimpleRecognizer.cpp

修改后繼續(xù)執(zhí)行cocos run -p android。 運(yùn)行效果如圖:

\

總結(jié)

在2048游戲的制作中,我們學(xué)習(xí)了如何創(chuàng)建一個(gè)新的工程,創(chuàng)建自己的Scene,并add 背景l(fā)ayer。 如何創(chuàng)建label顯示標(biāo)題,分?jǐn)?shù)。如何處理touch事件,如何添加菜單處理菜單事件。以及poplayer的添加,數(shù)據(jù)存儲(chǔ)等。

至此,一個(gè)簡(jiǎn)單的2048游戲已經(jīng)完成。游戲甚是簡(jiǎn)陋,還需小伙伴們完善。

源碼地址:http://git.oschina.net/ZeroYang/Tutorial-2048/repository/archive?ref=master

關(guān)鍵詞:Cocos2d-x2048游戲

贊助商鏈接:

主站蜘蛛池模板: 望奎县| 呼图壁县| 英超| 平南县| 临漳县| 正宁县| 舞阳县| 桑日县| 西充县| 德清县| 双峰县| 库车县| 蓬莱市| 侯马市| 清远市| 蓬莱市| 共和县| 邵阳县| 万源市| 瑞丽市| 永定县| 洛隆县| 永川市| 留坝县| 吉木萨尔县| 寿宁县| 枞阳县| 赣榆县| 龙口市| 景洪市| 巨野县| 铁岭市| 万州区| 广饶县| 巫溪县| 尼玛县| 临沂市| 天柱县| 永城市| 栾川县| 鸡西市|