RotateMenu说明
最终效果图
下面这个引用自乱斗西游
设计说明
1.菜单项(MenuItem)平均分布在椭圆(类似)上
2.椭圆长轴为2/3width,短轴为2/8 height
3.最前面的菜单项Scale=1,opacity=255,最后面Scale=0.5,opacity=129.其它位置根据三角函数变换(updatePosition中实现)
4.默认最前面菜单被选中(selected)
5.单位角度(unitAngle)是2*PI/菜单项的数量
6.滑动一个width,菜单旋转两个单位角度
7.Touch结束会自动调整位置,保证最前面位置有菜单项
8.滑动超过1/3单位角度会向前舍入
9.移动小于1/6单位角度会判定点击菜单
10.默认菜单大小不是全屏,而是屏幕的2/3,通过Node::setContentSize()设置
使用
使用这个菜单只要知道两个函数
1.构造函数
RotateMenu::create(CREATE_FUNC)
2.添加MenuItem
void addMenuItem(cocos2d::MenuItem *item);
其它函数可以看代码
相关参数的函数设置还未添加
代码
声明
#ifndef __ROTA__TE_MENU_H__ #define __ROTA__TE_MENU_H__ #include "cocos2d.h" /* *模仿乱斗西游主界面的旋转菜单 */ class RotateMenu :public cocos2d::Layer{ public: //构造方法 CREATE_FUNC(RotateMenu); //添加菜单项 void addMenuItem(cocos2d::MenuItem *item); //更新位置 void updatePosition(); //更新位置,有动画 void updatePositionWithAnimation(); //位置矫正 修改角度 forward为移动方向 当超过1/3,进1 //true 为正向 false 负 void rectify(bool forward); //初始化 virtual bool init(); //重置 操作有旋转角度设为0 void reset(); private: //设置角度 弧度 void setAngle(float angle); float getAngle(); //设置单位角度 弧度 void setUnitAngle(float angle); float getUnitAngle(); //滑动距离转换角度,转换策略为 移动半个Menu.width等于_unitAngle float disToAngle(float dis); //返回被选中的item cocos2d::MenuItem * getCurrentItem(); private: //菜单已经旋转角度 弧度 float _angle; //菜单项集合,_children顺序会变化,新建数组保存顺序 cocos2d::Vector<cocos2d::MenuItem *> _items; //单位角度 弧度 float _unitAngle; //监听函数 virtual bool onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event); virtual void onTouchEnded(cocos2d::Touch* touch, cocos2d::Event* event); virtual void onTouchMoved(cocos2d::Touch* touch, cocos2d::Event* event); //动画完结调用函数 void actionEndCallBack(float dx); //当前被选择的item cocos2d::MenuItem *_selectedItem; //动画运行时间 float animationDuration = 0.3f; }; #endif
实现
#include "RotateMenu.h" #include <math.h> #define PI acos(-1) USING_NS_CC; bool RotateMenu::init(){ if (!Layer::init()) return false; _angle = 0.0; this->ignoreAnchorPointForPosition(false); _selectedItem = nullptr; Size s = Director::getInstance()->getWinSize(); this->setContentSize(s/3*2); this->setAnchorPoint(Vec2(0.5f, 0.5f)); auto listener = EventListenerTouchOneByOne::create(); listener->onTouchBegan = CC_CALLBACK_2(RotateMenu::onTouchBegan,this); listener->onTouchMoved = CC_CALLBACK_2(RotateMenu::onTouchMoved, this); listener->onTouchEnded = CC_CALLBACK_2(RotateMenu::onTouchEnded, this); getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this); return true; }; void RotateMenu::addMenuItem(cocos2d::MenuItem *item){ item->setPosition(this->getContentSize() / 2); this->addChild(item); _items.pushBack(item); setUnitAngle(2 * PI / _items.size()); reset(); updatePositionWithAnimation(); return; } void RotateMenu::updatePosition(){ auto menuSize = getContentSize(); auto disY = menuSize.height / 8; auto disX = menuSize.width / 3; for (int i = 0; i < _items.size(); i++){ float x = menuSize.width / 2 + disX*sin(i*_unitAngle+getAngle()); float y = menuSize.height / 2 - disY*cos(i*_unitAngle + getAngle()); _items.at(i)->setPosition(Vec2(x, y)); _items.at(i)->setZOrder(-(int)y); //Opacity 129~255 _items.at(i)->setOpacity(192 + 63 * cos(i*_unitAngle + getAngle())); _items.at(i)->setScale(0.75 + 0.25*cos(i*_unitAngle + getAngle())); } return; } void RotateMenu::updatePositionWithAnimation(){ //先停止所有可能存在的动作 for (int i = 0; i < _items.size(); i++) _items.at(i)->stopAllActions(); auto menuSize = getContentSize(); auto disY = menuSize.height / 8; auto disX = menuSize.width / 3; for (int i = 0; i < _items.size(); i++){ float x = menuSize.width / 2 + disX*sin(i*_unitAngle + getAngle()); float y = menuSize.height / 2 - disY*cos(i*_unitAngle + getAngle()); auto moveTo = MoveTo::create(animationDuration, Vec2(x, y)); _items.at(i)->runAction(moveTo); //Opacity 129~255 auto fadeTo = FadeTo::create(animationDuration, (192 + 63 * cos(i*_unitAngle + getAngle()))); _items.at(i)->runAction(fadeTo); //缩放比例 0.5~1 auto scaleTo = ScaleTo::create(animationDuration, 0.75 + 0.25*cos(i*_unitAngle + getAngle())); _items.at(i)->runAction(scaleTo); _items.at(i)->setZOrder(-(int)y); } scheduleOnce(schedule_selector(RotateMenu::actionEndCallBack), animationDuration); return; } void RotateMenu::reset(){ _angle = 0; } void RotateMenu::setAngle(float angle){ this->_angle = angle; } float RotateMenu::getAngle(){ return _angle; } void RotateMenu::setUnitAngle(float angle){ _unitAngle = angle; } float RotateMenu::getUnitAngle(){ return _unitAngle; } float RotateMenu::disToAngle(float dis){ float width = this->getContentSize().width / 2; return dis / width*getUnitAngle(); } MenuItem * RotateMenu::getCurrentItem(){ if (_items.size() == 0) return nullptr; //这里实际加上了0.1getAngle(),用来防止精度丢失 int index = (int)((2 * PI - getAngle()) / getUnitAngle()+0.1*getUnitAngle()); index %= _items.size(); return _items.at(index); } bool RotateMenu::onTouchBegan(Touch* touch, Event* event){ //先停止所有可能存在的动作 for (int i = 0; i < _items.size(); i++) _items.at(i)->stopAllActions(); if (_selectedItem) _selectedItem->unselected(); auto position = this->convertToNodeSpace(touch->getLocation()); auto size = this->getContentSize(); auto rect = Rect(0, 0, size.width, size.height); if (rect.containsPoint(position)){ return true; } return false; } void RotateMenu::onTouchEnded(Touch* touch, Event* event){ auto xDelta = touch->getLocation().x - touch->getStartLocation().x; rectify(xDelta>0); if (disToAngle(fabs(xDelta))<getUnitAngle() / 6&&_selectedItem) _selectedItem->activate(); updatePositionWithAnimation(); return; } void RotateMenu::onTouchMoved(Touch* touch, Event* event){ auto angle = disToAngle(touch->getDelta().x); setAngle(getAngle() + angle); updatePosition(); return; } void RotateMenu::rectify(bool forward){ auto angle = getAngle(); while (angle<0) angle += PI * 2; while (angle>PI * 2) angle -= PI * 2; if(forward>0) angle = ((int)((angle + getUnitAngle() / 3*2) / getUnitAngle()))*getUnitAngle(); else angle = ((int)((angle + getUnitAngle() / 3 ) / getUnitAngle()))*getUnitAngle(); setAngle(angle); } void RotateMenu::actionEndCallBack(float dx){ _selectedItem = getCurrentItem(); if(_selectedItem) _selectedItem->selected(); }
一个糟糕的Demo
声明
#ifndef __HELLOWORLD_SCENE_H__ #define __HELLOWORLD_SCENE_H__ #include "cocos2d.h" class HelloWorld : public cocos2d::Layer { public: // there's no 'id' in cpp, so we recommend returning the class instance pointer static cocos2d::Scene* createScene(); // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone virtual bool init(); // a selector callback void menuCloseCallback(cocos2d::Ref* pSender); void menuItem1Callback(cocos2d::Ref* pSender); void menuItem2Callback(cocos2d::Ref* pSender); void menuItem3Callback(cocos2d::Ref* pSender); void menuItem4Callback(cocos2d::Ref* pSender); void menuItem5Callback(cocos2d::Ref* pSender); void hideAllSprite(); cocos2d::Sprite *sprite[5]; // implement the "static create()" method manually CREATE_FUNC(HelloWorld); }; #endif // __HELLOWORLD_SCENE_H__
声明
#include "HelloWorldScene.h" #include "RotateMenu.h" USING_NS_CC; typedef struct SceneList{ const char *name; std::function<cocos2d::Scene*()> callback; }SceneList; SceneList sceneList[] = { { "Demo1", [](){return HelloWorld::createScene(); } } }; const unsigned int sceneCount = sizeof(sceneList) / sizeof(SceneList); #define LINE_SPACE 40 Scene* HelloWorld::createScene() { // 'scene' is an autorelease object auto scene = Scene::create(); // 'layer' is an autorelease object auto layer = HelloWorld::create(); // add layer as a child to scene scene->addChild(layer); // return the scene return scene; } // on "init" you need to initialize your instance bool HelloWorld::init() { ////////////////////////////// // 1. super init first if ( !Layer::init() ) { return false; } Size visibleSize = Director::getInstance()->getVisibleSize(); Vec2 origin = Director::getInstance()->getVisibleOrigin(); ///////////////////////////// // 2. add a menu item with "X" image, which is clicked to quit the program // you may modify it. // add a "close" icon to exit the progress. it's an autorelease object 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)); // create menu, it's an autorelease object /* auto menu = Menu::create(closeItem, NULL); menu->setPosition(Vec2::ZERO); this->addChild(menu, 1);*/ auto item1 = MenuItemImage::create("Demo1/item1_1.png", "Demo1/item1_0.png", CC_CALLBACK_1(HelloWorld::menuItem1Callback, this)); auto item2 = MenuItemImage::create("Demo1/item2_1.png", "Demo1/item2_0.png", CC_CALLBACK_1(HelloWorld::menuItem2Callback, this)); auto item3 = MenuItemImage::create("Demo1/item3_1.png", "Demo1/item3_0.png", CC_CALLBACK_1(HelloWorld::menuItem3Callback, this)); auto item4 = MenuItemImage::create("Demo1/item4_1.png", "Demo1/item4_0.png", CC_CALLBACK_1(HelloWorld::menuItem4Callback, this)); auto item5 = MenuItemImage::create("Demo1/item5_1.png", "Demo1/item5_0.png", CC_CALLBACK_1(HelloWorld::menuItem5Callback, this)); RotateMenu *menu = RotateMenu::create(); menu->addMenuItem(item1); menu->addMenuItem(item2); menu->addMenuItem(item3); menu->addMenuItem(item4); menu->addMenuItem(item5); menu->setPosition(visibleSize/2); this->addChild(menu, 2); for (int i = 0; i < 5; i++){ char str[20]; sprintf(str, "Demo1/item%d.jpg", i + 1); sprite[i] = Sprite::create(str); sprite[i]->setAnchorPoint(Vec2(0.5f, 0.5f)); sprite[i]->setPosition(visibleSize / 2); this->addChild(sprite[i]); } hideAllSprite(); ///////////////////////////// // 3. add your codes below... // add a label shows "Hello World" // create and initialize a label auto label = LabelTTF::create("Hello World", "Arial", 24); // position the label on the center of the screen label->setPosition(Vec2(origin.x + visibleSize.width/2, origin.y + visibleSize.height - label->getContentSize().height)); // add the label as a child to this layer this->addChild(label, 1); return true; } void HelloWorld::menuCloseCallback(Ref* pSender) { #if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert"); return; #endif Director::getInstance()->end(); #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) exit(0); #endif } void HelloWorld::menuItem1Callback(cocos2d::Ref* pSender){ hideAllSprite(); sprite[0]->setVisible(true); } void HelloWorld::menuItem2Callback(cocos2d::Ref* pSender){ hideAllSprite(); sprite[1]->setVisible(true); } void HelloWorld::menuItem3Callback(cocos2d::Ref* pSender){ hideAllSprite(); sprite[2]->setVisible(true); } void HelloWorld::menuItem4Callback(cocos2d::Ref* pSender){ hideAllSprite(); sprite[3]->setVisible(true); } void HelloWorld::menuItem5Callback(cocos2d::Ref* pSender){ hideAllSprite(); sprite[4]->setVisible(true); } void HelloWorld::hideAllSprite(){ for (auto p : sprite){ if (p->isVisible()) p->setVisible(false); } }
作者:CCY
联系方式:[email protected]
时间: 2024-10-22 15:25:48