前言
本节主要包括菜单栏的绘制以及添加触发事件,菜单栏又分为两级,如下面两张图,当点击set时,出现模式选择的菜单项。这里主要利用到了 MenuItemLabel进行菜单的实现
设计
对于菜单栏的设计,我们主要从以下两个方面进行:
菜单的绘制
触发事件处理
1. 菜单的绘制
本处的菜单实际而言仍旧是label的绘制,然后封装到 MenuItemLabel中,为其添加回调事件,Label的创建与绘制前一节已经说明,因此此处主要注意的是布局问题,具体可参考后面贴出的代码
autolabel=Label::createWithSystemFont(text,"Airea",26); //label->setTextColor(Color4B(120,120,255)); autoitem=MenuItemLabel::create(label); item->setContentSize(size);
对于set界面的绘制中,可以看到一条白色的线段,同样是调用cocos2dx的API进行绘制,主要是DrawNode对象的相关方法
autodraw=DrawNode::create(); this->addChild(draw); draw->drawLine(Vec2(10,20),Vec2(50,Color4F(0,1));
drawLine(起点, 终点, 颜色) 绘制一条线段, drawCircle(..)绘制圆形。。。。。。
2. 触发事件处理
MenuItem有个回调函数setCallBack()可以直接设定点击后的回调函数,如:
resetmenu->setCallback(CC_CALLBACK_1(GameMenuLayer::resetGameFun,this));
表示当点击resetmenu后,将调用GameMenuLayer::resetGameFun函数,其源码为:
voidGameMenuLayer::resetGameFun(Ref*ref) { GameLayer::getInstance()->restartGame(); log("resetgame"); }
3. CC_CALLBACK_1
cocos2dx定义的宏,广泛应用于回调函数的处理中,其声明为:
//newcallbacksbasedonC++11 #defineCC_CALLBACK_0(__selector__,__target__,...)std::bind(&__selector__,##__VA_ARGS__) #defineCC_CALLBACK_1(__selector__,std::placeholders::_1,##__VA_ARGS__) #defineCC_CALLBACK_2(__selector__,std::placeholders::_2,##__VA_ARGS__) #defineCC_CALLBACK_3(__selector__,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> usingnamespacestd::placeholders; usingnamespacestd; voidnine(inta1,inta2,inta3,inta4,inta5,inta6,inta7,inta8,inta9) { 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; } intmain() { inti=5; cout<<"i==5?"<<(i==5)<<endl; inti1=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,_1,_1)(i9,i7); return0; }
输出结果为:
实现
菜单栏的实现主要涉及到GameMenuLayer,MenuButton,SetMenu三个文件
MenuButton: 生成MenuItemLabel项并返回
所有的代码均托管在: ttps://github.com/liuyueyi/2048
MenuButton.cpp
#include"MenuButton.h" boolMenuButton::init() { if(!Node::init()) returnfalse; else returntrue; } MenuItem*MenuButton::getMenuItem(conststd::string&text,constSize&size) { autolabel=Label::createWithSystemFont(text,26); //label->setTextColor(Color4B(120,255)); autoitem=MenuItemLabel::create(label); item->setContentSize(size); returnitem; }
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; } boolGameMenuLayer::init() { if(!Layer::init()) returnfalse; this->setContentSize(Size(300,30)); this->setPosition(Vec2(10,370)); automenuButton=MenuButton::create(); autoresetBg=LayerColor::create(Color4B(143,122,101,255),100,30); this->addChild(resetBg); autoresetmenu=menuButton->getMenuItem("Restart",Size(100,30)); resetmenu->setPosition(55,17); resetmenu->setCallback(CC_CALLBACK_1(GameMenuLayer::resetGameFun,this)); autosetBg=LayerColor::create(Color4B(143,50,30); setBg->setPosition(250,0); this->addChild(setBg); autosetmenu=menuButton->getMenuItem("Set",Size(40,30)); setmenu->setPosition(275,17); setmenu->setCallback(CC_CALLBACK_1(GameMenuLayer::setGameFun,this)); autoundoBg=LayerColor::create(Color4B(143,60,30); undoBg->setPosition(180,0); this->addChild(undoBg); autoundomenu=menuButton->getMenuItem("Undo",Size(60,30)); undomenu->setPosition(213,17); undomenu->setCallback(CC_CALLBACK_1(GameMenuLayer::undoGameFun,this)); automenu=Menu::create(resetmenu,undomenu,setmenu,NULL); //menu->alignItemsHorizontally(); menu->setPosition(0,0); this->addChild(menu); returntrue; } voidGameMenuLayer::resetGameFun(Ref*ref) { GameLayer::getInstance()->restartGame(); log("resetgame"); } voidGameMenuLayer::setGameFun(Ref*ref) { autolayer=static_cast<GameScene*>(this->getParent()); autosetmenu=layer->getChildByName("setlayer"); if(setmenu->isVisible()) setmenu->setVisible(false); else setmenu->setVisible(true); log("startgame"); } voidGameMenuLayer::undoGameFun(Ref*ref) { GameLayer::getInstance()->undoGame(); log("backtolaststat"); }
此处对于void GameMenuLayer::setGameFun(Ref* ref) 进行简单说明,这次设计中没有将SetMenu设置为单例模式,添加到父节点的是SetMenu的一个对象,这样的情况下,可以通过getChildByName,getChindByTag函数来获取该对象,然后进行相应的修改
对应的将SetMenu加入场景的代码在GameScene的init函数内:
autosetLayer=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" boolSetMenu::init() { if(!Layer::init()) returnfalse; this->setContentSize(Size(120,180)); this->setPosition(Vec2(195,185)); autobg=LayerColor::create(Color4B(30,30,200),180); this->addChild(bg); autodraw=DrawNode::create(); this->addChild(draw); //draw->drawLine(Vec2(10,1)); autoclassic=Label::createWithSystemFont(Grid::G2U("经典模式"),"Arial",25); autosoldier=Label::createWithSystemFont(Grid::G2U("小兵模式"),25); autocolor=Label::createWithSystemFont(Grid::G2U("纯色模式"),25); autosound=Label::createWithSystemFont(Grid::G2U("声音"),25); autoitem01=MenuItemLabel::create(classic,CC_CALLBACK_1(SetMenu::classicCallFunc,this)); item01->setPosition(60,135+22.5f); draw->drawLine(Vec2(5,135),Vec2(115,Color4F(1,1,1)); autoitem02=MenuItemLabel::create(soldier,CC_CALLBACK_1(SetMenu::soldierCallFunc,this)); item02->setPosition(60,90+22.5f); draw->drawLine(Vec2(5,90),1)); autoitem03=MenuItemLabel::create(color,CC_CALLBACK_1(SetMenu::colorCallFunc,this)); item03->setPosition(60,45+22.5f); draw->drawLine(Vec2(5,45),1)); autoitem04=MenuItemLabel::create(sound,CC_CALLBACK_1(SetMenu::soundCallFunc,this)); item04->setPosition(60,22.5f); //draw->drawLine(Vec2(5,22.5f),157.5f),1)); automenu=Menu::create(item01,item02,item03,item04,nullptr); //menu->alignItemsVertically(); menu->setPosition(0,0); this->addChild(menu); returntrue; } voidSetMenu::classicCallFunc(Ref*ref) { changeType(1); } voidSetMenu::soldierCallFunc(Ref*ref) { changeType(0); } voidSetMenu::colorCallFunc(Ref*ref) { changeType(2); } voidSetMenu::soundCallFunc(Ref*ref) { } voidSetMenu::changeType(intnewType) { autotype=Grid::getType(); if(type==newType)//donotchangethegamemodel return; //dochange,thensavethegamestatandrestartnewgamemodel DataConf::getInstance()->dumpData(type); GameLayer::getInstance()->clearGrids(); Grid::changeType(newType); this->setVisible(false); }
Label中文显示以及直接添加点击事件
Label创建时,直接赋值中文时,会出现问题(不显示内容,或者乱码),主要是编码格式的问题造成的,其解决方法有从xml,json中读取中文,然后传入;或者直接采用下面函数对中文重新编码
char*Grid::G2U(constchar*gb2312) { intlen=MultiByteToWideChar(CP_ACP,gb2312,-1,NULL,0); wchar_t*wstr=newwchar_t[len+1]; memset(wstr,len+1); MultiByteToWideChar(CP_ACP,wstr,len); len=WideCharToMultiByte(CP_UTF8,NULL); char*str=newchar[len+1]; memset(str,len+1); WideCharToMultiByte(CP_UTF8,str,len,NULL); if(wstr)delete[]wstr; returnstr; }
2. 事件绑定
直接在Label上绑定事件,利用EventListenerTouchOneByOne来实现绑定
autolabel=Label::createWithSystemFont("label",40); this->addChild(label); label->setPosition(100,100); autolistener=EventListenerTouchOneByOne::create(); listener->onTouchBegan=[label](Touch*touch,Event*e){ log("entered”); if(label->getBoundingBox().containsPoint(touch->getLocation())) log("trulyclicked"); returnfalse; }; Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraPHPriority(listener,label);
注意: