Cocos2d-xUI组件详解
标签(Label)
Cocos2d-x提供Label
对象给用户,可以使用位图字体,TrueType字体,系统字体创建标签。这个单一的类能处理你所有的标签需求。下面介绍使用各种字体,创建标签的方法。
BMFont
BMFont
是一个使用位图字体创建的标签类型,位图字体中的字符由点阵组成。使用这种字体标签性能非常好,但是不适合缩放。由于点阵的原因,缩放会导致失真。标签中的每一个字符都是一个单独的Sprite
,也就是说精灵的属性(旋转,缩放,着色等)控制都适用于这里的每个字符。
创建BMFont
标签需要两个文件,.fnt
文件和.png
文件。可以使用像Glyph Designer
这样的工具来创建位图字体,这些文件将会自动生成。
使用位图字体创建标签:
auto myLabel = Label::createWithBMFont("bitmapRed.fnt","Your Text");
所有在标签中出现的字符都应该能在提供的.fnt
文件找到,如果找不到,字符就不会被渲染。如果你渲染了一个Label
,同时它有丢失字符,记得去查看一下,.fnt
文件是否完备。
TTF
True Type
字体和我们上面了解的位图字体不同,使用这种字体很方便,你不需要为每种尺寸和颜色单独使用字体文件。不像BMFont
,如果想不失真的缩放,就要提供多种字体文件。
要创建这种标签,需要制定.ttf
字体文件名,文本字符串和字体大小。
使用TrueType
字体创建标签:
auto myLabel = Label::createWithTTF("Your Text","Marker Felt.ttf",24);
虽然使用TruType
字体比使用位图字体更灵活、但是它渲染速度较慢,并且更改标签的属性(字体,大小)是一项非常消耗性能的操作。
如果需要具有相同属性的多个Label对象,那可以创建一个TTFConfig
对象来统一配置,TTFConfig
对象允许设置所有标签的共同属性。
通过以下方式创建一个TTConfig
对象:
//为所有的Label标签创建一个TTFConfig共享文件配置
TTFConfig labelConfig;
labelConfig.fontFilePath = "myFont.ttf";
labelConfig.fontSize = 16;
labelConfig.glyphs = GlyphCollection::DYNAMIC;
labelConfig.outlineSize = 0;
labelConfig.customGlyphs = nullptr;
labelConfig.distanceFieldEnabled = false;
//从TTFConfig文件中创建TTF Label标签
auto myLabel = Label::createWithTTF(labelConfig,"My Label Text");
TTFConfig
也能用于展示中文,日文,韩文的字符。
SystemFont
SystemFont
是一个使用系统默认字体,默认字体大小的标签类型,这样的标签不要改变它的属性,它会使用系统的规则。
使用系统字体创建标签:
auto myLabel = Label::createWithSystemFont("My Label Text","Arial",16);
标签效果
在屏幕上有标签后,它们可能看起来很普通,这时希望让它们变得更加漂亮。不用创建自定义字体!Label对象就可以对标签应用效果,包括阴影,描边,发光。
阴影效果:
auto myLabel = Label::createWithTTF("myFont.ttf","My Label Text",16);
//所有标签类型都支持阴影效果
myLabel->enableShadow();
描边效果:
auto myLabel = Label::createWithTTF("myFont.ttf","My Label Text",16);
//描边效果仅支持TTF字体,而且需要指定所需的颜色
myLabel->enableOutline(Color4B::WHITE,1);
发光效果:
auto myLabel = Label::createWithTTF("myFont.ttf","My Label Text",16);
//发光效果仅支持TTF字体,而且需要指定所需的颜色
myLabel->enabelGlow(Color4B::YELLOW);
实例
- 使用文本标签实现打字机效果,要求:
- 控制每行字数自动换行
- 文字字体使用方正姚体
- 字体加粗
- 字体颜色粉红色
- 实现打字机效果
演示效果
示例代码如下
json文件
使用json文件主要是解决中文乱码的问题,详见链接:https://blog.csdn.net/qq135595696/article/details/122850029
HelloWorldScene.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
#include <string>
class HelloWorld : public cocos2d::Scene
{
public:
static cocos2d::Scene* createScene();
virtual bool init();
// implement the "static create()" method manually
CREATE_FUNC(HelloWorld);
private:
//记录当前文字内容
std::string prt_content;
cocos2d::Label* prt_label;
};
#endif // __HELLOWORLD_SCENE_H__
HelloWorldScene.cpp
#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
#include "LayerDemo.h"
//引入cJson库文件
#include "cJSON.h"
#include <fstream>//C++IO流头文件
USING_NS_CC;
using namespace std;
Scene* HelloWorld::createScene()
{
return HelloWorld::create();
}
// Print useful error message instead of segfaulting when files are not there.
static void problemLoading(const char* filename)
{
printf("Error while loading: %s\n", filename);
printf("Depending on how you compiled you might have to add 'Resources/' in front of filenames in HelloWorldScene.cpp\n");
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
if (!Scene::init())
return false;
auto visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
//读取test.json文件内容
FILE* pFile = fopen("test.json", "r");
if (nullptr == pFile)
{
problemLoading("test.json");
return false;
}
char testContent[1024 * 20]{ 0 };
while (!feof(pFile))
{
char buffer[1024]{ 0 };
fgets(buffer, 1024, pFile);
int index = strlen(testContent);
for (int i = 0; i < strlen(buffer); i++)
{
if (buffer[i] == '\n')
break;
testContent[index++] = buffer[i];
}
}
//解析test.json文件内容
cJSON* monitor = cJSON_Parse(testContent);//将字符串转换为json对象
if (nullptr == monitor)
return false;
//获取key为mainKey1的对象内容
cJSON* mainKey1 = cJSON_GetObjectItem(monitor, "mainKey1");
//获取key为value的对象内容
auto value1 = cJSON_GetObjectItem(mainKey1, "value");
//输出打印value对象的内容
log(value1->valuestring);
//实现背景图片
auto sprite = Sprite::create("1.jpg");
sprite->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
sprite->setPosition(Vec2(visibleSize / 2));
sprite->setScale(0.3);
this->addChild(sprite);
//显示文字内容
prt_content = value1->valuestring;
//开始不显示任何内容
prt_label = Label::create(prt_content, "方正姚体", 30);
prt_label->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
prt_label->setPosition(Vec2(visibleSize / 2));
prt_label->setColor(Color3B(248, 248, 255));
prt_label->enableBold();
prt_label->setDimensions(400.0f, 0);//使用纯英文换行失效,只有存在中文才可能生效!
this->addChild(prt_label);
//实现打字效果
static int i = 0;
this->schedule([&](float dlt) {
char ch = prt_content[i];
//获取当前下标的字符
if (ch < 0 && ch > -128)
//在utf-8编码中一个中文占三个字节,在ANSI编码中一个中文占2个字节,在utf-16中一个中文占4个字节
i += 3;
else
i++;
//截取字符串
string str = prt_content.substr(0, i);
prt_label->setString(str);
if (i > prt_content.length())
this->unschedule("scheduleCallback");//停止定时器
}, 0.1f, "scheduleCallback");
return true;
}
菜单(Menu)
- 继承自Layer类,是一个层容器
在每个游戏中都会有菜单。我们使用菜单浏览游戏选项,更改游戏设置。菜单通常包含开始,退出,设置,关于等项,菜单当然也可以包含子菜单。在Cocos2d-x提供Menu
对象支持菜单功能,Menu
对象是一种特殊的Node
对象。
创建一个菜单用于添加菜单项:
auto myMenu = Menu::create();
像我们刚才提到的一个菜单,总会有一些菜单项,比如开始,退出,设置等,没有菜单项的菜单没有存在的意义。Cocos2d-x提供了一些方法来创建菜单项,比如使用Label
对象,或是使用一张图像。菜单项一般由正常状态和选择状态。菜单项显示是正常状态,当你点击时变为选择状态,同时点击菜单还会触发一个回调函数。
使用图像创建菜单:
//通过指定的图像创建菜单项
auto closeItem = MenuItemImage::create("CloseNormal.png","CloseSelected.png",CC_CALLBACK_1(HelloWorld::menuCloseCallback,this));
auto menu = Menu::create(closeItem,NULL);
this->addChild(menu,1);
还可以使用MenuItem
的一个vector
创建菜单:
//创建一个vector来管理menuItems
Vector<MenuItem*> MenuItems;
auto closeItem = MenuItemImage::craete("CloseNormal.png","CloseSelected.png",CC_CALLBACK_1(HelloWorld::menuCloseCallback,this));
MenuItems.pushBack(closeItem);
auto menu = Menu::createWithArray(MenuItems);
this->addChild(menu,1);
使用Lambda表达式
当点击菜单项时会触发一个回调函数。C++11支持了lambda表达式,lambda表达式时匿名函数
,所以可以在回调方法处使用lambda表达式,这样能让代码看起来更简单,同时不会有额外的性能开销。
一个简单的lambda表达式:
//创建一个简单的lambda表达式
auto func = [](){cout<<"Hello World";};
//执行该函数
func();
使用lambda表达式作为菜单项的回调函数:
auto closeItem = MenuItemImage::create("CloseNormal.png","CloseSelected.png",[&](Ref* sender){
//执行我们需要的逻辑
});
按钮(Button)
按钮在我们点击后会使我们的游戏产生一些变化,比如更改了场景,触发了动作等。按钮会拦截点击事件,事件触发时调用事先定义好的回调函数。按钮有一个正常状态,一个选择状态,还有一个不可点击状态,按钮的外观可以根据这三个状态而改变。Cocos2d-x提供Button
对象支持按钮功能,创建一个按钮并定义一个回调函数很简单,但是需要在操作的时候要有头文件需包含:#include"ui/CocosGUI.h"
。
auto button = Button::create("normal_image.png","selected_image.png","disabled_image.png");
button->setTitleText("Button Text");
button->addTouchEventListener([&](Ref* sender,Widget::TouchEventType type){
switch(type)
{
case ui::Widget::TouchEventType::BEGAN:
break;
case ui::Widget::TouchEventType::ENDED:
std::cout<<"Button 1 clicked"<<std::endl;
break;
default:
break;
}
});
this->addChild(button);
可以看到,我们为按钮的每个状态都制定了一个.png
图像:
在屏幕显示的时候,同一个时刻只能看到一个状态,正常显示状态像这样:
复选框(CheckBox)
日常生活中复选框很常见,比如填写问卷时,让我们选一些喜欢的项目,游戏设置中,某一设置时打开还是关闭。只有两种状态的项目经常被设计为复选框。Cocos2d-x提供Checkbox
对象支持复选框功能。
创建一个复选框:
#include "ui/CocosGUI.h"
auto checkbox = CheckBox::create("check_box_normal.png",
"check_box_normal_press.png",
"check_box_active.png",
"check_box_normal_disable.png",
"check_box_active_disable.png");
checkbox->addTouchEventListener([&](Ref* sender,Widget::TouchEventType type){
switch(type)
{
case ui::Widget::TouchEventType::BEGAN:
break;
case ui::Widget::TouchEventType::ENDED:
std::cout<<"check 1 clicked"<<std::endl;
break;
default:
break;
}
});
this->addChild(checkbox);
在上面的例子中,我么能看到一个复选框制定了五张图像,因为复选框有五种状态,未被选中,被点击,未被选中时不可用,被选中,选中时不可用。这样五种状态的图像依次如下:
在频幕显示的时候,同一个时刻只能看到一个状态,被选中时状态像这样:
进度条(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::craete("LoadingBarFile.png");
loadingBar->setDirection(LoadingBar::Direction::RIGHT);
//触发事件,改变进度条的百分比
loadingBar->setPercent(25);
//再次改变
loadingBar->setPercent(35);
this->addChild(loadingBar);
上面的例子,使用的进度条图像是:
在屏幕上一个满进度的进度条是这样的:
滑动条(Slider)
有时候你想平滑的改变一个值,比如游戏设置中,调整背景音乐的音量,或者你有一个角色,允许用户设置攻击敌人的力量。这种场景最适合使用滑动条,Cocos2d-x提供Slider
对象支持滑动条。
创建滑动条:
#include "ui/CocosGUI.h"
auto slider = Slider::create();
slider->loadBarTexture("Slider_Back.png");//滑动条看起来像什么
slider->loadSlidBallTextures("SliderNode_Normal.png","SliderNode_Press.png","SliderNode_Disable.png");
slider->loadProgressBarTexture("Slider_PressBar.png");
slider->addTouchEventListener([&](Ref* sender,Widget::TouchEventType type){
switch(type)
{
case ui::Widget::TouchEventType::BEGAN:
break;
case ui::Widget::TouchEventType::ENDED:
std::cout << "slider moved" << std::endl;
break;
default:
break;
}
});
this->addChild(slider);
从上面的例子,可以看出,实现一个滑动条需要提供五张图像,对应滑动条的不同部分不同状态,分别为:滑动条背景,上层进度条,正常显示时的滑动端点,滑动式的滑动端点,不可用时的滑动端点。本次示例的五张图如下:
在屏幕上一个滑动条看起来是这样的:
文本框(TextField)
如果你想让参与游戏的玩家可以自定义一个昵称怎么办,在哪里输入文本?Cocos2d-x提供TextField
满足这种需求。它支持触摸事件,焦点,定位内容百分比等。
创建一个文本框:
#include "ui/CocosGUI.h"
auto textField = TextField::craete("","Arial",30);
textField->addTouchEventListener([&](Ref* sender,Widget::TouchEventType type){
std::cout<<"editing a TextField"<<std::endl;
});
this->addChild(textField);
这个例子中,创建了一个TextField
,指定了回调函数。
提供的文本对象,是多功能的,能满足所有的输入需求,比如用户密码的输入,限制用户可以输入的字符数等等!
看一个例子:
#include "ui/CocosGUI.h"
auto textField = TextField::craete("","Arial",30);
//启动此文本字段密码
textField->setPasswordEnabled(true);
//设置用户可以输入此文本字段的最大字符数
textField->setMaxLength(10);
textField->addTouchEventListener([&](Ref* sender,Widget::TouchEventType type){
std::cout << "editing a TextField" << std::endl;
});
this->addChild(textField);
屏幕上一个文本框是这样的:
当点击文本框,键盘就会自动调出来,此时可以输入文本: