반응형

게임 개발을 하다보면 ScrollView, ListView, TableView는 거의 필수로 들어 갑니다.

cocos2d-x 2.x 부터 ScrollView에 Menu를 붙여 사용할 때  Menu부분을 터치하여 스크롤 하면

스크롤이 안먹는 문제? 가 있었습니다.


3.9버전까지 오면서 수정 안되는걸 보면 cocos애들은 그방법이 더 옳다고 생각하는것 같내요.

현재 나온 ui::Widget에 있는 Button과 ScrollView를 사용하면 정상동작하고 보너스로 스크롤바 까지 보여줍니다.


2.x 버전을 사용하는 분들이나 3.x버전을 사용하고 있지만 기존에 ScrollView를 사용하시는 분들을 위해

Menu 부분을 터치하고 스크롤하여도 스크롤이 동작하는 소스를 공유합니다.


저는 이번 새로운 프로젝트에서 ui::Widget으로 갈아 탔지만 이전 프로젝트까지 잘 사용했던 소스 입니다.


ScrollView를 상속받아 새로 만드시는 분들도 계신데 그 방법보다 Menu를 상속받아 새로 만드는 방법이 간단해서

이 방법으로 소개 합니다.


- cocos2d-x 3.x 버전에서 사용 가능한 소스 입니다. (2.x 버전은 시간나는대로 올리겠습니다.)


