在游戏关卡选择,道具店之中经常会用到类似于ScrollView的控件,之前用AndEngine引擎的时候简单的实现了一个,(AndEngine滑动菜单传送门),考虑到手头的游戏以后也会用到,就先用cocos2d-x简单的实现了一下!原理非常类似,android中的app(比如微博,qq)也是类似的原理!
1.设计思路
自定义ScrollView继承于CCLayer,固定其每页的大小(ios上假设480x320),每一页是一个子层(也是继承于CCLayer),由使用者根据需求丰富,依次水平方向添加到ScrollView中(垂直方向的原理类似)!在ScrollView中实现触摸监听,如果是滑动事件,执行滚屏的操作,并且在触摸事件完成后跳转到当前页;如果是点击事件,则根据当前页交由子层处理。
2.ScrollView类
- <span style="font-size:16px;">#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
- </span>
CCPoint 成员是为了判断触摸事件相关,
addPage(CCLayer *pLayer) 是提供的外部接口,向ScrollView中添加子层,
void goToPage() 在触摸事件完成后根据当前偏移量跳转到指定页码
CCArray *m_PageLayer 用来存储所有的子层
3.判断规则
(1) 移动事件
根据触摸的点与当前ScrollView所在位置,得到ScrollView的偏移量
- <span style="font-size:16px;">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;
- }
- </span>
(2) 触摸事件完成
得到用户按下和抬起的坐标偏移量,和触摸误差作比较,如果大于触摸误差,则认为是滑动;如果小于触摸误差,则认为是点击。
如果是点击,则交由当前的子层处理
如果是滑动,则根据滑动的偏移量来决定是否滑动到哪一页
- <span style="font-size:16px;">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)) {
-
-
- ((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();
- }
- }</span>
4.测试
写了一个测试层TestLayer,主要用来测试接收触摸事件
- <span style="font-size:16px;">bool TestLayer::ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)
- {
- CCLOG("I am %d", getTag());
-
- return true;
- }</span>
简单的输出一个标记,可根据需求再具体分发触摸事件
5.用法
循环了10个子层,每层随机了一个背景色,用来区别各个子层,分别加入到ScrollView对象中
- <span style="font-size:16px;">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;
- }</span>
效果如图
示例代码下载