天天看點

Cocos2dx3.2編寫常用UI元件(二)滾動計數器NumberScroller

前言:

廢話不多說,先看最終效果圖

Cocos2dx3.2編寫常用UI元件(二)滾動計數器NumberScroller

正文:

1、使用說明:

①引入頭檔案 “NumberScroller.h"

②用NumberScroller::create(int length,int fontSize) 函數來建立NumberScroller對象

③利用setNumber(int number)函數來設定計時器裡面的值(這裡隻實作了向後滾動,即設定的值要大于等于目前值)

④利用getNumber獲得目前計數器顯示的值

⑤利用setTime(float time)函數來設定計時器滾動的速度,預設為1

2、實作思路:

①根據輸入的長度length确定計時器的長度(即多少位)

②根據輸入的fontSize确定字型大小

③建立length條并排放置的豎着的從01234567890(注意末尾有個零)

④根據設定的數字,不同的豎條移動相應的位置。

⑤利用遮罩層遮罩

3、源碼解析:

對應思路①②

bool NumberScroller::init(int length,int fontSize){
	if(!Node::init())	return false;
	m_length = length;
	m_fontSize = fontSize;
	m_visibleNode = Node::create();
	
	//排好length列數字
	for(int i=0;i<length;i++){
		NumberColumn* column = NumberColumn::create(fontSize);
		m_columns.pushBack(column);
		column->setAnchorPoint(Point(0,0));
		column->setPosition(i * fontSize,0);
		column->setTime(m_time);
		m_visibleNode->addChild(column);
	}
	/*設定遮罩層*/
	ClippingNode* cliper = ClippingNode::create();
	DrawNode* drawNode = DrawNode::create();
	Point points[] = {Point(getPosition()),Point(getPositionX(),getPositionY()+m_fontSize),
		Point(getPositionX()+m_length*m_fontSize,getPositionY()+m_fontSize),Point(getPositionX()+m_length*m_fontSize,getPositionY())};
	drawNode->drawPolygon(points,4,Color4F(0,0,0,1),0,Color4F(0,0,0,1));
	
	cliper->setStencil(drawNode);
	cliper->addChild(m_visibleNode);
	this->addChild(cliper);
}
           

先暫時不用看設定遮罩層部分,前面隻是儲存長度和字型大小到成員變量中,下面的一個for循環就是用來排好length列數字的

其中這裡出現了一個NumberColumn類,表示一列數字01234567890

具體的初始化函數如下

bool NumberColumn::init(int fontSize){
	if(!Node::init())	return false;
	m_numbers = Node::create();
	this->addChild(m_numbers);
	m_fontSize = fontSize;

	this->scheduleUpdate();

	for(int i=0;i<10;i++){
		char str[2];
		str[0] = i + '0';
		str[1] = '\0';

		Label* temp = Label::create(str,"",fontSize);
		temp->setAnchorPoint(Point(0,0));
		temp->setPosition(Point(0,i * fontSize));
		m_numbers->addChild(temp);
	}
	char str[2];
	str[0]='0';
	str[1]='\0';
	Label* temp = Label::create(str,"",fontSize);
	temp->setAnchorPoint(Point(0,0));
	temp->setPosition(Point(0,10 * fontSize));
	m_numbers->addChild(temp);

	return true;
}
           

NumberColumn的init函數裡面,除了儲存長度和字型大小和啟動update外,接下來就是構造一個01234567890的豎列

注意,我們這裡把所有Label都放在一個Node裡面,這樣可使我們更友善的操作

最後,回到NumberScroller  初始化完的效果是這樣子(無遮罩)的:

Cocos2dx3.2編寫常用UI元件(二)滾動計數器NumberScroller

NumberScroller最重要的函數還是setNumber

void NumberScroller::setNumber(int number){
	if(number > m_cur_num){
		m_cur_num = number;

		for(int i=0;i<m_length;i++){
			m_columns.at(m_length - i -1)->setNumber(number);
			number /= 10;
		}		
	}
}
           

