文档章节

cocos2dx-v3.5 2048(三):菜单实现

小灰灰Blog
 小灰灰Blog
发布于 2015/04/29 15:44
字数 1967
阅读 101
收藏 0

行业解决方案、产品招募中!想赚钱就来传!>>>

前言



本节主要包括菜单栏的绘制以及添加触发事件,菜单栏又分为两级,如下面两张图,当点击set时,出现模式选择的菜单项。这里主要利用到了 MenuItemLabel进行菜单的实现

image

image

 

设计


对于菜单栏的设计,我们主要从以下两个方面进行:

  • 菜单的绘制

  • 触发事件处理

1. 菜单的绘制

    本处的菜单实际而言仍旧是label的绘制,然后封装到 MenuItemLabel中,为其添加回调事件,Label的创建与绘制前一节已经说明,因此此处主要注意的是布局问题,具体可参考后面贴出的代码

auto label = Label::createWithSystemFont(text, "Airea", 26);
//label->setTextColor(Color4B(120,120,0, 255));

auto item = MenuItemLabel::create(label);
item->setContentSize(size);

    对于set界面的绘制中,可以看到一条白色的线段,同样是调用cocos2dx的API进行绘制,主要是DrawNode对象的相关方法

auto draw = DrawNode::create();
this->addChild(draw);
draw->drawLine(Vec2(10, 20), Vec2(50, 20), Color4F(0, 0, 0, 1));

    drawLine(起点, 终点, 颜色) 绘制一条线段, drawCircle(..)绘制圆形。。。。。。


2. 触发事件处理

    MenuItem有个回调函数setCallBack()可以直接设定点击后的回调函数,如:

resetmenu->setCallback(CC_CALLBACK_1(GameMenuLayer::resetGameFun, this));

    表示当点击resetmenu后,将调用GameMenuLayer::resetGameFun函数,其源码为:

void GameMenuLayer::resetGameFun(Ref* ref)
{
	GameLayer::getInstance()->restartGame();
	log("reset game");
}


3. CC_CALLBACK_1

    cocos2dx定义的宏,广泛应用于回调函数的处理中,其声明为:

// new callbacks based on C++11
#define CC_CALLBACK_0(__selector__,__target__, ...) std::bind(&__selector__,__target__, ##__VA_ARGS__)
#define CC_CALLBACK_1(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, ##__VA_ARGS__)
#define CC_CALLBACK_2(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, ##__VA_ARGS__)
#define CC_CALLBACK_3(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, ##__VA_ARGS__)

    后面跟随的0,1,2,3分别表示回调函数带0,1,2,3个参数,这里利用了c++11的新特性,使用了std::bind函数

    std:: bind函数实现类似函数指针的方法,采用传值传递参数,在对某个函数进行绑定时,可以指定部分参数或全部参数,也可以不指定任何参数,还可以调整各个参数间的顺序。对于未指定的参数,可以使用占位符_1、_2、_3来表示。_1表示绑定后的函数的第1个参数,_2表示绑定后的函数的第2个参数,其他依次类推

bind(callable, arg_list)

    其中的callbale表示函数指针,arg_list表示参数列表

#include <iostream>
#include <functional>
using namespace std::placeholders;
using namespace std;
void nine(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9)
{
cout << "a1 = " << a1 << endl;
cout << "a2 = " << a2 << endl;
cout << "a3 = " << a3 << endl;
cout << "a4 = " << a4 << endl;
cout << "a5 = " << a5 << endl;
cout << "a6 = " << a6 << endl;
cout << "a7 = " << a7 << endl;
cout << "a8 = " << a8 << endl;
cout << "a9 = " << a9 << endl;
}

int main()

{
	int i = 5;
	cout << "i == 5?" << (i==5) << endl;
	int i1=1,i2=2,i3=3,i4=4,i5=5,i6=6,i7=7,i8=8,i9=9;
	bind(nine, _9, _8, _7, _6, _5,_4,_3,_2,_1)(i1, i2, i3, i4,i5,i6,i7,i8, i9);
	bind(nie, i1, i2, i3, i4, i5, i6, _1, _2, _1)(i9, i7);
	return 0;


}

    输出结果为:

image

 


