博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Cocos2d-x 3.2:通过ClippingNode实现一个功能完善的跑马灯公告(1)
阅读量:6250 次
发布时间:2019-06-22

本文共 7245 字,大约阅读时间需要 24 分钟。

Cocos2d-x 3.2:通过ClippingNode实现一个功能完善的跑马灯公告(1)

本文转载至

 

这篇文章主要是通过一步一步实现一个功能完善的跑马灯公告来展示ClippingNode的用法并且最终深入ClippingNode的源码,了解其实现原理。

首先,先介绍一下ClippingNode,ClippingNode也叫裁剪节点,能将一些内容通过使用模板裁剪出来显示在界面上,可以实现一些很炫酷的效果。来看看今天要实现的效果

1.gif

1、ClippingNode类分析

先来看看ClippingNode的声明文件 看看其中的public方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class 
CC_DLL ClippingNode : 
public 
Node  
{  
public
:  
    
static 
ClippingNode* create();  
    
static 
ClippingNode* create(Node *stencil);  
   
    
Node* getStencil() 
const
;  
    
void 
setStencil(Node *stencil);  
   
    
virtual 
bool 
hasContent() 
const
;  
   
    
GLfloat getAlphaThreshold() 
const
;  
    
void 
setAlphaThreshold(GLfloat alphaThreshold);  
       
    
bool 
isInverted() 
const
;  
    
void 
setInverted(
bool 
inverted);  
};

首先是create,这个方法是用于创建一个ClippingNode,这个就不多做赘述了,第二个create是创建一个带遮罩模板的裁剪节点。

接下来的getStencil和setStencil分别是获取和设置一个遮罩模板,裁剪物体方法就是通过这个遮罩模板的,遮罩模板只要是基于Node的对象都可以(非常重要)。

接下来的hasContent返回其是否有需要绘制的内容,如果没有绘制的内容则返回false,有则返回true。

getAlphaThreshold 和setAlphaThreshold分别是获取和设置一个像素的透明度值,取值范围从0-1,其中0表示都不绘制,1表示都绘制。0.5表示透明度在 0.5以上的都绘制,这个函数涉及到opengl的Alpha测试的相关概念,Alpha测试的作用通过一句话解释就是:所有像素的透明度值低于某个阀值 的统统抛弃,不绘制到屏幕上。

最后的isInverted和setInverted分别表示绘制的内容是模板内的还是模板外的,其效果如下:

2.jpg

3.jpg

2、简易跑马灯实现

上节简单介绍了一下ClippingNode的函数,这节就通过实现一个简易的跑马灯功能来直观的了解。首先介绍一下制作跑马灯的思路。

首先我们需要将跑马灯中的一部分超出的字裁剪掉,不让他显示在界面上。这就需要用到ClippingNode,现在先来做第一步。实现的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//设置模板  
auto stencil = Sprite::create();  
//设置显示区域大小  
stencil->setTextureRect(Rect(0, 0, 50, 30));  
   
//设置跑马灯文字  
auto text = Label::createWithSystemFont(
"-1dasdasdasd efadaewfevgds dfhrthrbgrg1-"
""
, 24);  
//设置锚点  
text->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);  
   
//创建裁剪节点  
auto clippingNode = ClippingNode::create(stencil);  
//设置节点位置  
clippingNode->setPosition(Vec2(700, 400));  
//显示模板内的内容  
clippingNode->setInverted(
false
);  
//添加显示内容  
clippingNode->addChild(text, 2);  
//加入到UI树  
addChild(clippingNode);

上述的每一句代码都有注释,就不再多解释了,这一步实现出来的效果如下图,但是跑马灯还不能动起来,待会我们就将跑马灯动起来。

4.jpg

现在我们就设计一个Action将跑马灯动起来,跑马灯一般需要先将文字左移,移动到文字看不见的时候再将文字移除或者隐藏,代码如下(为了简便,就直接设置隐藏了):

1
2
auto sequ = Sequence::create(MoveBy::create(5.0f, Vec2(-text->getContentSize().width, 0)), Hide::create(), nullptr);  
text->runAction(sequ);

现在跑马灯的样子就如同开篇展示的那样了,可是这样还不能直接使用,因为这只是一串代码,还需要对其进行一定的封装,然后提供一个非常简便的方法给别的类调用。

3、封装

