来迟了的1024

来迟了的1024(也就是2048啦),用了几个晚上写出了游戏的主要逻辑,不曾想到有大神已经发出了教程贴,是啊,晚了一步!本人一直犹豫是否要把他发出来,然而思前想后还是发出来吧,一来每个人的代码思路是不同的,没有最好的,只要最适合的,各位可以按照自己的喜好去选择不同的实现方式,二来毕竟写了几个晚上,不发出来实在是可惜啦,三来看着本论坛人气越来越高,本水货版主实在不忍心自己如此的水下去,发发教程,给论坛多增加些人气吧。本人的实现方式同样是分步骤完成的,这里发主要逻辑的实现教程。

http://www.zaojiahua.com/gesture-of-judgment.html

http://www.zaojiahua.com/movement-cards-cards.html

http://www.zaojiahua.com/card-flips-and-eliminating.html

添加UI

上一次的博客写到了1024游戏主要逻辑的实现,本篇博客继续往下完善我们的游戏——添加UI。添加UI这些操作相对就简单了一些,我们就当回忆了一下以前的知识,这里主要涉及的就是场景的切换,菜单的使用,按钮的使用,半透明的弹出层的实现,游戏数据的重置(写的时候遇到的问题就是这个)。好了,把最后的成果拿上来先展示一下吧。

我们先来写一下这个游戏的开始界面吧!代码很简单,就是添加了几张图片,几个按钮,然后添加按钮的事件响应。

#ifndef _START_SCENE_H_
#define _START_SCENE_H_
#include "cocos2d.h"
//扩展库,因为用到了CCControlButton
#include "cocos-ext.h"
//前置声明,由于头文件相互包含将导致编译错误,解决的方法就是在头文件中
//包含另一个类的前置声明,在.cpp文件中使用include进行包含
class MainGameScene;

using namespace cocos2d;
using namespace cocos2d::extension;

class StartScene : public CCLayer
{
public:
    static CCScene * scene();
    bool init();
    CREATE_FUNC(StartScene);
    //button按钮的响应函数
    void helpClicked(CCObject * obj);
    void scoreClicked(CCObject * obj);
    void startClicked(CCObject * obj);
private:
    CCSize m_size;
};

#endif



```

#include "StartScene.h"
//这里包含头文件
#include "MainGameScene.h"

CCScene * StartScene::scene()
{
    CCScene * scene = CCScene::create();

    StartScene * layer = StartScene::create();

    scene->addChild(layer);

    return scene;
}

bool StartScene::init()
{
    if(!CCLayer::init())
    {
        return false;
    }

    m_size = CCDirector::sharedDirector()->getWinSize();

    //添加背景图片
    CCSprite * home = CCSprite::create("home.png");
    home->setPosition(ccp(m_size.width/2,m_size.height/2));
    this->addChild(home);

    //添加logo
    CCSprite * logo = CCSprite::create("logo.png");
    logo->setPosition(ccp(m_size.width/2,m_size.height*0.9));
    this->addChild(logo);

    //添加帮助按钮
    CCMenuItemImage * help = CCMenuItemImage::create("help.png","help.png",
        this,menu_selector(StartScene::helpClicked));
    //添加分数按钮
    CCMenuItemImage * score = CCMenuItemImage::create("score.png","score.png",
        this,menu_selector(StartScene::scoreClicked));

    CCMenu * menu1 = CCMenu::create(score,help,NULL);
    menu1->alignItemsHorizontallyWithPadding(350);
    menu1->setPosition(ccp(m_size.width/2,m_size.height*0.9));
    this->addChild(menu1);

    //添加开始游戏按钮
    CCMenuItemImage * start = CCMenuItemImage::create("start_normal.png","start_clicked.png",
        this,menu_selector(StartScene::startClicked));
    CCMenu * menu2 = CCMenu::create(start,NULL);
    menu2->setPosition(ccp(m_size.width/2,m_size.height*0.15));
    this->addChild(menu2);

    return true;
}

void StartScene::helpClicked(CCObject * obj)
{
    //这里切换到帮助场景
    CCLog("help");
}

void StartScene::scoreClicked(CCObject * obj)
{
    //这里切换到分数榜场景
    CCLog("score");
}

void StartScene::startClicked(CCObject * obj)
{
    //切换到游戏开始场景
    CCDirector::sharedDirector()->replaceScene(MainGameScene::scene());
}



```

帮助按钮和分数榜我没有实现,因为这个用到了列表,打算专门用一篇博客来写。点击开始按钮切换到游戏主场景中。在主场景我们看到了有一些UI,比如分数,和那个暂停的按钮,我不在主场景中添加这些元素,而是新建了一个层,叫做UILayer来专门处理UI,下面看看代码吧。

#ifndef _UI_LAYER_H_
#define _UI_LAYER_H_
#include "cocos2d.h"
//包含扩展库
#include "cocos-ext.h"
//弹出层
#include "PopScene.h"