实现


    菜单栏的实现主要涉及到GameMenuLayer, MenuButton, SetMenu三个文件

  1.         MenuButton: 生成MenuItemLabel项并返回

  2.         GameMenuLayer: 则包含显示三个主菜单reset, undo, set,并实现对应的回调函数

  3.         SetMenu: 则显示set子菜单项目,并实现对应的回调函数(即模式切换)

所有的代码均托管在: ttps://github.com/liuyueyi/2048

MenuButton.cpp

#include "MenuButton.h"

bool MenuButton::init()
{
	if(!Node::init())
		return false;
	else
		return true;
}

MenuItem* MenuButton::getMenuItem(const std::string& text, const Size& size)
{
	auto label = Label::createWithSystemFont(text, "Airea", 26);
	//label->setTextColor(Color4B(120,120,0, 255));

	auto item = MenuItemLabel::create(label);
	item->setContentSize(size);

	return item;
}

GameMenuLayer.cpp

#include "GameMenuLayer.h"
#include "MenuButton.h"
#include "GameLayer.h"
#include "GameScene.h"
#include "SetMenu.h"

USING_NS_CC;

GameMenuLayer* GameMenuLayer::_instance = nullptr;
GameMenuLayer* GameMenuLayer::getInstance()
{
	if(_instance == nullptr)
		_instance = create();
	return _instance;
}

bool GameMenuLayer::init()
{
	if(!Layer::init())
		return false;

	this->setContentSize(Size(300, 30));
	this->setPosition(Vec2(10, 370));

	auto menuButton = MenuButton::create();
	auto resetBg = LayerColor::create(Color4B(143, 122, 101, 255), 100, 30);
	this->addChild(resetBg);
	auto resetmenu = menuButton->getMenuItem("Restart", Size(100, 30));
	resetmenu->setPosition(55, 17);
	resetmenu->setCallback(CC_CALLBACK_1(GameMenuLayer::resetGameFun, this));
	
	auto setBg = LayerColor::create(Color4B(143, 122, 101, 255), 50, 30);
	setBg->setPosition(250, 0);
	this->addChild(setBg);
	auto setmenu = menuButton->getMenuItem("Set", Size(40, 30));
	setmenu->setPosition(275, 17);
	setmenu->setCallback(CC_CALLBACK_1(GameMenuLayer::setGameFun, this));
	
	auto undoBg = LayerColor::create(Color4B(143, 122, 101, 255), 60, 30);
	undoBg->setPosition(180, 0);
	this->addChild(undoBg);
	auto undomenu = menuButton->getMenuItem("Undo", Size(60, 30));
	undomenu->setPosition(213, 17);
	undomenu->setCallback(CC_CALLBACK_1(GameMenuLayer::undoGameFun, this));

	auto menu = Menu::create(resetmenu, undomenu, setmenu, NULL);
	//menu->alignItemsHorizontally();
	menu->setPosition(0,0);

	this->addChild(menu);
	return true;
}

void GameMenuLayer::resetGameFun(Ref* ref)
{
	GameLayer::getInstance()->restartGame();
	log("reset game");
}

void GameMenuLayer::setGameFun(Ref* ref)
{
	auto layer = static_cast<GameScene*>(this->getParent());
	auto setmenu = layer->getChildByName("setlayer");
	if(setmenu->isVisible())
		setmenu->setVisible(false);
	else
		setmenu->setVisible(true);
	log("start game");
}

void GameMenuLayer::undoGameFun(Ref* ref)
{
	GameLayer::getInstance()->undoGame();
	log("back to last stat");
}

此处对于void GameMenuLayer::setGameFun(Ref* ref) 进行简单说明, 这次设计中没有将SetMenu设置为单例模式,添加到父节点的是SetMenu的一个对象,这样的情况下,可以通过getChildByName, getChindByTag函数来获取该对象,然后进行相应的修改

对应的将SetMenu加入场景的代码在GameScene的init函数内:

auto setLayer = SetMenu::create();
	setLayer->setName("setlayer");
	setLayer->setVisible(false);
	this->addChild(setLayer);


 SetMenu.cpp

#include "SetMenu.h"
#include "Grid.h"
#include "GameTool.h"
#include "GameLayer.h"
#include "DataConf.h"

bool SetMenu::init()
{
	if(!Layer::init())
		return false;

	this->setContentSize(Size(120, 180));
	this->setPosition(Vec2(195, 185));

	auto bg = LayerColor::create(Color4B(30, 30, 30, 200), 120, 180);
	this->addChild(bg);

	auto draw = DrawNode::create();
	this->addChild(draw);
	//draw->drawLine(Vec2(10, 20), Vec2(50, 20), Color4F(0, 0, 0, 1));

	auto classic = Label::createWithSystemFont(Grid::G2U("经典模式"), "Arial", 25);
	auto soldier = Label::createWithSystemFont(Grid::G2U("小兵模式"), "Arial", 25);
	auto color = Label::createWithSystemFont(Grid::G2U("纯色模式"), "Arial", 25);
	auto sound = Label::createWithSystemFont(Grid::G2U("声音"), "Arial", 25);

	auto item01 = MenuItemLabel::create(classic, CC_CALLBACK_1(SetMenu::classicCallFunc, this));
	item01->setPosition(60, 135 + 22.5f);
	draw->drawLine(Vec2(5, 135), Vec2(115, 135), Color4F(1,1,1,1));
	auto item02 = MenuItemLabel::create(soldier, CC_CALLBACK_1(SetMenu::soldierCallFunc, this));
	item02->setPosition(60, 90 + 22.5f);
	draw->drawLine(Vec2(5, 90), Vec2(115, 90), Color4F(1, 1, 1,1));
	auto item03 = MenuItemLabel::create(color, CC_CALLBACK_1(SetMenu::colorCallFunc, this));
	item03->setPosition(60, 45 + 22.5f);
	draw->drawLine(Vec2(5, 45), Vec2(115, 45), Color4F(1, 1, 1,1));
	auto item04 = MenuItemLabel::create(sound, CC_CALLBACK_1(SetMenu::soundCallFunc, this));
	item04->setPosition(60, 22.5f);
	//draw->drawLine(Vec2(5, 22.5f), Vec2(115, 157.5f), Color4F(0,0,0,1));

	auto menu = Menu::create(item01, item02, item03, item04, nullptr);
	//menu->alignItemsVertically();
	menu->setPosition(0, 0);
	this->addChild(menu);
	return true;
}

void SetMenu::classicCallFunc(Ref* ref)
{
	changeType(1);
}

void SetMenu::soldierCallFunc(Ref* ref)
{
	changeType(0);
}

void SetMenu::colorCallFunc(Ref* ref)
{
	changeType(2);
}

void SetMenu::soundCallFunc(Ref* ref)
{

}

void SetMenu::changeType(int newType)
{
	auto type = Grid::getType();
	if(type == newType) // do not change the game model
		return;
	// do change, then save the game stat and restart new game model
	DataConf::getInstance()->dumpData(type); 
	GameLayer::getInstance()->clearGrids();
	Grid::changeType(newType);
	this->setVisible(false);
}


Label中文显示以及直接添加点击事件


1. 中文显示

    Label创建时,直接赋值中文时,会出现问题(不显示内容,或者乱码),主要是编码格式的问题造成的,其解决方法有从xml,json中读取中文,然后传入;或者直接采用下面函数对中文重新编码

char* Grid::G2U(const char* gb2312)
{
	int len = MultiByteToWideChar(CP_ACP, 0, gb2312, -1, NULL, 0);
	wchar_t* wstr = new wchar_t[len+1];
	memset(wstr, 0, len+1);
	MultiByteToWideChar(CP_ACP, 0, gb2312, -1, wstr, len);
	len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
	char* str = new char[len+1];
	memset(str, 0, len+1);
	WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);
	if(wstr) delete[] wstr;
	return str;
}


2. 事件绑定 

    直接在Label上绑定事件,利用EventListenerTouchOneByOne来实现绑定

auto label = Label::createWithSystemFont("label", "Arial", 40);
this->addChild(label);
label->setPosition(100, 100);
auto listener = EventListenerTouchOneByOne::create();
listener->onTouchBegan = [label](Touch* touch, Event* e){
	log("entered”);
	if(label->getBoundingBox().containsPoint(touch->getLocation()))
		log("truly clicked");
	return false;
};
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, label);


