本文部分代码来自http://blog.csdn.net/q229827701/article/details/39996171,在此基础上稍作修改,并添加了详细注释。
1、ScrollText类
#ifndef __ScrollTest__ScrollText__ #define __ScrollTest__ScrollText__ #include "cocos2d.h" #include "ui/CocosGUI.h" USING_NS_CC; using namespace ui; class ScrollText : public cocos2d::Node { public: enum class FONT_TYPE //类似类的创建。可以通过命名空间,访问变量,防止命名冲突 { BOLD,NOMAL,SLIM }; CREATE_FUNC(ScrollText); static ScrollText* create(cocos2d::Node* pMoveChild,...); static ScrollText* create(FONT_TYPE mType); void setAutoScroll(bool isScroll,bool byWidth = false); CC_CONSTRUCTOR_ACCESS://protected的宏定义 ScrollText(); virtual ~ScrollText(); virtual bool init(); virtual bool initWithDatas(cocos2d::Node* &pMoveChild); virtual bool init(FONT_TYPE mType); bool initLabel(FONT_TYPE mType); bool initScroll(); void update(float dt); virtual void onEnterTransitionDidFinish(); //此处设计的很巧妙,_content其实是赋值给_mLabel的,我们可以通过_content传递跑马灯的内容尽量 //_mSize其实大小等于ScrollSize的size,如果忘记设置ScrollText的size,默认为构造函数里的数值 CC_SYNTHESIZE_READONLY(cocos2d::Node*,_mLabel,Label); CC_SYNTHESIZE_READONLY(cocos2d::Size,_mSize,TextSize); CC_SYNTHESIZE(std::string,_content,Content); public: void setSize(const cocos2d::Size &size); private: cocos2d::Vector<Node*> _mNodes; bool _autoScroll; bool _byWidth; cocos2d::ui::ScrollView* _scrollView; }; #endif /* defined(__ScrollTest__ScrollText__) */
#include "ScrollText.h" #include <stdarg.h> //宏用来判断函数或对象是否执行或创建成功 #define IF_RETURN(cont,p) if((cont)){return (p);} #define IF_RETURN_FALSE(cont) IF_RETURN(cont,false) //构造函数 ScrollText::ScrollText() :_autoScroll(false),_byWidth(false),_mLabel(nullptr),_content("no content"),_scrollView(nullptr),_mSize(120,40) { } //析构函数 ScrollText::~ScrollText() { CC_SAFE_RELEASE(_mLabel); CC_SAFE_RELEASE(_scrollView); } //调用通过CREATE_FUNC调用init()函数 bool ScrollText::init() { //默认字体类型初始化 IF_RETURN_FALSE(!init(FONT_TYPE::NOMAL)); return true; } //通过字体类型初始化 bool ScrollText::init(FONT_TYPE mType) { bool bRet = false; do{ CC_BREAK_IF(!Node::init()); //初始化跑马灯内容和ScrollView对象 IF_RETURN_FALSE(!initLabel(mType)); IF_RETURN_FALSE(!initScroll()); bRet = true; }while(0); return bRet; } //初始化ScrollView对象 bool ScrollText::initScroll() { _scrollView = ui::ScrollView::create(); IF_RETURN_FALSE(!_scrollView); _scrollView->setContentSize(_mSize); addChild(_scrollView); setContentSize(_scrollView->getContentSize()); //添加可变参数的子对象,前提是使用create(cocos2d::Node *pMoveChild,...)创建ScrollText对象 for(auto child:_mNodes) { IF_RETURN_FALSE(!child); _scrollView->addChild(child); } IF_RETURN_FALSE(!_mLabel); _scrollView->addChild(_mLabel); return true; } //帧更新函数 void ScrollText::update(float dt) { if(!_mLabel) { return ; } //获取跑马灯位置判断的相关数据 float contentX = getContentSize().width*(-1.0f); float _labelX = _mLabel->getContentSize().width*(-1.0f); //如果想通过跑马灯内容来确定是否循环播放 if(_byWidth) { //执行此处,即如果跑马灯的字体内容超出了ScrollText的宽度,则设置循环tag为真 _autoScroll = _mLabel->getContentSize().width>getContentSize().width?true:false; } //如果循环播放为真 if(_autoScroll) { //首先判断跑马灯内容有没有完全跑出ScrollText,因为锚点是左边,所以当位置为负的_mLabel的width时,完全跑出ScrollText if(_mLabel->getPositionX() >= _labelX) { _mLabel->setPositionX(_mLabel->getPositionX() - 1.0f); }else{ _mLabel->setPositionX(-contentX); } }else { _mLabel->setPositionX(0); } //设置跑马灯内容 auto label = dynamic_cast<Label*>(_mLabel); if(label && label->getString() != _content) { label->setString(_content); } } //设置是否循环播放跑马灯内容 void ScrollText::setAutoScroll(bool isScroll,bool byWidth/*=false*/) { _byWidth = byWidth; if(isScroll && byWidth) { _autoScroll = _mLabel->getContentSize().width>getContentSize().width?true:false; }else{ _autoScroll = isScroll; } } //可变形参创建跑马灯,可以添加任意Node对象到跑马灯中 ScrollText* ScrollText::create(cocos2d::Node *pMoveChild,...) { auto sTxt = new ScrollText(); if(sTxt) { //可变参数指针地址的获取 va_list lst;//va_list是char*类型,用户获取可变参数的地址 va_start(lst,pMoveChild);//从最后入栈的变量初始化指针 Node* pNow; pNow = pMoveChild; bool isFirst = true; while (pMoveChild) { if (nullptr != pNow) { if(isFirst) isFirst = false; else sTxt->_mNodes.pushBack(pNow); pNow = va_arg(lst,Node*);//依次获取可变参数的地址,第二个参数是可变形参的类型。注意传参时一定是Node的对象或子对象 }else break; } va_end(lst);//结束 //初始化跑马灯内容,真正把跑马灯内容加进去的函数是initScroll if(sTxt->initWithDatas(pMoveChild)) { sTxt->autorelease(); return sTxt; }else{//如果函数执行失败后安全释放内存,防止野指针 delete sTxt; sTxt = NULL; return NULL; } }else{ delete sTxt; sTxt = NULL; return NULL; } } ScrollText* ScrollText::create(ScrollText::FONT_TYPE mType) { auto text = new ScrollText(); if(text && text->init(mType)) { text->autorelease(); return text; }else{ delete text; text = nullptr; return nullptr; } } //初始化跑马灯内容 bool ScrollText::initWithDatas(cocos2d::Node *&pMoveChild) { bool bRet = false; do{ CC_BREAK_IF(!Node::init()); IF_RETURN_FALSE(!pMoveChild); _mLabel = pMoveChild; _mLabel->setAnchorPoint(Vec2::ANCHOR_BOTTOM_LEFT); IF_RETURN_FALSE(!initScroll());//在initScroll真正把跑马灯内容初始化 bRet = true; }while(0); return bRet; } //设置跑马灯可见区域Size void ScrollText::setSize(const cocos2d::Size& size) { _mSize = size; _scrollView->setContentSize(_mSize); setContentSize(_mSize); } bool ScrollText::initLabel(ScrollText::FONT_TYPE mType) { _mLabel = Label::createWithSystemFont("Content","Arial Bold",40); IF_RETURN_FALSE(!_mLabel); _mLabel->setAnchorPoint(Vec2::ANCHOR_BOTTOM_LEFT); return true; } //节点进入完成,执行帧更新 //例如:如果我ScrollText节点执行一个进入动画的特效,如果不设置此处,可能在动画播放的同时, //跑马灯就开始走了,为了达到动画播放完毕,才执行跑马灯的播放,就是此处函数的功能啦 void ScrollText::onEnterTransitionDidFinish() { scheduleUpdate(); }