标签(Label)
是一个 **Label**
对象,可以使用位图字体,TrueType 字体,系统字体创建标签。
注意,如果在使用中文时乱码,可以在.cpp
文件中添加
#ifdef WIN32
#pragma execution_character_set("utf-8")
//主体代码
#endif
BMFont格式
BMFont
是一个使用位图字体创建的标签类型,位图字体中的字符由点阵组成。使用这种字体标签性能非常好,但是不适合缩放。由于点阵的原因,缩放会导致失真。标签中的每一个字符都是一个单独的 Sprite
,也就是说精灵的属性(旋转,缩放,着色等)控制都适用于这里的每个字符。
创建 BMFont
标签需要两个文件:.fnt
文件和 .png
文件。
使用位图字体创建标签:
auto myLabel = Label::createWithBMFont("bitmapRed.fnt", "Your Text");
不过.fnt
虽然具有渲染快速的优点,不过缺点是制作繁琐,中文资源不足,大多数情况下,使用TTF
即可,而且TTF
缩放也不会失真
TTF格式
要创建这种标签,需要指定 .ttf
字体文件名,文本字符串和字体大小。
使用 TrueType 字体创建标签:
auto myLabel = Label::createWithTTF("Your Text", "Marker Felt.ttf", 24); //如果想使用自己的.ttf字体文件,需要将下载得到的文件放在工程目录的\Resources\fonts下
如果您需要具有相同属性的多个 Label 对象,那可以创建一个 TTFConfig
对象来统一配置,TTFConfig
对象允许你设置所有标签的共同属性。
通过以下方式创建一个 TTFConfig
对象:
// 创建 TTFConfig 文件,优点是,更改它即可一并更改所有引用此对象的Label
TTFConfig labelConfig;
labelConfig.fontFilePath = "fonts/Marker Felt.ttf";
labelConfig.fontSize = 20;
labelConfig.outlineSize = 2;
labelConfig.underline = true;
// 为创建的TTF Label 使用 labelConfig
auto myLabel = Label::createWithTTF(labelConfig, "My Label Text");
SystemFont
SystemFont
是一个使用系统默认字体,默认字体大小的标签类型。
使用系统字体创建标签:
auto myLabel = Label::createWithSystemFont("My Label Text", "Arial", 16); //有的系统中如果要使用“黑体”,可能对应的是“SimHei” ,微软雅黑可能是“Microsoft YaHei”,而不是中文,不过有的系统可以直接使用中文
标签效果
在屏幕上有标签后, Label 对象就可以对标签应用效果,包括阴影,描边,发光。
阴影效果:
// 首先创建一个标签
auto myLabel = Label::createWithTTF("myFont.ttf", "My Label Text", 16);
// 启动阴影,该函数有三个参数Color4B(R,G,B,A),Size(大小,偏移值),以及一个模糊度数值,如果不填,则会有默认参数(为黑色)
myLabel->enableShadow(Color4B(0,0,0,125),Size(3,-2), 10);
描边效果:
auto myLabel = Label::createWithTTF("myFont.ttf", "My Label Text", 16);
// 参数为颜色以及描边宽度
myLabel->enableOutline(Color4B::WHITE, 1));
发光效果:
auto myLabel = Label::createWithTTF("myFont.ttf", "My Label Text", 16);
// 参数只有一个,发光颜色
myLabel->enableGlow(Color4B::YELLOW);
菜单(Menu)
菜单项(MenuItem)
- 文本菜单项 ```cpp MenuItemFont:: setFontName(“字体名称”); MenuItemFont:: setFontSize(大小); MenuItemFont item1 = MenuItemFont::create(名称, 回调函数); MenuItemAtlasFont item2 = MenuItemAtlasFont::create(名称,路径,大小x,大小y,’ ‘,回调函数);
//利用已经创建好的Label来创建菜单
auto buttonLabel1 = Label::createWithTTF(“fonts/TencentSans-W7.ttf”,”Place”,24);
auto button1 = MenuItemLabel::create(buttonLabel1, = {
Director::getInstance()->pushScene(Action1::createScene(PLACE_TAG));
});
2. 精灵菜单项
2. 开关菜单项
```cpp
auto toggleMenuuItem = MenuItemToggle::createWithCallback(回调函数,开启状态,关闭状态,NULL);
创建菜单
我们使用菜单浏览游戏选项,更改游戏设置。菜单通常包含开始,退出,设置,关于等项,菜单当然也可以包含子菜单。Cocos2dX 提供 **Menu**
对象支持菜单功能,_Menu_
对象是一种特殊的 _节点Node_
对象。
创建一个菜单用于添加菜单项:
auto myMenu = Menu::create();
像我们刚才提到的一个菜单,总会有一些菜单项,比如开始,退出,设置等,没有菜单项的菜单没有存在的意义。Cocos2d-x 提供了一些方法来创建菜单项,比如使用 Label
对象,或是使用一张图像。菜单项一般有正常状态和选择状态。菜单项显示时是正常状态,当你点击它时变为选择状态,同时点击菜单还会触发一个回调函数。
使用图像创建菜单:
// 创建一个菜单,并传入Item
// 用MenuItemImage的方式创建,参数有点击前的图片,点击后的图片,回调函数
auto closeItem = MenuItemImage::create("CloseNormal.png", "CloseSelected.png",
CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
// 其余的菜单物体有MenuItemLabel, MenuItemFont等
//创建一个MenuItemFont
auto myLabel = Label::createWithTTF("Cancel", "fonts/Marker Felt.ttf", 20);
//创建一个MenuItemLabel
auto changeScene = MenuItemFont::create("Go To The Level_01", CC_CALLBACK_0(HelloWorld::goToLevel_01, this));
auto cancelMenuItem = MenuItemLabel::create(myLabel, CC_CALLBACK_0(HelloWorld::goToLevel_01, this));
cancelMenuItem->setPositionY(-30);
//添加进我们的菜单中,注意,菜单的最后一个参数不是菜单Item节点,默认情况下,传入NULL即可
auto myMenu = Menu::create(closeItem, changeScene, cancelMenuItem, NULL);
this->addChild(myMenu, 1);
还可以使用 MenuItem
的一个 vector
创建菜单:
// 创建一个保存MenuItem*的Vector集合
Vector<MenuItem*> MenuItems;
auto closeItem = MenuItemImage::create("CloseNormal.png", "CloseSelected.png",
CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
//menuCloseCallback中存在一个传参 Ref* pSender 可以在函数中或者按钮参数
//如 MenuItem *item = (MenuItem*)pShender;
MenuItems.pushBack(closeItem); //用pushBack可以让菜单Item排序按照加载顺序排列
// 注意创建时,需要使用createWithArray而不是create
auto menu = Menu::createWithArray(MenuItems);
this->addChild(menu, 1);
使用匿名函数
当您点击菜单项时会触发一个回调函数。C++ 11 支持了 匿名函数,所以你可以在回调方法处,使用匿名函数,这样能让代码看起来更简洁,同时不会有额外的性能开销。
一个简单的 lambda 表达式:
auto func = [] () { cout << "Hello World"; };
// 之后可以在任意地方调用该函数(当然,一般我们用匿名函数的时候,是抱着不会二次调用的情况下使用的)
func();
使用 lambda 表达式作为菜单项的回调函数:
auto changeScene = MenuItemFont::create("Go To The Level_01", [&](Ref* sender) {
clickCount++; //我们让每次点击的时候,计数参数++
string clickStr = "Click time: " + to_string(clickCount);
clickLabel->setString(clickStr); //更新当前点击的次数
auto level_01_Scene = Level_01::create();
Director::getInstance()->pushScene(level_01_Scene);
});//替换了原先我们指定的CC_CALLBACK_0(HelloWorld::goToLevel_01, this)部分
//这样做的好处是,我们不需要再定义一个函数,再在.cpp中完善函数体,如果这个函数就单单只会在这一个地方调用的话
关于匿名函数的参数捕获形式,更多内容可以参考cocos2d3.10 简单引用lambda表达式用作回调函数
按钮(Button)
创建按钮
按钮的关键在于,点击后会有什么响应。按钮会拦截点击事件,事件触发时调用事先定义好的回调函数。按钮有一个正常状态normal
,一个选择状态selected
,还有一个不可点击状态disabled
,按钮的外观可以根据这三个状态而改变。Cocos2d-x 提供 **Button**
对象支持按钮功能,创建一个按钮并定义一个回调函数很简单,记得在操作的时候要有头文件包含: #include "ui/CocosGUI.h"
,以及命名空间using namespace cocos2d::ui;
。
#include "ui/CocosGUI.h"
using namespace cocos2d::ui;
//按钮在创建时可以传入三个参数,也可以传入一个,如果只传一个,则按钮点击样式默认为点中时放大,放开点击后回复大小
auto button = Button::create("normal_image.png", "selected_image.png", "disabled_image.png");
//auto button = Button::create("HelloWorld.png");
button->setTitleText("Button Text"); //这段文字会显示在按钮之上
button->setPosition(Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y));
button->addTouchEventListener([&](Ref* sender, Widget::TouchEventType type) {
// 这是一段匿名函数
log("按钮点击的事件监听器");
});
this->addChild(button);
可以看到,我们为按钮的每个状态都指定了一个.png
图像:
在屏幕显示的时候,同一个时刻只能看到一个状态,正常显示状态像这样:
如果我们不想在按钮处使用匿名函数,那么我们可以创建一个函数,但是一定要注意,函数需要包括按钮必要的传参参数
void HelloWorld::logInfo(Ref* pSender, Widget::TouchEventType type) {
log("这是一个打印函数");
}
auto button = Button::create("HelloWorld.png");
button->setTitleText("Button Text");
button->setPosition(Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y));
//在这里使用回调函数的方式增加我们的自定义函数
button->addTouchEventListener(CC_CALLBACK_2(HelloWorld::logInfo, this));
输入框(EditBox)
Cocos2d-x 提供 **EditBox**
满足这种需求。它支持触摸事件,焦点,定位内容百分比等。
创建一个文本框:
//使用输入框之前,请加上这三句代码
#include "cocos-ext.h"
using namespace cocos2d::ui;
USING_NS_CC_EXT;
auto editBoxSize = Size(400, 40); //创建一个可用于编辑的Size变量
auto editName = EditBox::create(editBoxSize, "inputBox.png"); //构造函数的参数为Size大小,以及一张背景图
editName->setPosition(Point(origin.x + visibleSize.width / 2, origin.y + visibleSize.height / 2 + 50)); //设置位置
editName->setFont("Arial", 20); //设置字体以及大小(目前只能支持系统自带字体)
// editName->setFontSize(20); 注意,如果不指定字体,直接设置大小将不会生效,不过可以通过修改引擎源码解决,或者,直接用系统最常用的Arial字体就好
editName->setFontColor(Color3B::RED); //输入的字体颜色
editName->setPlaceHolder("Please enter your name"); //当未输入任何内容时,默认显示的内容,一单输入内容后将会被替换
editName->setPlaceholderFontColor(Color3B::WHITE); //设置上一行代码的字体颜色
editName->setMaxLength(8); //设置最大可输入长度
this->addChild(editName);
这个例子中,创建了一个 EditBox
提供的文本框对象,是多功能的,能满足所有的输入需求,比如用户密码的输入,限制用户可以输入的字符数等等!
如果想要这是一个密码输入框,例子:
editPassword->setInputFlag(ui::EditBox::InputFlag::PASSWORD);
用户登录实例
首先,我们需要的是两个输入框
auto editBoxSize = Size(400, 40); //确定输入框的大小
auto editName = ui::EditBox::create(editBoxSize, "inputBox.png");
editName->setPosition(Vec2(origin.x + visibleSize.width / 2, origin.y + visibleSize.height / 2 + 80));
editName->setFont("Arial", 20);
editName->setFontColor(Color3B::RED);
editName->setPlaceHolder("Please enter your name");
editName->setPlaceholderFontColor(Color3B::WHITE);
editName->setMaxLength(16);
this->addChild(editName);
auto editPassword = ui::EditBox::create(editBoxSize, "inputBox.png");
editPassword->setPosition(Vec2(origin.x + visibleSize.width / 2, origin.y + visibleSize.height / 2 + 40));
editPassword->setFont("Arial", 20);
editPassword->setFontColor(Color3B::RED);
editPassword->setPlaceHolder("Please enter your password");
editPassword->setPlaceholderFontColor(Color3B::WHITE);
editPassword->setMaxLength(16);
editPassword->setInputFlag(ui::EditBox::InputFlag::PASSWORD);
this->addChild(editPassword);
光有输入框还不行,我们需要一个按钮
auto loginButton = ui::Button::create("next.png");
loginButton->setPosition(Vec2(origin.x + visibleSize.width / 2 + 150, origin.y + visibleSize.height / 2-20));
loginButton->setScale(0.4f);
//注意匿名函数的参数
loginButton->addTouchEventListener([=](Ref* pSender, ui::Widget::TouchEventType type) {
//这一步判断,当触碰事件类型type为触碰结束时执行
//更多关于监听事件,以及交互操作会在之后补充
if (type == ui::Widget::TouchEventType::ENDED) {
auto name = editName->getText(); //这一步可以获取我们输入在输出框中的字符串
auto password = editPassword->getText();
if (name == nullptr || password == nullptr)
return;
if(strcmp(name,"root")==0 &&
strcmp(password, "123456")==0) { //strcmp比较函数,会在相同时返回0
log("密码正确");
loginSuccessful(); //登录函数,我们可以写一个新的场景
}
else {
log("密码或账号错误");
}
}
});
this->addChild(loginButton, 1);
void HelloWorld::loginSuccessful() {
auto scene = MyPage::createScene(); //跳转到新的场景以验证登录
Director::getInstance()->pushScene(scene);
}
进度条(LoadingBar)
如果你经常玩游戏,那肯定见过一个情景:屏幕上显示了一个进度条,提示资源正在加载中,这个条表示资源加载的进度。Cocos2d-x 提供 **LoadingBar**
对象支持进度条。
创建一个进度条:
#include "ui/CocosGUI.h"
auto loadingBar = LoadingBar::create("LoadingBarFile.png");
// 设置进度条的方向
loadingBar->setDirection(LoadingBar::Direction::RIGHT);
this->addChild(loadingBar);
上面的例子,我们创建了一个进度条,设置了当进度增加时,进度条向右填充。
在进度的控制中,你肯定需要改变进度条的进度. 示例:
#include "ui/CocosGUI.h"
auto loadingBar = LoadingBar::create("LoadingBarFile.png");
loadingBar->setDirection(LoadingBar::Direction::RIGHT);
// 改变进度条的进度百分比
loadingBar->setPercent(25);
this->addChild(loadingBar);
在屏幕上一个满进度的进度条是这样的:
或者,也可以为我们的进度条设置一个背景框
auto loadingBarPosition = Vec2(origin.x + visibleSize.width / 2, + 80); //用于创建时的参数
auto loadingBarSize = Size(400, 40);
//创建进度条的背景
auto loadingBG = Sprite::create("PB_border.png"); //背景框可以使用Sprite
loadingBG->setPosition(loadingBarPosition);
loadingBG->setContentSize(loadingBarSize);
loadingBG->setAnchorPoint(Point(0.5, 0.5)); //为了便于我们将进度条背景与进度条对其,锚点可以设置为几何中心
//创建进度条
auto loadingBar = LoadingBar::create("LoadingBarFile.png"); //使用LoadingBar创建
loadingBar->setDirection(LoadingBar::Direction::LEFT); //进度条方向为从左向右
loadingBar->setPosition(Vec2(loadingBarSize.width / 2, loadingBarSize.height / 2)); //居中在背景框中
loadingBar->setContentSize(loadingBarSize);
loadingBar->setScale(1.9);
loadingBar->setAnchorPoint(Point(0.5,0.5));
// 改变进度条的进度百分比
loadingBar->setPercent(90);
//别忘了将背景框添加到场景层中,为了便于对齐,可以将loadingBar加载到loadingBG背景框上,也便于后期统一处理
this->addChild(loadingBG);
loadingBG->addChild(loadingBar);
大致效果:
而进一步使用进度条,需要介绍Cocos中的计时器部分,进度条的案例在下一章介绍