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

小塔1024——添加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 www.zaojiahua.com ! 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);
#ifndef _MESSAGE_H_
#define _MESSAGE_H_

#define MESSAGE_TYPE_ADD_CUBE "add_cube"
#define MESSAGE_TYPE_RESET_DATA "resetData"

#endif

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