如何制作一个可以运行在iOS游戏上的滑动菜单?

我想制作一个滑动菜单,其中,在每屏的顶层菜单(level menu)中,设置40个精灵,分别用level 1和level 2进行标记。在右下角,设置另一个“箭头”精灵,在我点击它的时候,场景可以滑到下一屏,然后显示出levels 41到80的内容。这种操作该如何实现呢?
注:我用的是Xcode,而且只想求得,在C++语言环境下编写的cocos2d-x的解决方法。

原问题:How to make a sliding menu in cocos2dx in c++ for IOS game

阅读 3.8k
1 个回答

FuzzyBunnySlippers 的回答:
我尝试过这种方法,感觉还可以。首先,创建一个CCScene子类别。植入全部的menu item,包括可控制所有页面的“箭头”。你需要摆放的所有物品,第一组要放置在在屏幕第一页的可见部分,下一组是向右偏移100%,第三组是向右偏移200%,等等。
设定屏幕上的控制按钮,让它可以启动一个action,并以100%的幅度控制layer的左右移动。
上述所有内容都要关联到一个单独的“Menu”中,当然,你也可以将menu置入一个layer中。
对于以上的阐述,我可以提供一个menu的样例,具体内容如下:
MainScene.h

#ifndef __MainScene__
#define __MainScene__

#include "cocos2d.h"

using namespace cocos2d;


class MainScene : public CCScene
{
private:
   // This class follows the "create"/"autorelease" pattern.
   // Private constructor.
   MainScene();
   CCMenu* _menu;
   bool _sliding;
   void MenuCallback(CCObject* sender);
   void PageLeft();
   void PageRight();
   void SlidingDone();

protected:
   // This is protected so that derived classes can call it
   // in their create methods.
   bool init();

private:
   void CreateMenu();

public:

   static MainScene* create();

   ~MainScene();

   virtual void onEnter();
   virtual void onExit();
   virtual void onEnterTransitionDidFinish();
   virtual void onExitTransitionDidStart();

};


#endif /* defined(__MainScene__) */

MainScene.cpp

#include "MainScene.h"

#define ARROW_LEFT (-1)
#define ARROW_RIGHT (-2)

#define MENU_ITEMS_ACROSS 4
#define MENU_ITEMS_DOWN 5
#define MENU_ITEMS_PAGE (MENU_ITEMS_ACROSS*MENU_ITEMS_DOWN)
#define MENU_ITEMS_TOTAL 50

#define MENU_PAGES ((MENU_ITEMS_TOTAL/MENU_ITEMS_PAGE)+1)
#define MENU_FRACTION (ccp(0.8,0.8))
#define MENU_ANCHOR (ccp(0.5,0.5))

#define SLIDE_DURATION 1.0

MainScene::MainScene() :
   _menu(NULL)
   _sliding(false)
{
}

MainScene::~MainScene()
{
}

static CCPoint CalculatePosition(int itemNum)
{
   CCSize scrSize = CCDirector::sharedDirector()->getWinSize();
   float Xs = scrSize.width;
   float Ys = scrSize.height;
   int gRows = MENU_ITEMS_DOWN;
   int gCols = MENU_ITEMS_ACROSS;
   int gBins = gRows*gCols;
   float Xb = MENU_FRACTION.x*Xs/gCols;
   float Yb = MENU_FRACTION.y*Ys/gRows;
   float Xa = MENU_ANCHOR.x * Xs;
   float Ya = MENU_ANCHOR.y * Ys;
   int page = itemNum / gBins;

   int binCol = itemNum % gCols;
   int binRow = (itemNum-page*gBins) / gCols;

   float xPos = binCol * Xb + Xb/2 + Xa - MENU_FRACTION.x*Xs/2 + page * Xs;
   float yPos = Ya - binRow*Yb - Yb/2 + MENU_FRACTION.y * Ys/2;

   CCPoint pos = ccp(xPos,yPos);

   return pos;
}

void MainScene::CreateMenu()
{
   if(_menu == NULL)
   {
      CCSize scrSize = CCDirector::sharedDirector()->getWinSize();

      _menu = CCMenu::create();
      _menu->setPosition(ccp(0,0));
      addChild(_menu);


      CCMenuItemFont* pItem;
      CCPoint position;

      // Create the next/back menu items.
      for(int page = 0; page < MENU_PAGES; page++)
      {
         // Create the Back/Forward buttons for the page.
         // Back arrow if there is a previous page.
         if(page > 0)
         {
            pItem = CCMenuItemFont::create("Back", this, menu_selector(MainScene::MenuCallback));
            pItem->setTag(ARROW_LEFT);
            position = ccp(page*scrSize.width + scrSize.width*0.1,scrSize.height*0.1);
            pItem->setPosition(position);
            pItem->setFontSize(35);
            pItem->setFontName("Arial");
            _menu->addChild(pItem);
         }
         if(page < (MENU_PAGES-1))
         {
            pItem = CCMenuItemFont::create("Next", this, menu_selector(MainScene::MenuCallback));
            pItem->setTag(ARROW_RIGHT);
            position = ccp(page*scrSize.width + scrSize.width*0.9,scrSize.height*0.1);
            pItem->setPosition(position);
            pItem->setFontSize(35);
            pItem->setFontName("Arial");
            _menu->addChild(pItem);
         }
      }
      // Create the actual items
      for(int idx = 0; idx < MENU_ITEMS_TOTAL; idx++)
      {
         char buffer[256];
         sprintf(buffer,"Item #%d",idx);
         pItem = CCMenuItemFont::create(buffer, this, menu_selector(MainScene::MenuCallback));
         pItem->setFontSize(35);
         pItem->setFontName("Arial");
         pItem->setTag(idx);
         position = CalculatePosition(idx);
         pItem->setPosition(position);
         _menu->addChild(pItem);

      }
   }
}


bool MainScene::init()
{

   return true;
}

MainScene* MainScene::create()
{
   MainScene *pRet = new MainScene();
   if (pRet && pRet->init())
   {
      pRet->autorelease();
      return pRet;
   }
   else
   {
      CC_SAFE_DELETE(pRet);
      return NULL;
   }
}

void MainScene::onEnter()
{
   CCScene::onEnter();
   CreateMenu();
}

void MainScene::onExit()
{
   CCScene::onExit();
}

void MainScene::onEnterTransitionDidFinish()
{
   CCScene::onEnterTransitionDidFinish();
}

void MainScene::onExitTransitionDidStart()
{
   CCScene::onExitTransitionDidStart();

}

void MainScene::SlidingDone()
{
   _sliding = false;
}

void MainScene::PageLeft()
{
   if(_sliding)
      return;
   _sliding = true;
   CCSize scrSize = CCDirector::sharedDirector()->getWinSize();
   CCFiniteTimeAction* act1 = CCMoveBy::create(SLIDE_DURATION, ccp(scrSize.width,0));
   CCFiniteTimeAction* act2 = CCCallFunc::create(this, callfunc_selector(MainScene::SlidingDone));
   _menu->runAction(CCSequence::create(act1,act2,NULL));
}

void MainScene::PageRight()
{
   if(_sliding)
      return;
   _sliding = true;
   CCSize scrSize = CCDirector::sharedDirector()->getWinSize();
   CCFiniteTimeAction* act1 = CCMoveBy::create(SLIDE_DURATION, ccp(-scrSize.width,0));
   CCFiniteTimeAction* act2 = CCCallFunc::create(this, callfunc_selector(MainScene::SlidingDone));
   _menu->runAction(CCSequence::create(act1,act2,NULL));
}

void MainScene::MenuCallback(CCObject* sender)
{
   // This is a very contrived example
   // for handling the menu items.
   // -1 ==> Left Arrow
   // -2 ==> Right Arrow
   // Anything else is a selection
   CCMenuItem* pMenuItem = (CCMenuItem*)sender;
   switch(pMenuItem->getTag())
   {
      case ARROW_LEFT:
         PageLeft();
         break;
      case ARROW_RIGHT:
         PageRight();
         break;
      default:
         CCLOG("Got Item %d Pressed",pMenuItem->getTag());
         break;
   }
}

注:实现item覆盖多页的效果,它的算法可能会有些复杂。利用“Screen fraction”,或者"menu anchor"都可以获取页面上item的网格信息。
抓屏样图:
enter image description here
enter image description here

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进