Scrolling vertically a CCMenu


#1

Hi all, I’m trying to do the typical menu which is scrolled vertically. To show an example:

I don’t know if you have played that game, but you have the menu and you can scroll up and down touching everywhere.

What I’ve done is create a CCMenu and add it to a CCLayer, which is the one that is scrolled up and down. I’ve also managed to smoothly take the menu back inside the view if the player scrolls too much up or down. But, there’s a problem.

I can only scroll the layer if I touch outside the CCMenu. If I touch inside the CCMenu it won’t scroll.

I’ve thought to create a class containing a CCMenu but I suspect the problem will persist.
Also I’ve tryed to extend CCMenu to contain ccTouches’ events but I’m having problems as all CCMenu methods are static and I don’t seem to be able to finally extend the class.

I’m sure this can be done in a easy way so please, show me. Thanks.

  • Using cocos 2.0.4 -

#2

Ok I finally kinda achieved it but I dont know how because the new class doesn’t really extends nothing…

class MenuPanel : public CCMenu
{
public:
    MenuPanel(void);
    ~MenuPanel(void);
    static MenuPanel* create();
    static MenuPanel* create(CCMenuItem* item, ...);
};

And then just implented “create” like this:

MenuPanel* MenuPanel::create()
{
    return MenuPanel::create(NULL, NULL);
}

MenuPanel* MenuPanel::create(CCMenuItem* item, ...)
{
    va_list args;
    va_start(args,item);
    MenuPanel *pRet = new MenuPanel();
    if (pRet && pRet->initWithItems(item, args))
    {
        pRet->autorelease();
        va_end(args);
        return pRet;
    }
    va_end(args);
    CC_SAFE_DELETE(pRet);
    return NULL;
}

The only catch I have now is that if I add

void MenuPanel::registerWithTouchDispatcher()
{
    CCDirector* pDirector = CCDirector::sharedDirector();
    pDirector->getTouchDispatcher()->addTargetedDelegate(this, 0, false);
}

It will execute the menu callback when touchs ended, but if I don’t add it, the menu will scroll but I won’t be able to launch the callback. Though it must be easy to make it work.


#3

To end my monologue I finally achieved it.

To help some newbie like me, you just have to put a flag in the MyScene wich contains the scrollable CCLayer that contains the PanelMenu mentioned above.

Lets say the flag is “touchsMoving”.

bool MyScene::init(){
    ...

    touchsMoving = true;

    ...
}

bool MyScene::ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent){
    ...

    touchsMoving = false;

    ...
}

And then just in the menu CallBack of any item in PanelMenu:

void MySceneLayer::menuCallback1(CCObject* sender)
{
    if (!touchsMoving){
    // do whatever
    ...
    }
}

#4

I think this has been implemented in CCListView and/or CCTableView. Might want to look at samples/TestCpp


#5

EDIT:

Ok I don’t know why but I missed “ExtensionTest” at Tests. Thanks TableView seems to let me do this. Though I almost achieved the same (not that smooth but nearly the same)


#6

I don’t seem to be able to add CCTableView in my project.

Can’t #include “CCTableView.h”, neither “cocos-ext.h”

I’ve set all the properties of my project to the ones at TestCpp.

Edit:

Any tip?

Ok finally managed it with:

#include “extensions\GUI\CCScrollView\CCTableView.h”
using namespace cocos2d::extension;


#7

Just say that CCTableView doesn’t really offer more options.

It would be much more useful to have it as something like “CCViewLayout” where you can place the objects you want in each row/slot of the table. Actually you can’t make a Menu like the one I posted in my first post and also I challenge someone to try to do it with CCTableView, you’ll get mad.

That said, before spending like 10 hours with it I’m gonna try to implement a vertical layout to achieve it since right now cocos2d-x doesn’t offer something like that.

Cheers


#8

I didn’t really use the CCTableView myself though since it’s easier to just create my own scrolling layer using CCLayerColor objectss


#9

So, do you make a class which extend CCLayerColor and then manually add the scrolling with touch events?

I’m interested in your way if is not too much work, could you explain it?


#10

Depends on the situation. Most of the time, I just have a CCLayerColor member class and have it respond to touch.
Easiest way to explain it is to have a CCLayerColor object that contains a bunch of CCMenuItemImage objects.

This is a short example: http://pastebin.com/j19xGWsd It’s supposed to be animated using CCActions (similar to most “level select screens”)
What it does it it creates a scene, a layer, and a menu with a bunch of items on it. If you touch and drag, the menu would follow along the touch (only horizontally as it is a menu, its easy to do it only vertically though). Then when the touch ends, it calculates the length of the touch. If it is greater than the length of your choosing, it runs a CCMoveTo action ( although not coded, very sleepy right now ). If it is not, then it goes back to CCPointZero.

I have a more complicated work with a bit of physics (friction and bounce) but that’s way too long to re-create. Try reading the code here: http://www.supersuraccoon-cocos2d.com/2011/03/29/scroll-layer-demo/ It’s in objective-c though but that’s where I based off one of my custom scroll layers.


#11

Thanks for your time Lance. I get it now.

It looks similar to what I was doing on my first posts. I think it’s all actions + scheduling & unscheduling (for example when CCMoveTo is running and TouchBegan you have to stop the action unscheduling it).

To be truth CCScrollView, while its fine as a prefab, is not as smooth or as natural as it should be, specially at bounds. My first shitty version of custom scroll view did scroll more smoothly and in a more natural way so I think I’ll keep working on it.

The only interesting thing of CCScrollView is that it only draws inside a frame, and that might save compution time.

In my first implementation I used easeOut actions to scroll giving a “friction feeling” and a combination of actions at bounds to resemble “bounce” and believe me, it looks pretty natural.


#12

Ok I’m done.

Finally I’ve implemented a scrolling layer in which you add items and the layer itself position them from top to down with vertical and horizontal margins. Also if the layer needs to make its height bigger (needed if it has any bg color) it automatically resizes to meet the necessary size for the items added.

You can add any kind of item, it will put it under the items added previously making it easy to do a scrolling menu.

I’ve taken as inspiration this layer: http://www.supersuraccoon-cocos2d.com/2011/03/29/scroll-layer-demo/

It’s attached on the post. The only thing left is to cut the regions outside the CCRect which contains the view. It needs code for Horizontal Scrolling but it’s almost the same than Vertical Scrolling.

Have fun.

THIS VERSION IS OUTDATED


#13

This might come in handy so let me keep it. :slight_smile:

A good additional feature might be some kind of masking thing so that it doesn’t occupy the whole screen (like the top and bottom parts fade out to transparency or something).


#14

There was a few things to fix, fixed in the attachements. Now it works ok - at least Vertically -

By the way I think that the masking you say is super easy to do (if there’s an opacity gradient function wich I supose there is). I don’t need it but no big deal to make it.

Hope someone find this useful. Back to work!


#15

danadn _ wrote:

There was a few things to fix, fixed in the attachements. Now it works ok - at least Vertically -
>
By the way I think that the masking you say is super easy to do (if there’s an opacity gradient function wich I supose there is). I don’t need it but no big deal to make it.
>
Hope someone find this useful. Back to work!

Hello danadn and thank you for some interesting reading.

I have also had some problems with CCScrollView, first of all that it seems to scroll upside down and eventually I will probably run into the same problem you had with CCMenu.

I decided to try your class out and it looks really good and simple to use, like I wanted CCScrollView to be. Anyway, when I run my code I get an assert error. Any help would be appreciated, I have probably spent more than 10 hours trying to got CCScrollView to work. :stuck_out_tongue: Anyways, here’s my code to test your class:

        header = CCSprite::create("menuHeader.png");
        game1 = CCSprite::create("menuGameSample1.png");
        game2 = CCSprite::create("menuGameSample2.png");
        footer = CCSprite::create("menuFooter.png");

        ////////////////
        //MENU SETTINGS

        CCMenuItemSprite *menuHeader = CCMenuItemSprite::create( header, header, this, menu_selector(Menu::menu1Callback));
        CCMenuItemSprite *menuGame1 = CCMenuItemSprite::create( game1, game1, this, menu_selector(Menu::menu1Callback));
        CCMenuItemSprite *menuGame2 = CCMenuItemSprite::create( game2, game2, this, menu_selector(Menu::menu1Callback));
        CCMenuItemSprite *menuFooter = CCMenuItemSprite::create( footer, footer, this, menu_selector(Menu::menu1Callback));
        CCMenu* menu = CCMenu::create( menuHeader, menuGame1, menuGame2, menuFooter, NULL );

        /////////////////
        //Sliding layer

        CCSlidingLayer* slidingLayer = CCSlidingLayer::create( Vertically, CCSizeMake(winSize.width, winSize.height), CCRectMake(0,0,winSize.width,winSize.height), ccc4(0,0,0,0));
        slidingLayer->addChildWithSize( menuHeader, header->getContentSize(), kAlignmentCenter);
        slidingLayer->addChildWithSize( menuGame1, game1->getContentSize(), kAlignmentCenter);
        slidingLayer->addChildWithSize( menuGame2, game2->getContentSize(), kAlignmentCenter);
        slidingLayer->addChildWithSize( menuFooter, footer->getContentSize(), kAlignmentCenter);

        this->addChild( slidingLayer );

Thank you for sharing your solution! :slight_smile:


#16

Hi Andreas,

The only way to fix the CCMenu issue is to make a CCMenu subclass and implement this function:

//MenuPanel is a CCMenu subclass
void MenuPanel::registerWithTouchDispatcher()
{
    CCDirector* pDirector = CCDirector::sharedDirector();
    pDirector->getTouchDispatcher()->addTargetedDelegate(this, 0, false);
}

Here’s the MenuPanel subclass I’m using:

MenuPanel.h

#include "cocos2d.h"

USING_NS_CC;

class MenuPanel : public CCMenu
{
public:
    MenuPanel(){}
    virtual ~MenuPanel(){}
    static MenuPanel* create(CCMenuItem* item, ...);
    virtual void registerWithTouchDispatcher();
};

MenuPanel.cpp

#include "MenuPanel.h"

MenuPanel* MenuPanel::create(CCMenuItem* item, ...)
{
    va_list args;
    va_start(args, item);
    MenuPanel *pRet = new MenuPanel();
    if (pRet && pRet->initWithItems(item,args))
    {
        pRet->autorelease();
        va_end(args);
        return pRet;
    }
    va_end(args);
    CC_SAFE_DELETE(pRet);
    return NULL;
}

void MenuPanel::registerWithTouchDispatcher()
{
    CCDirector* pDirector = CCDirector::sharedDirector();
    pDirector->getTouchDispatcher()->addTargetedDelegate(this, 0, false);
}

I attach you my last implementation of CCSlidingLayer, since I found more bugs. (Can’t do it perfect at first time ;))

You have to take care about a couple of things:

1 - When you create a CCMenu or a subclass of it, and you want to add it to CCSlidingLayer, you have to set the CCMenu size to what it’s containing size.

I mean, when you create a menu, at least it happens when I create them, its size is equal to window size (320x480 on win32). And that’s not what we want. You have to myMenu~~>setContentSize) and then add it to CCSlidingLayer.
Also you are adding CCMenuItemSprite’s which I’m not sure if that’s fine for CCLayers.
Try this code, it should work BUT USING A CCMENU SUBCLASS.
<pre>
header = CCSprite::create;
game1 = CCSprite::create;
game2 = CCSprite::create;
footer = CCSprite::create;
////////////////
//MENU SETTINGS
float menuHeight = 0.0f;
CCMenuItemSprite menuHeader = CCMenuItemSprite::create);
CCMenuItemSprite
menuGame1 = CCMenuItemSprite::create);
CCMenuItemSprite menuGame2 = CCMenuItemSprite::create);
CCMenuItemSprite
menuFooter = CCMenuItemSprite::create);
CCMenu* menu = MenuPanel::create;
menuHeight = menuHeader~~>getContentSize().height

  • menuGame1~~>getContentSize.height
  • menuGame2~~>getContentSize().height
  • menuFooter~~>getContentSize.height;
    menu~~>setContentSize(CCSize(winSize.width, menuHeight));

/////////////////
//Sliding layer

CCSlidingLayer* slidingLayer = CCSlidingLayer::create( Vertically, CCSizeMake(winSize.width, winSize.height), CCRectMake(0,0,winSize.width,winSize.height), ccc4(0,0,0,0));

// To let the slidingLayer resize itself, use a lower height:
// CCSlidingLayer* slidingLayer = CCSlidingLayer::create( Vertically, CCSizeMake(winSize.width, 1), CCRectMake(0,0,winSize.width,winSize.height), ccc4(0,0,0,0))

slidingLayer~~>addChildWithSize, kAlignmentCenter);
this~~>addChild( slidingLayer );

Also don’t forget you can put some color in the SlidingLayer to see if it’s working properly.

Hope this helps.

PS: CCSlidingLayer doesn’t handle quite well when you add a CCLayer to it and then resize that CCLayer. The workaround is to add directly the items in the
CCSlidingLayer


#17

Thanks a lot for replying, reading through and commenting my code. I will try this out as soon as possible. :slight_smile:


#18

No problem, let me know if it worked


#19

danadn _ wrote:

No problem, let me know if it worked

It works like a charm. Thanks a lot! :smiley: I can’t with words describe how grateful I am after all the frustrating hours with CCScrollView. :slight_smile:


#20

I’m happy it’s useful for others, I spent the same 10 hours of frustration with CCScrollView. It is too complicated to understand whats going on there, a lot of variables here and there and also it doesn’t move as naturally (I think) as it should. CCSlidingLayer may not be perfect but the code is more easy, understandable and the scrolling looks pretty natural.

Have fun.