注意:

  • TouchBegan调用的函数内部的if语句,表示当确切的点击到label时,才会执行相应的代码,也因此需要将具体的逻辑判断写在if语句内

  • 注册listener,除了上面的方法外也可以用_eventDispatcher直接进行,两者效果相同(?)

    • _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, label);
  • 直接绑定和用MenuItem的区别:具体的原理没有去详细研究,就表观而言,MenuItem点击时,会有明显的标签字体放大的动画效果,而直接绑定触发事件时没有相应动画效果,其次就便捷性而言,MenuItem编程实现更加方便


小灰灰Blog

小灰灰Blog

粉丝 239
博文 291
码字总数 526351
作品 0
武汉
程序员
私信 提问
加载中
请先登录后再评论。
Netty那点事(三)Channel与Pipeline

Channel是理解和使用Netty的核心。Channel的涉及内容较多,这里我使用由浅入深的介绍方法。在这篇文章中,我们主要介绍Channel部分中Pipeline实现机制。为了避免枯燥,借用一下《盗梦空间》的...

黄亿华
2013/11/24
2W
22
用vertx实现高吞吐量的站点计数器

工具:vertx,redis,mongodb,log4j 源代码地址:https://github.com/jianglibo/visitrank 先看架构图: 如果你不熟悉vertx,请先google一下。我这里将vertx当作一个容器,上面所有的圆圈要...

jianglibo
2014/04/03
4K
3
SQLServer实现split分割字符串到列

网上已有人实现sqlserver的split函数可将字符串分割成行,但是我们习惯了split返回数组或者列表,因此这里对其做一些改动,最终实现也许不尽如意,但是也能解决一些问题。 先贴上某大牛写的s...

cwalet
2014/05/21
9.6K
0
Swift百万线程攻破单例(Singleton)模式

一、不安全的单例实现 在上一篇文章我们给出了单例的设计模式,直接给出了线程安全的实现方法。单例的实现有多种方法,如下面: class SwiftSingleton { } 这段代码的实现,在shared中进行条...

一叶博客
2014/06/20
3.3K
16
程序猿媛一:Android滑动翻页+区域点击事件

滑动翻页+区域点击事件 ViewPager+GrideView 声明:博文为原创,文章内容为,效果展示,思路阐述,及代码片段。文尾附注源码获取途径。 转载请保留原文出处“http://my.oschina.net/gluoyer...

花佟林雨月
2013/11/09
4.2K
1

没有更多内容

加载失败,请刷新页面

加载更多

什么是移动语义? - What is move semantics?

问题: I just finished listening to the Software Engineering radio podcast interview with Scott Meyers regarding C++0x . 我刚刚结束了对Scott Meyers进行的有关C ++ 0x的Software En......

技术盛宴
47分钟前
24
0
算法与数据结构体系课

算法与数据结构体系课【超清原画】 下载地址:百度云盘 从0到工作5年,面试、进大厂、搭建知识体系、拓展技术上限 你不再需要其它算法与数据结构课程了 为什么学算法已经是一个不应该问的问题...

1930133570
今天
21
0
如何停止跟踪并忽略对Git中文件的更改? - How to stop tracking and ignore changes to a file in Git?

问题: I have cloned a project that includes some .csproj files. 我已经克隆了一个包含一些.csproj文件的项目。 I don't need/like my local csproj files being tracked by Git (or bei......

富含淀粉
今天
25
0
Redis阻塞

可能存在问题 内在原因:API或数据结构使用不合理、CPU饱和、持久化阻塞等 外在原因:CPU竞争、内存交换、网络问题等 问题处理: API或数据结构使用不合理,可能存在慢查询或者大对象: 发现...

游泳鸟
今天
17
0
OSChina 周五乱弹 —— 来人,上幼儿园老师跳舞的图!

Osc乱弹歌单(2020)请戳(这里) 【今日歌曲】 小小编辑:《奇跡の海》- 坂本真綾 《奇跡の海》- 坂本真綾 手机党少年们想听歌,请使劲儿戳(这里) 巴蜀(@巴拉迪维)最近有点闹心了, @巴...

小小编辑
今天
64
1

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部