在遊戲關卡選擇,道具店之中經常會用到類似於ScrollView的控件,之前用AndEngine引擎的時候簡單的實現了一個,(AndEngine滑動菜單傳送門),考慮到手頭的遊戲以後也會用到,就先用cocos2d-x簡單的實現了一下!原理非常類似,android中的app(比如微博,qq)也是類似的原理!
1.設計思路
自定義ScrollView繼承於CCLayer,固定其每頁的大小(ios上假設480x320),每一頁是一個子層(也是繼承於CCLayer),由使用者根據需求豐富,依次水平方向添加到ScrollView中(垂直方向的原理類似)!在ScrollView中實現觸摸監聽,如果是滑動事件,執行滾屏的操作,並且在觸摸事件完成後跳轉到當前頁;如果是點擊事件,則根據當前頁交由子層處理。
2.ScrollView類
#ifndef ScrollView_ScrollView_h
#define ScrollView_ScrollView_h
#include "cocos2d.h"
USING_NS_CC;
// 屏幕尺寸
const float WINDOW_WIDTH = 480.0f;
const float WINDOW_HEIGHT = 320.0f;
// 觸摸誤差
const int TOUCH_DELTA = 5;
class ScrollView: public CCLayer
{
private:
// 按下點
CCPoint m_TouchDownPoint;
// 擡起點 配合使用判斷是否爲點擊事件
CCPoint m_TouchUpPoint;
// 當前觸摸點
CCPoint m_TouchCurPoint;
private:
// 總頁數
int m_Page;
// 當前顯示頁
int m_CurPage;
private:
// 存儲所有頁層
CCArray *m_PageLayer;
private:
// 跳轉頁
void goToPage();
public:
ScrollView();
~ScrollView();
virtual bool init();
LAYER_NODE_FUNC(ScrollView);
public:
// 初始化相關
virtual void onEnter();
virtual void onExit();
// 觸摸事件相關
virtual bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent);
public:
// 添加頁
void addPage(CCLayer *pPageLayer);
};
#endif
CCPoint 成員是爲了判斷觸摸事件相關,
addPage(CCLayer *pLayer) 是提供的外部接口,向ScrollView中添加子層,
void goToPage() 在觸摸事件完成後根據當前偏移量跳轉到指定頁碼
CCArray *m_PageLayer 用來存儲所有的子層
3.判斷規則
(1) 移動事件
根據觸摸的點與當前ScrollView所在位置,得到ScrollView的偏移量
void ScrollView::ccTouchMoved(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)
{
// 移動
CCPoint touchPoint = CCDirector::sharedDirector()->convertToGL(pTouch->locationInView());
CCPoint posPoint = CCPointMake(getPositionX() + touchPoint.x - m_TouchCurPoint.x, getPositionY());
setPosition(posPoint);
m_TouchCurPoint = touchPoint;
}
(2) 觸摸事件完成
得到用戶按下和擡起的座標偏移量,和觸摸誤差作比較,如果大於觸摸誤差,則認爲是滑動;如果小於觸摸誤差,則認爲是點擊。
如果是點擊,則交由當前的子層處理
如果是滑動,則根據滑動的偏移量來決定是否滑動到哪一頁
void ScrollView::ccTouchEnded(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)
{
m_TouchUpPoint = CCDirector::sharedDirector()->convertToGL(pTouch->locationInView());
// 計算按下和擡起的偏移量
float off = (m_TouchUpPoint.x - m_TouchDownPoint.x) * (m_TouchUpPoint.x - m_TouchDownPoint.x) + (m_TouchUpPoint.y - m_TouchDownPoint.y) * (m_TouchUpPoint.y - m_TouchDownPoint.y);
if (off < (TOUCH_DELTA * TOUCH_DELTA)) {
// 點擊
// 向子Layer發送Click消息
((CCLayer*) m_PageLayer->objectAtIndex(m_CurPage))->ccTouchBegan(pTouch, pEvent);
}
else {
// 滑動結束
int offset = getPositionX() - m_CurPage * (-WINDOW_WIDTH);
if (offset > WINDOW_WIDTH / 2) {
// 上一頁
if (m_CurPage > 0) {
--m_CurPage;
}
}
else if (offset < -WINDOW_WIDTH / 2) {
// 下一頁
if (m_CurPage < (m_Page - 1)) {
++m_CurPage;
}
}
// 執行跳轉動畫
goToPage();
}
}
4.測試
寫了一個測試層TestLayer,主要用來測試接收觸摸事件
bool TestLayer::ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)
{
CCLOG("I am %d", getTag());
return true;
}
簡單的輸出一個標記,可根據需求再具體分發觸摸事件
5.用法
循環了10個子層,每層隨機了一個背景色,用來區別各個子層,分別加入到ScrollView對象中
bool ScrollViewScene::init()
{
bool bRet = false;
do {
CC_BREAK_IF(!CCScene::init());
ScrollView *scrollView = ScrollView::node();
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
for (int i=0; i<10; ++i) {
CCLayer *layer = TestLayer::node();
layer->setAnchorPoint(CCPointZero);
layer->setTag(i);
scrollView->addPage(layer);
}
this->addChild(scrollView);
bRet = true;
} while (0);
return bRet;
}
效果如圖