using namespace cocos2d;
using namespace cocos2d::extension;

//这个类用到了单例的设计模式
class MyUILayer : public CCLayer
{
public:
    bool init();
    //更新游戏的得分
    void setScore(int score);
    //重置游戏的得分
    void resetScore(){score = 0;};
    //单例的实现
    static MyUILayer * sharedMyUILayer();
    static void freeMyUILayer();
    //暂停按钮按下的回调函数
    void pausePress(CCObject * obj,CCControlEvent evt);
private:
    static MyUILayer * m_MyUILayer;
    //私有化构造函数
    MyUILayer(){};
    CCSize m_size;
    //分数文本
    CCLabelBMFont * m_score;
    //记录分数值
    int score;
    //滚动字幕文本
    CCLabelTTF * m_label;
    //滚动字幕
    void scrollLabel(float tm);
};

#endif



```

#include "MyUILayer.h"

//单例的固定写法,不说了
MyUILayer * MyUILayer::m_MyUILayer = NULL;

MyUILayer * MyUILayer::sharedMyUILayer()
{
    if(m_MyUILayer == NULL)
    {
        m_MyUILayer = new MyUILayer();
        m_MyUILayer->init();
    }
    return m_MyUILayer;
}

void MyUILayer::freeMyUILayer()
{
    if(m_MyUILayer != NULL)
    {
        delete m_MyUILayer;
        m_MyUILayer = NULL;
    }
}

bool MyUILayer::init()
{
    if(!CCLayer::init())
    {
        return false;
    }

    this->m_size = CCDirector::sharedDirector()->getWinSize();
    //刚开始的时候score为0
    score = 0;

    //添加分数显示的文本
    m_score = CCLabelBMFont::create("0","ScoreNewRec.fnt");
    m_score->setPosition(ccp(m_size.width/2,m_size.height*0.83));
    this->addChild(m_score,1);

    //添加滚动字幕
    m_label = CCLabelTTF::create("welcome to %url% ! this is xiaota 1024 !","Arial",40);
    m_label->setColor(ccc3(2,200,200));
    m_label->setPosition(ccp(m_size.width/2,m_size.height*0.83));
    this->addChild(m_label,0);
    //滚动字幕
    this->schedule(schedule_selector(MyUILayer::scrollLabel),0.005f);

    //添加暂停按钮
    CCScale9Sprite * scale9 = CCScale9Sprite::create("pause_normal.png");
    CCLabelTTF * label_pause = CCLabelTTF::create("Pause","",42);

    CCControlButton * pause = CCControlButton::create(label_pause,scale9);
    pause->setPosition(ccp(m_size.width-pause->getContentSize().width,m_size.height*0.93));
    //添加事件
    pause->addTargetWithActionForControlEvents(this,
        cccontrol_selector(MyUILayer::pausePress),CCControlEventTouchDown);
    this->addChild(pause);

    return true;
}

//暂停按钮点击以后的执行事件
void MyUILayer::pausePress(CCObject * obj,CCControlEvent evt)
{
    //弹出一个层
    PopScene * scene = PopScene::create();
    this->addChild(scene);
}

//更新玩家分数
void MyUILayer::setScore(int score)
{
    this->score += score;
    CCString * str = CCString::createWithFormat("%d",this->score);
    this->m_score->setString(str->getCString());
}

//滚动字幕的实现,我前边的博客有,不理解的可以看看原来的博客
void MyUILayer::scrollLabel(float tm)
{
    CCPoint point = m_label->getPosition();
    if(point.x < -getContentSize().width/2)
    {
        m_label->setPosition(ccp(m_size.width+m_label->getContentSize().width/2,point.y));
        return;
    }
    this->m_label->setPosition(ccp(point.x-2,point.y));
}



```

点击暂停按钮添加了一个层,关于这个层的实现我们等下再说,先来完成游戏分数的更新操作!那么什么时候来更新玩家的分数呢,当然是消除了卡片以后更新了,所以setScore函数的调用就放到了卡片管理器中了,在clear函数中添加如下的代码。


//设置玩家分数
    MyUILayer::sharedMyUILayer()->setScore(tag);


```

每消除一张卡片把他的分数加到上面去,很简单吧!好了你现在可以运行游戏看看分数是否添加上去了,接下来就是那个暂停按钮的响应了,我这里又添加了一个层,那我们就看看这个层里边做了什么吧。

#ifndef _POP_SCENE_H_
#define _POP_SCENE_H_
#include "cocos2d.h"

//为了防止相互包含的问题出现,导致编译出错,需要在头文件中使用前置声明,在.cpp文件中包含头文件
class MainGameScene;
#include "StartScene.h"
#include "Message.h"

using namespace cocos2d;

class PopScene : public CCLayerColor
{
public:
    bool init();
    CREATE_FUNC(PopScene);
    //resume
    void resume(CCObject * obj);
    //retry
    void retry(CCObject * obj);
    //quit
    void quit(CCObject * obj);
    //触摸
    void registerWithTouchDispatcher();
    bool ccTouchBegan(CCTouch * touch,CCEvent * evt);
};

#endif



```