现在我们从便捷性的角度考虑如何将跑马灯功能封装成一个函数供其他类调用。首先提取出函数的参数,分别是:显示区域,跑马灯文字,字体字号,跑马灯位置,跑马灯的父节点。下面是初步封装好的一套跑马灯函数的声明:

1
void 
showMarquee(Node* parent, 
const 
std::string& text, 
const 
std::string& font, 
float 
fontSize, 
const 
Rect& showRect, 
const 
Vec2& position);

看参数是不是有些略多,每次调用这个函数是不是非常的不方便,那么我们现在来看看究竟有那些参数是必须要传入的吧。每次调用跑马灯显示的文字都会改变,其他的参数在一个游戏中是不会改变的。那么就有必要做一个类来保证使用方法的便捷性了。

首先,我们简单的构建一下一个跑马灯类,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include "cocos2d.h"  
   
USING_NS_CC;  
   
class 
Marquee : 
public 
Node  
{  
public
:  
   
    
CREATE_FUNC(Marquee);  
   
    
bool 
init();  
   
    
void 
show(
const 
std::string& text);  
   
public
:  
    
const 
std::string& getFont() 
const 
return 
_font; }  
    
void 
setFont(std::string& font) { _font = font; }  
    
float 
getFontSize() 
const 
return 
_fontSize; }  
    
void 
setFontSize(
float 
fontSize) { _fontSize = fontSize; }  
   
public
:  
    
const 
Rect& getShowRect() 
const 
return 
_showRect; }  
    
void 
setShowRect(Rect& showRect) { _showRect = showRect; }  
protected
:  
    
Marquee() :   
        
_font(
""
),  
        
_fontSize(24),  
        
_showRect(Rect(0,0,200,30))  
    
{};  
    
~Marquee() {};  
   
private
:  
    
std::string _font;  
    
float 
_fontSize;  
   
    
Rect _showRect;  
};

然后是最重要的init方法和show方法的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
bool 
Marquee::init()  
{  
    
//设置模板  
    
auto stencil = Sprite::create();  
    
//设置显示区域大小  
    
stencil->setTextureRect(_showRect);  
   
    
//设置跑马灯文字  
    
_label = Label::createWithSystemFont(
""
, _font, _fontSize);  
    
//设置锚点  
    
_label->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);  
   
    
_label->setAlignment(TextHAlignment::LEFT);  
   
    
//创建裁剪节点  
    
auto clippingNode = ClippingNode::create(stencil);  
    
//显示模板内的内容  
    
clippingNode->setInverted(
false
);  
    
//添加显示内容  
    
clippingNode->addChild(_label);  
    
//加入到UI树  
    
addChild(clippingNode);  
   
    
return 
true
;  
}  
   
void 
Marquee::show(
const 
std::string& text)  
{  
    
_label->setString(text);  
    
_label->setPosition(Vec2(0, _label->getContentSize().height / 2));  
   
    
auto sequ = Sequence::create(MoveBy::create(5.0f, Vec2(-_label->getContentSize().width, 0)), Hide::create(), nullptr);  
    
_label->runAction(sequ);  
}

这样就可以通过以下的调用方法来调用跑马灯了

1
2
3
4
Marquee* m = Marquee::create();  
    
m->show(
"----hhahahah veeeeee-----"
);  
    
m->setPosition(Vec2(700, 300));  
    
this
->addChild(m);

4、完善

看 上去,此前的步骤我们已经完成了一个跑马灯的功能,实际上这个类距离真正能使用还差那么一点点,因为传入跑马灯的消息的传入时机是不确定的,可能这一条消 息还没有播放完成下一条就要开始播放了。这样就需要实现一个播放等待队列,将需要播放的消息加入播放队列,然后跑马灯自动判断是否需要显示。下面是改进后 的类声明文件以及实现文件。

.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include "cocos2d.h"  
   
USING_NS_CC;  
   
class 
Marquee : 
public 
Node  
{  
public
:  
    
enum 
class 
State  
    
{  
        
idle,  
        
playing,  
    
};  
   
public
:  
   
    
CREATE_FUNC(Marquee);  
   
    
bool 
init();  
   
    
void 
addMessage(
const 
std::string& text);  
   
public
:  
    
const 
std::string& getFont() 
const 
return 
_font; }  
    
void 
setFont(std::string& font) { _font = font; }  
    
float 
getFontSize() 
const 
return 
_fontSize; }  
    
void 
setFontSize(
float 
fontSize) { _fontSize = fontSize; }  
   
