Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cocos2d-x CCScrollview is scrolling upside down

Greetings Stackoverflow,

I am currently trying to implement a scrolling menu into a cocos2d-x game that I am developing. The problem is that the items are scrolling in the opposite direction that I want. For example, if I drag my finger from the bottom of the screen, to the top, the sprite will bounce back to its original position.

And if I drag my finger from the top to the the bottom, the sprite will follow my finger (until it reaches the boundary).

This is my code:

    /////////////////////
    //SCROLL VIEW + LAYER
    CCLayer *layer = CCLayer::create();
    layer->setContentSize( CCSizeMake( winSize.width, winSize.height ) );
    layer->addChild( menu );

    scrollView = CCScrollView::create();
    scrollView->retain();
    scrollView->setContentSize( CCSizeMake ( layer->getContentSize().width,  layer->getContentSize().height ) );
    scrollView->setDirection( CCScrollViewDirectionVertical );
    scrollView->setPosition( ccp( 0,0 ) );
    scrollView->setContainer( layer );

    this->addChild(scrollView);

    scrollView->setContentOffset( CCPointZero );

Any suggestions or help is highly appreciated. If you have any other work around for creating a scrolling menu with another approach, I would love to hear.

Thank you!

Best regards Andreas

like image 362
Prince John Avatar asked Dec 09 '22 17:12

Prince John


2 Answers

Refer: cocos2dx/extensions/GUI/CCScrollView/CCScrollView.cpp

Specifically CCScrollView::minContainerOffset and CCScrollView::maxContainerOffset

CCScrollView works in OpenGL coordinates (as opposed window coordinates) - values are relative to (left, bottom) with positive Y axis going upwards. Also, keep in mind the scroll view's positioning and the container are anchored (CCNode::setAnchorPoint) to (left, bottom).

When you scroll down (move/pull the content upwards to see content below the cut/clip), you see the content below the bottom edge of the screen but it bounces back the moment you let go of the touch/drag because maxContainerOffset returns (0, 0) and you've just tried to move to a positive content offset.

Coordinates of CCScrollView and the container

The diagram shows the state of the scrollview and the container when they're created/initialized. This is the state or the coordinates to "think in" when setting up and positioning the child elements and container. The gray rectangle (left, bottom) shows the valid region for scrolling the container. Imagine the container's anchor point moves inside it.

To see the container scrolled to the top to begin with (what you'd expect when working in window coordinates), set the content offset accordingly (immediately after setting it up). This will give the expected results/behavior.

scrollView->setContentOffset(ccp(0.f, (scrollViewHeight-scrollContainerHeight)), false);

A more complete example is in the edited code below.

  1. A "fix" to allow scrolling in the window coordinate (positive Y axis downwards) may be to adjust the extension, this would require you to rebuild the cocos2dx library and will affect all projects (even other sample code you may try).

    /*
    // (StackOverflow Post Edit: This hack is not required.)
    CCPoint CCScrollView::maxContainerOffset()
    {
        // Default CCPointZero;
        return ccp(0.0f, m_pContainer->getContentSize().height*m_pContainer->getScaleY() - m_tViewSize.height);
    }
    CCPoint CCScrollView::minContainerOffset()
    {
        // Default Y = m_tViewSize.height - m_pContainer->getContentSize().height*m_pContainer->getScaleY();
        return ccp(m_tViewSize.width - m_pContainer->getContentSize().width*m_pContainer->getScaleX(), 
               0.f);
    }
    */
    
  2. A less intrusive hack, is to scale the ScrollView instance to -1 and the children of the container node also to -1. You also have to reposition the child nodes taking into account the inverse scale. The result of the scaling at both levels is the content (child nodes) are viewed straight (not flipped). The result of scaling the ScrollView to -1 is that the scroll happens for you in the expected direction. Note though this "fix" will flip scrolling in the X axis as well and hence is only suitable if you wish to scroll vertically (CCScrollViewDirectionVertical).

    CCSize winSize = CCDirector::sharedDirector()->getWinSize();
    CCLayer* scrollContainer = CCLayer::create(); // Container for the scroll view
    scrollContainer->setAnchorPoint(CCPointZero); // CCScrollView does this too when it's set as the container.
    
    // Content for the container
    CCSprite *tallContentA = CCSprite::create("TallContentA.png");
    tallContentA ->setPosition(ccp(winSize.width*0.5f, winSize.height*0.9f));
    CCSprite *tallContentB = CCSprite::create("TallContentB.png");
    tallContentB ->setPosition(ccp(winSize.width*0.5f, winSize.height*0.1f));
    scrollContainer->addChild(tallContentA, 2);
    scrollContainer->addChild(tallContentB, 2);
    
    float scrollContainerHeight = tallContentA->getContentSize().height + tallContentB->getContentSize().height;
    scrollContainer->setPosition(CCPointZero);
    scrollContainer->setContentSize(CCSizeMake(winSize.width, scrollContainerHeight*1.05f));
    
    // Set up scroll view
    CCScrollView* scrollView = CCScrollView::create(winSize, scrollContainer);
    scrollView->setPosition(CCPointZero);
    scrollView->setDirection(CCScrollViewDirectionVertical);
    // ScrollView initializes at the (left, bottom). The container also gets positioned relative to that and goes Y-up.
    // Pre-set it to the value CCScrollView::minContainerOffset will return when it's scrolled to the top
    // (note, this is a negative number, indicating the touch moving downwards, i.e. it's pre-scrolled such that the top of the content is visible when we begin)
    scrollView->setContentOffset(ccp(0.f, (winSize.height-scrollContainerHeight*1.05f)), false);
    /*
    // (StackOverflow Post Edit: This hack is not required.)
    // Hack: CCScrollView's maxContainerOffset is (0, 0) and minContainerOffset is (difference between view and content size which is negative)
    // It's designed to be (left, bottom) based and positive scrolling means showing stuff above the top of the screen.
    // Since we're using it in terms of Window coordinates ((left, top) based), we scale the scroll view
    // and it's container's children by -1 and position the children differently
    // (eg. Y position winSize.height*0.1f was changed to winSize.height*0.9f)
    // We can't just set the scroll view's Y scale to -1 because CCNode::getScale asserts that X and Y scale must be the same.
    scrollView->setScale(-1.f);
    tallContentA->setScale(-1.f);
    tallContentB->setScale(-1.f);
    */
    
    addChild(scrollView);
    

Also note that the two fixes mentioned above are mutually exclusive, do not apply both.

Answer now on the cocos2d-x forum too.

like image 171
PAT Avatar answered Dec 19 '22 23:12

PAT


@Zennichimaro,

@PAT's answer is correct and is essentially a simplified version

With that in mind, note that 3 things are important:

  1. CCScrollView::setViewSize() - you clearly missed out this one, it has to be the width and height of your viewable space (the blue box, I think it is your entire windowSize if you want it fullscreen)

  2. CCScrollView::setContentSize() - this is different from setContentOffset, looks like you've mistaken this to setContentOffset, anyway, this should be set to the size of the entire container (the red box, yours will probably be ccp(windowSize.width,2496.0f))

  3. CCScrollView::setContentOffset() - if you want to position it so that the topmost is visible initially, the minimum and maximum is specified by the small grey rectangle box in the answer (the grey box, yours will probably be ccp(0.0f, windowSize.height-2496.0f))

like image 29
Jenny Kim Avatar answered Dec 19 '22 23:12

Jenny Kim