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

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

本文转载至深入理解Cocos2d-x 3.x:一步一步通过ClippingNode实现一个功能完善的跑马灯公告(1)

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

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


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、简易跑马灯实现

上节简单介绍了一下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);

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

现在我们就设计一个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);

时间: 2024-08-11 13:12:24

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

【深入了解cocos2d-x 3.x】一步一步通过ClippingNode实现一个功能完善的跑马灯公告(1)

这篇文章主要是通过一步一步实现一个功能完善的跑马灯公告来展示ClippingNode的用法并且最终深入ClippingNode的源码,了解其实现原理. 首先,先介绍一下ClippingNode,ClippingNode也叫裁剪节点,能将一些内容通过使用模板裁剪出来显示在界面上,可以实现一些很炫酷的效果.来看看今天要实现的效果 1.ClippingNode类分析 先来看看ClippingNode的声明文件 看看其中的public方法 class CC_DLL ClippingNode : publ

如何基于WKWebView开发一个功能完善的资讯内容页

前言 对于资讯类的APP来说 良好的阅读体验是必不可少的, 那么如何去开发一个功能完善的资讯文章页面就是本文要说的重点.相信本文会对很多在做同类功能开发的道友们有很大的帮助 , 如果某只大佬路过也欢迎指点一二. 废话不多说 开讲(chui)~ 分析 数据 对于图文混排的富文本形式 , 最好最通用的数据格式当属 HTML , 再加上 CSS 和 JS 的配合, 可以随心所欲的展示出成百上千在不同的样式.当然 , 除了 HTML 也不排除有使用其他规则的数据格式来表示.但这里我们还是选择使用 HTM

面试角度分析:微信里面让你删掉一个功能,你选哪个?

作为产品面试题,让你砍掉一个功能显然不是为了看你的喜好或者让你猜微信下一个动作,而是要考察你的产品思维能力和市场敏锐度.如果你的回答是“我觉得应该砍掉XXX因为……”,那么恭喜你,你是个有意见的用户,而不是个产品负责人.另外很多面试者虽然想到了要使用数据调查等等方式来获取信息帮助自己做决定,在答题的最后却没有给出具体要砍哪个功能的答案.这个虽然好于凭感觉瞎猜,却也不是理想的回答,因为谁都知道面试过程中你没法做调查获取数据,给出充足的时间和资源任何人都有可能得到这些数据,但是现实本来就是区别于理想

写代码的思路之写一个功能的思路

如果让你实现一个功能,你要完成这个功能的一般思路是什么? 我是一位初学者,希望和大家共享下我在书写代码时的思路,希望对大家有帮助. 以下是我实现一个简单‘日历“这一功能的思路: 当我决定要做一个日历的时候,我会先制作一个”日历“模型,即要实现的”日历“的模型(样子),我用控件制作的: 之后我会按照”日历“模型上面的控件从上到下,从左到右依次声明,但先不实现: 之后一个控件一个控件实现,在设计控件的属性的时候是严格按照之前”日历“模型的属性,这会提高效率: 一个控件实现了之后要做一次测试,保证该控

一个功能更强大的函数,也是用正则表达式写的

<% Option Explicit Function stripHTML(strtext) dim arysplit,i,j, strOutput arysplit=split(strtext,"<") if len(arysplit(0))>0 then j=1 else j=0 for i=j to ubound(arysplit) if instr(arysplit(i),">") then arysplit(i)=mid(arysp

WebLech是一个功能强大的Web站点下载与镜像工具

WebLech是一个功能强大的Web站点下载与镜像工具.它支持按功能需求来下载web站点并能够尽可能模仿标准Web浏览器的行为.WebLech有一个功能控制台并采用多线程操作. http://sourceforge.net/projects/weblech/files/WebLech/weblech-0.0.3/weblech-0.0.3.tar.gz/download?use_mirror=nchc&download=

一个功能齐全的CMS管理系统模板,内置6套皮肤,已经转化为标准的Eclipse项目,直接导入即可

原文:一个功能齐全的CMS管理系统模板,内置6套皮肤,已经转化为标准的Eclipse项目,直接导入即可 源代码下载地址:http://www.zuidaima.com/share/1550463745526784.htm - MrZhao只分享精品,话不多说,直接上图  - 为了压缩文件我把WEB-INF下面的lib包打包放在网盘下载地址:http://pan.baidu.com/s/1eQEbAAA - 把lib解压进去以后项目直接导入Eclise即可运行 - 数据库Mysql,初始脚本项目里

SuperSwipeRefreshLayout 一个功能强大的自定义下拉刷新组件

SuperSwipeRefreshLayout 一个功能强大的自定义下拉刷新组件. Why? 下拉刷新这种控件,想必大家用的太多了,比如使用很多的XListView等.最近,项目中很多列表都是使用ReyclerView实现的,代替了原有的ListView,原有下拉刷新方式遭到挑战.本来Google推出的SwipeRefreshLayout已经能够满足大部分的需求了.然而,由于其定制性较差,下拉刷新的样式无法修改,而且被嵌套的View也无法跟随手指的滑动而滑动.基于以上考虑,定制自己强大的Supe

XAMPP(Apache+MySQL+PHP+PERL)是一个功能强大的建 XAMPP 软

XAMPP(Apache+MySQL+PHP+PERL)是一个功能强大的建 XAMPP 软件站集成软件包.这个软件包原来的名字是 LAMPP,但是为了避免误解,最新的几个版本就改名为 XAMPP 了.它可以在Windows.Linux.Solaris.Mac OS X 等多种操作系统下安装使用,支持多语言:英文.简体中文.繁体中文.韩文.俄文.日文等. http://baike.baidu.com/view/864591.htm?fr=aladdin