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<: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
#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 setPosition(Vec2(x, y));
_items.at(i)->setZOrder(-(int)y);
//Opacity129~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 stopAllActions();
auto menuSize = getContentSize();
auto disY = menuSize.height / 8;
auto disX = menuSize.width / 3;
for (int i = 0; i runAction(moveTo);
//Opacity129~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(),用来防止精度丢失
intindex = (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 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))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 (anglePI * 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<: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 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
联系方式:810278677@qq.com