public
:  
    
const 
Rect& getShowRect() 
const 
return 
_showRect; }  
    
void 
setShowRect(Rect& showRect) { _showRect = showRect; }  
   
public
:  
    
const 
State& getState() 
const 
return 
_state; }  
   
protected
:  
    
Marquee() :   
        
_font(
""
),  
        
_fontSize(24),  
        
_showRect(Rect(0,0,200,30)),  
        
_state(State::idle)  
    
{};  
    
~Marquee() {};  
    
void 
show(
const 
std::string& text);  
   
private
:  
    
State _state;  
   
private
:  
    
std::string _font;  
    
float 
_fontSize;  
   
    
Rect _showRect;  
   
private
:  
    
Label * _label;  
   
private
:  
    
std::queue<std::string> _texts;  
};

.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <Marquee.h>  
   
bool 
Marquee::init()  
{  
    
//设置模板  
    
auto stencil = Sprite::create();  
    
//设置显示区域大小  
    
stencil->setTextureRect(_showRect);  
   
    
//设置跑马灯文字  
    
_label = Label::createWithSystemFont(
""
, _font, _fontSize);  
    
//设置锚点  
    
_label->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);  
   
    
_label->setAlignment(TextHAlignment::LEFT);  
   
    
//创建裁剪节点  
    
auto clippingNode = ClippingNode::create(stencil);  
    
//显示模板内的内容  
    
clippingNode->setInverted(
false
);  
    
//添加显示内容  
    
clippingNode->addChild(_label);  
    
//加入到UI树  
    
addChild(clippingNode);  
    
stencil->setColor(Color3B::BLACK);  
    
addChild(stencil, -1);  
   
    
return 
true
;  
}  
   
void 
Marquee::show(
const 
std::string& text)  
{  
    
_state = State::playing;  
   
    
_label->setString(text);  
    
_label->setPosition(Vec2(0, 0));  
   
    
auto sequ = Sequence::create(  
        
Show::create(),   
        
MoveBy::create(5.0f, Vec2(-(_label->getContentSize().width + _showRect.size.width / 2), 0)),  
        
Hide::create() , DelayTime::create(1.0f),   
        
CCCallFunc::create([&]()  
    
{  
        
if 
(_texts.size() == 0)  
        
{  
            
_state = State::idle;  
        
}  
        
else  
        
{  
            
show(_texts.front());  
            
_texts.pop();  
        
}  
    
}), nullptr);  
    
_label->runAction(sequ);  
}  
   
void 
Marquee::addMessage(
const 
std::string& text)  
{  
    
if 
(text.empty())  
    
{  
        
return
;  
    
}  
       
    
if 
(_state == State::idle)  
    
{  
        
show(text);  
    
}  
    
else  
    
{  
        
_texts.push(text);  
    
}  
}

此处将show方法隐藏,并且提供了addMessage方法,内部实现了一个有限状态机,根据状态来显示剩余的消息,其使用方法与此前相似:

1
2
3
4
m = Marquee::create();  
m->addMessage(
"----hhahahah veeeeee-----"
);  
m->setPosition(Vec2(700, 300));  
this
->addChild(m);

 

转载于:https://www.cnblogs.com/dudu580231/p/4560310.html

你可能感兴趣的文章
easyui combobox两种不同的数据加载方式
查看>>
报错:该页必须具有 <%@ webservice class="MyNamespace.MyClass" ... %> 指令。
查看>>
Smarty配置与实例化
查看>>
***Redis hash是一个string类型的field和value的映射表.它的添加、删除操作都是O(1)(平均)。hash特别适合用于存储对象...
查看>>
抽象方法和接口区别
查看>>
Siege——多线程编程最佳实例
查看>>
c# 生成 验证码
查看>>
Selenium学习(4) 键盘操作
查看>>
SQL Server 触发器
查看>>
神奇语言 python 初识面向对象
查看>>
何为SLAM
查看>>
Effective C++ 条款五 了解C++默默编写并调用哪些函数
查看>>
图的存储结构(邻接矩阵)
查看>>
[工具]infolite-chrome插件css插件
查看>>
javascript 深拷贝
查看>>
SwitchHosts—hosts管理利器
查看>>
【代码小记】无
查看>>
【知识点】Java机密
查看>>
如何在 Java 中正确使用 wait, notify 和 notifyAll?
查看>>
BarTender 2016表单中的“秤显示”控件
查看>>