来迟了的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,同时将卡片管理器中的卡片全部移除了出去,这样就完成了对数据的重置!好了,运行程序,最后的效果图就是你在最开始看到的啦!