[ ScrollMenu.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
//  cocos2d-x 3.x 버전에서 사용 가능 합니다.
 
//  ScrollMenu.h
//  Solitaire
//
//  Created by Kyung-Min Park on 2015. 4. 6..
 
#ifndef __ScrollMenu__
#define __ScrollMenu__
 
#include <stdio.h>
#include <cocos2d.h>
 
using namespace cocos2d;
 
class ScrollMenu:public Menu
{
public:
    virtual bool onTouchBegan(Touch* touch, Event* event);
    virtual void onTouchEnded(Touch* touch, Event* event);
    virtual void onTouchMoved(Touch* touch, Event* event);
    
    static ScrollMenu* createWithArray(const Vector<MenuItem*>& arrayOfItems);
    static ScrollMenu* createWithItem(MenuItem* item);
    static ScrollMenu* createWithItems(MenuItem *firstItem, va_list args);
    static ScrollMenu* create(MenuItem* item, ...);
 
    bool init();
    bool initWithArray(const Vector<MenuItem*>& arrayOfItems);
    
private:
    bool isTouching;
 
    Vec2 m_touchesStart;
    Vec2 m_touchesEnd;
};
 
#endif /* defined(__ScrollMenu__) */
 
cs



[ ScrollMenu.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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//  cocos2d-x 3.x 버전에서 사용 가능 합니다.
 
//  ScrollMenu.cpp
//  Solitaire
//
//  Created by Kyung-Min Park on 2015. 4. 6..
 
#include "ScrollMenu.h"
 
bool ScrollMenu::init()
{
    return initWithArray(Vector<MenuItem*>());
}
 
bool ScrollMenu::initWithArray( const Vector<MenuItem*>& arrayOfItems )
{
    if (Layer::init()) {
        _enabled = true;
 
        Size sizeVisible = Director::getInstance()->getWinSize();
        
        this->ignoreAnchorPointForPosition(true);
        setAnchorPoint(Vec2(0.5f, 0.5f));
        this->setContentSize(sizeVisible);
        
        setPosition(Vec2(sizeVisible.width/2, sizeVisible.height/2));
        
        int count = 0;
        
        for (auto& item : arrayOfItems) {
            this->addChild(item, count);
            count++;
        }
        
        _selectedItem = nullptr;
        _state = Menu::State::WAITING;
        
        setCascadeColorEnabled(true);
        setCascadeOpacityEnabled(true);
        
        auto touchListener = EventListenerTouchOneByOne::create();
        touchListener->setSwallowTouches(false);
        touchListener->onTouchBegan = CC_CALLBACK_2(ScrollMenu::onTouchBegan, this);
        touchListener->onTouchMoved = CC_CALLBACK_2(ScrollMenu::onTouchMoved, this);
        touchListener->onTouchEnded = CC_CALLBACK_2(ScrollMenu::onTouchEnded, this);
        touchListener->onTouchCancelled = CC_CALLBACK_2(ScrollMenu::onTouchCancelled, this);
        _eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
        
        return true;
    }
    return false;
}
 
ScrollMenu* ScrollMenu::createWithArray( const Vector<MenuItem*>& arrayOfItems )
{
    auto ret = new ScrollMenu();
    if (ret && ret->initWithArray(arrayOfItems)) {
        ret->autorelease();
    }
    else {
        CC_SAFE_DELETE(ret);
    }
    return ret;
}
 
ScrollMenu* ScrollMenu::createWithItem( MenuItem* item )
{
    return ScrollMenu::create(item, nullptr);
}
 
ScrollMenu* ScrollMenu::createWithItems( MenuItem *item, va_list args )
{
    Vector<MenuItem*> items;
    if( item ) {
        items.pushBack(item);
        MenuItem *= va_arg(args, MenuItem*);
        while(i) {
            items.pushBack(i);
            i = va_arg(args, MenuItem*);
        }
    }
    return ScrollMenu::createWithArray(items);
}
 
ScrollMenu* ScrollMenu::create( MenuItem* item, ... )
{
    va_list args;
    va_start(args,item);
    
    ScrollMenu *ret = ScrollMenu::createWithItems(item, args);
    
    va_end(args);
    
    return ret;
}
 
bool ScrollMenu::onTouchBegan(Touch* touch, Event* event)
{
    m_touchesStart = touch->getStartLocation();
    
    return Menu::onTouchBegan(touch, event);
    
}
 
void ScrollMenu::onTouchMoved(Touch* touch, Event* event)
{
    Menu::onTouchMoved(touch, event);
    
}
 
void ScrollMenu::onTouchEnded(Touch* touch, Event *event)
{
    // move은 가로 or 세로로 얼마만큼 Move되었나를 체크하여 그 이상이면 버튼 동작을 해지 합니다.
    // 값이 낮을수록 민감하게 반응하지만 너무 낮으면 버튼 터치가 힘들 수 있습니다.
    float move = 30.f;
    
    m_touchesEnd = touch->getLocation();
    Vec2 difference = m_touchesEnd - m_touchesStart;
    
    if (difference.x > move || difference.y > move ) {
        Menu::onTouchCancelled(touch, event);
    }
    else if (difference.x < -move || difference.y < -move) {
        Menu::onTouchCancelled(touch, event);
    }
    else {
        Menu::onTouchEnded(touch, event);
    }
}
 
 
cs

다른 부분은 수정할 부분이 없는데 float move = 30.f 이부분만 필요에 따라 수정하시면 됩니다.

값을 수정해 가며 스타일에 따라 테스트 해보시면 편합니다.



[ 사용 예제 ]

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 HelloWorld::init()
{
    if ( !Layer::init() ) return false;
    
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
 
    // ScrollView 생성
    ScrollView* scrollView = ScrollView::create(Size(visibleSize.width, visibleSize.height));
    scrollView->setDirection(ScrollView::Direction::HORIZONTAL);
    scrollView->setContentOffset(Vec2(00));
    scrollView->setContentOffsetInDuration(Vec2(00), 10.f);
    scrollView->setPosition(visibleSize * 0.5f);
    this->addChild(scrollView);
 
    // Menu 생성
    auto closeItem = MenuItemImage::create("CloseNormal.png",
                                           "CloseSelected.png",
                                           CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
    
    closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 ,
                                origin.y + closeItem->getContentSize().height/2));
 
    //auto menu = Menu::create(closeItem, NULL);
    // 기존에 Menu를 ScrollMenu로 변경만 하면 됩니다.
    auto menu = ScrollMenu::create(closeItem, NULL);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 1);
    
    // ScrollView에 버튼 붙이기
    scrollView->addChild(menu);
 
    return true;
}
cs

ListView, TableView에서도 사용 가능 합니다.


문제있으면 알려주세요 ^^

2.x 버전도 곧 올리겠습니다.


(추가적으로 ui::Widget에 있는 ScrollView와 Button을 이용한 방법도 올리겠습니다.)

반응형

+ Recent posts