4 votes

Cocos2d-x CCScrollview défile à l'envers

Bonjour Stackoverflow,

J'essaie actuellement d'implémenter un menu déroulant dans un jeu cocos2d-x que je suis en train de développer. Le problème est que les éléments défilent dans la direction opposée à celle que je souhaite. Par exemple, si je fais glisser mon doigt du bas de l'écran vers le haut, le sprite rebondira à sa position initiale.

Et si je fais glisser mon doigt du haut vers le bas, le sprite suivra mon doigt (jusqu'à ce qu'il atteigne la limite).

Voici mon 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 );

Toute suggestion ou aide est vivement appréciée. Si vous avez d'autres solutions pour créer un menu déroulant avec une autre approche, je serais ravi de les connaître.

Merci de votre attention !

Meilleures salutations Andreas

8voto

PAT Points 322

Se référer : cocos2dx/extensions/GUI/CCScrollView/CCScrollView.cpp

Plus précisément CCScrollView::minContainerOffset y CCScrollView::maxContainerOffset

CCScrollView travaille en Coordonnées OpenGL (par opposition à coordonnées de la fenêtre ) - les valeurs sont relatives à (gauche, bas), l'axe Y positif allant vers le haut. Il convient également de tenir compte du positionnement de la vue de défilement et de la fonction conteneur sont ancrés ( CCNode::setAnchorPoint ) à (gauche, bas).

Lorsque vous faites défiler le contenu vers le bas (déplacez/tirez le contenu vers le haut pour voir le contenu sous la coupure/le clip), vous voyez le contenu sous le bord inférieur de l'écran mais il rebondit dès que vous relâchez le toucher/tirer parce que maxContainerOffset retours (0, 0) et vous venez d'essayer de passer à une compensation de contenu positive.

Coordinates of CCScrollView and the container

Le diagramme montre l'état de la écran de défilement et le conteneur lorsqu'ils sont créés/initialisés. Il s'agit de l'état ou des coordonnées auxquels il faut "penser" lors de la mise en place et du positionnement des éléments enfants y conteneur . Le rectangle gris (à gauche, en bas) montre la zone valide pour le défilement de l'image. conteneur . Imaginez que le point d'ancrage du conteneur se déplace à l'intérieur de celui-ci.

Pour voir le conteneur défiler vers le haut au départ (ce à quoi on s'attend lorsqu'on travaille en coordonnées de la fenêtre ), réglez le décalage du contenu en conséquence (immédiatement après l'avoir configuré). Vous obtiendrez ainsi les résultats/comportements attendus.

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

Un exemple plus complet se trouve dans le code édité ci-dessous.

  1. Une "solution" pour permettre le défilement dans les coordonnées de la fenêtre (axe Y positif vers le bas) pourrait être d'ajuster l'extension, ce qui nécessiterait de reconstruire la bibliothèque cocos2dx et affecterait tous les projets (même les autres exemples de code que vous pouvez essayer).

    /*
    // (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 moins intrusif piratage est de mettre à l'échelle le ScrollView à -1 et l'instance enfants de la nœud de conteneur également à -1. Vous devez également repositionner le nœuds enfants en tenant compte de l'échelle inverse. Le résultat de la mise à l'échelle aux deux niveaux est le contenu ( nœuds enfants ) sont vus directement (et non à l'envers). Le résultat de la mise à l'échelle du ScrollView à -1 est que le défilement se déroule pour vous dans la direction attendue. Notez cependant que cette "correction" inversera également le défilement sur l'axe des X et qu'elle est donc sólo convient si vous souhaitez faire défiler verticalement ( 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);

Notez également que les deux correctifs mentionnés ci-dessus sont mutuellement exclusifs , faire no appliquer à la fois .

Réponse maintenant aussi sur le forum cocos2d-x.

3voto

Hakim Hauston Points 965

@Zennichimaro,

La réponse de @PAT est correcte et est essentiellement une version simplifiée

Dans cette optique, il convient de noter que trois éléments sont importants :

  1. CCScrollView::setViewSize() - vous avez clairement omis ce point, il doit s'agir de la largeur et de la hauteur de votre espace visible (la boîte bleue, je pense qu'il s'agit de la taille totale de votre fenêtre si vous voulez qu'elle soit en plein écran).

  2. CCScrollView::setContentSize() - ceci est différent de setContentOffset, il semble que vous ayez confondu ceci avec setContentOffset, de toute façon, ceci devrait être défini à la taille du conteneur entier (la boîte rouge, la vôtre sera probablement ccp(windowSize.width,2496.0f) )

  3. CCScrollView::setContentOffset() - si vous voulez le positionner de façon à ce que le plus haut soit visible au départ, le minimum et le maximum sont spécifiés par le petit rectangle gris dans la réponse (le rectangle gris, le vôtre, sera probablement ccp(0.0f, windowSize.height-2496.0f) )

3voto

mtet88 Points 325

Une réponse plus courte serait :

scrollView->setContentOffset(scrollView->minContainerOffset());

Le point d'ancrage étant situé dans le coin inférieur gauche, le décalage initial n'est pas nul, mais négatif.

0voto

Dainius Kreivys Points 327

Si quelqu'un veut faire un scrollview comme dans les tables de chat des jeux mmo (ajoute les messages en bas et fait défiler automatiquement jusqu'en bas), alors peut-être que quelqu'un trouvera cela utile :

partie du code de mon ChatTableView.h

//tableView dataSource is vector of strings
class ChatTableView :public Layer, public TableViewDataSource, public TableViewDelegate
{
public:
    vector<string> n_msglist;
    TableView* pTableView;
    void addMsg(string text);
private:

};

//part of the code of my ChatTableView.cpp
void Init(){
    pTableView = TableView::create(this, VisibleRect::getVisibleRect().size, NULL);
    pTableView->setDirection(ScrollView::Direction::VERTICAL);
    pTableView->setVerticalFillOrder(TableView::VerticalFillOrder::TOP_DOWN);
    pTableView->setDelegate(this);
    pTableView->setBounceable(false);

} 
void ChatTableView::addMsg(string text){

    n_msglist.push_back(text); //adds string to vector
    pTableView->reloadData(); //reloads data with newly updated vector and scroll back to top
    pTableView->setContentOffset(pTableView->maxContainerOffset()); //scrolls to bottom
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X