可以看出都是間接地調用NumberColumn的setNumber方法

void NumberColumn::setNumber(int number,bool direction){
	m_target_num = number;
	int delta = m_target_num - m_cur_num;
	update_speed = (delta * m_fontSize / m_time);
}
           

奇怪啦,NumberColumn也隻是簡單的把傳來的number儲存到m_target_num裡面,還有計算滾動的速度 也沒有實作滾動的動作啊

奧秘在于NumberColumn啟動的update函數

void NumberColumn::update(float d){
	if(m_cur_num != m_target_num){<span style="white-space:pre">		</span>//如果目前顯示的數字不等于目标數字,即要開始滾動
		float dis = update_speed*d;<span style="white-space:pre">	</span>//每次調用update函數需要滾動的距離等于update_speed 乘以 d (update_speed在setNumber函數中已經算出)
		m_numbers->setPositionY(m_numbers->getPositionY() - dis);<span style="white-space:pre">	</span>//每次使整條向下移動dis距離
		update_moveSum += dis;<span style="white-space:pre">		</span>//update_moveSum 用于儲存現在到底移動了多少距離
		if(update_moveSum >= m_fontSize){<span style="white-space:pre">	</span>//如果現在已經移動了一個字大小的距離
			m_cur_num++;<span style="white-space:pre">			</span>//目前數字+1
			m_numbers->setPositionY( - m_cur_num%10 * m_fontSize);<span style="white-space:pre">	</span>//标準對齊
			update_moveSum = 0; <span style="white-space:pre">	</span>//重置
		}
	}
}
           

update函數便是實作滾動效果的核心,每一行都給了詳細的注釋

因為我們這裡要實作的是類似于時鐘的滾動效果(秒針轉1圈時,分針轉1/60圈),應用到計時器,個位滾動10次,十位才會轉一下

是以對應每一條NumberColum,其對應的m_cur_num是不一樣的。

例如999這個數 ,第一條(百位)對應的m_cur_num應該是9,第二條(十位)對應的m_cur_num應該是99,第三條(個位)對應的m_cur_num應該是999

由于setNumber時候計算了速度:

int delta = m_target_num - m_cur_num;
update_speed = (delta * m_fontSize / m_time);
           

間隔越大,速度越快,是以保證了每條NumberColumn均會在m_time時間内完成滾動

有了上面的介紹,再回過頭來再看一次NumberScroller的setNumber函數,是不是恍然大悟呢

void NumberScroller::setNumber(int number){
	if(number > m_cur_num){
		m_cur_num = number;

		for(int i=0;i<m_length;i++){
			m_columns.at(m_length - i -1)->setNumber(number);
			number /= 10;
		}		
	}
}
           

完成效果後如下:

Cocos2dx3.2編寫常用UI元件(二)滾動計數器NumberScroller

最後,添加遮罩層,隻顯示目前數字部分。

bool NumberScroller::init(int length,int fontSize){
        。。。省略了一部分。。。
	/*設定遮罩層*/
	ClippingNode* cliper = ClippingNode::create();
	DrawNode* drawNode = DrawNode::create();
	Point points[] = {Point(getPosition()),Point(getPositionX(),getPositionY()+m_fontSize),
		Point(getPositionX()+m_length*m_fontSize,getPositionY()+m_fontSize),Point(getPositionX()+m_length*m_fontSize,getPositionY())};
	drawNode->drawPolygon(points,4,Color4F(0,0,0,1),0,Color4F(0,0,0,1));
	
	cliper->setStencil(drawNode);
	cliper->addChild(m_visibleNode);
	this->addChild(cliper);
}
           

關于遮罩的基本知識,推薦一個很好的blog

http://shahdza.blog.51cto.com/2410787/1561937

4、附上源碼:

點選打開連結

繼續閱讀