#include "PopScene.h"
#include "MainGameScene.h"

bool PopScene::init()
{
    if(!CCLayerColor::initWithColor(ccc4(100,100,100,100)))
    {
        return false;
    }

    //创建三个菜单
    CCMenuItemImage * resume = CCMenuItemImage::create("resume.png","resume.png",
        this,menu_selector(PopScene::resume));
    CCMenuItemImage * retry = CCMenuItemImage::create("retry.png","retry.png",
        this,menu_selector(PopScene::retry));
    CCMenuItemImage * quit = CCMenuItemImage::create("quit.png","quit.png",
        this,menu_selector(PopScene::quit));

    CCMenu * menu = CCMenu::create(resume,retry,quit,NULL);
    menu->alignItemsVerticallyWithPadding(50);
    //添加到层中
    this->addChild(menu);

    //开启触摸,屏蔽下层对触摸的接受
    this->setTouchEnabled(true);

    return true;
}

//设置触摸的优先级
void PopScene::registerWithTouchDispatcher()
{
    //菜单的优先级为-128,这样的话,本层的菜单可以接收到触摸
    CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,-128,true);
}

bool PopScene::ccTouchBegan(CCTouch * touch,CCEvent * evt)
{
    //返回true表示吞噬消息,下层将接受不到消息
    return true;
}

void PopScene::resume(CCObject * obj)
{
    //将当前层清除从父节点中
    this->removeFromParentAndCleanup(true);
}

void PopScene::retry(CCObject * obj)
{
    this->removeFromParentAndCleanup(true);
    //发送重置数据的消息
    CCNotificationCenter::sharedNotificationCenter()->postNotification(MESSAGE_TYPE_RESET_DATA,NULL);
}

void PopScene::quit(CCObject * obj)
{
    //返回到开始场景
    CCDirector::sharedDirector()->replaceScene(StartScene::scene());
}



```

这边的代码不难理解吧,其他的东西都不说,这里只说一个比较重要的东西,就是点击重新开始游戏按钮以后的事件响应函数,首先我们将这个层从父节点中移除了出去,然后发送了一个消息,同样消息的类型定义在message.h中,我们的这个消息是给chess发送过去了,下面看看chess的init函数中添加的一段监听消息的代码吧。
//监听重置数据的消息
    CCNotificationCenter::sharedNotificationCenter()->addObserver(this,
        callfuncO_selector(Chess::resetData),MESSAGE_TYPE_RESET_DATA,NULL);



```

chess监听了重置数据的消息,当消息发送过来的时候调用了resetData函数,那么现在看看这个函数是如何实现的吧。
void Chess::resetData(CCObject * obj)
{
    CCObject * object;
    //将卡片管理器中的卡片全部取出来然后remove掉
    CCARRAY_FOREACH(m_cubeManager->getCubeArray(),object)
    {
        CCSprite * cube = (CCSprite *)object;
        cube->removeFromParentAndCleanup(true);
    }
    //重置分数
    MyUILayer::sharedMyUILayer()->resetScore();
    MyUILayer::sharedMyUILayer()->setScore(0);

    //将卡片管理器中的卡片全部移除
    this->m_cubeManager->getCubeArray()->removeAllObjects();

    //在棋盘中添加俩个2的格子
    CCSprite * randSprite_1 = this->m_cubeManager->addCubeToChess();
    //添加到棋盘中
    m_chess->addChild(randSprite_1);

    //使用同样的方法添加另一个2的格子
    CCSprite * randSprite_2 = this->m_cubeManager->addCubeToChess();
    m_chess->addChild(randSprite_2);
}



```

函数中重置了一下分数label,同时将卡片管理器中的卡片全部移除了出去,这样就完成了对数据的重置!好了,运行程序,最后的效果图就是你在最开始看到的啦!

这些代码都是在哪个文件家里写的- -
新手有点蒙

恩,这个整理的不好,不好意思,现在加班从弄一下。

:2: :2: :2: :2: 强烈支持楼主,嘿嘿

小塔加油哦

谢谢各位!

不错不错!赞一个

您的文章已被推荐到CocoaChina首页热门文章精选,感谢您的分享。:7::7:

:2:升级下整到4096好么亲

这个是使用的是2.0+还是3.0版本的

:2::2::2::2:

我是新手 能上传压缩文件吗 想下了学习一下

游戏的名字无所谓吧,写代码的思路是一样的,不要在乎这个。

是2.2的版本,老版本,如果想用新的版本,论坛有个帖子是用的3.0,可以参考下。

楼主可以提供源代码下载么?谢谢楼主哈

:2::2::2::2::2::2::2:

MARK
一直想做一个自己的APP,但是一直在迷茫。。。

给楼主赞一个!加油!:7:

Mark!!!!

强的掉渣啊,2048时间太长了吧。