基于cocos2dx的伪立体菜单

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

基于cocos2dx的伪立体菜单的相关文章

cocos2dx基础篇(7)——菜单按钮CCMenu、CCMenuItem

[本节内容] 菜单按钮在游戏中是经常被用到的,比如主菜单界面的菜单选项,暂停游戏时的菜单选项等等.cocos2dx引擎同样为我们提供了CCMenu菜单的功能,并包含了一些简单的菜单项CCMenuItem.且菜单项附带触碰按钮时,自动放大的效果. 温馨提示:本节内容比较多,需要大家慢慢分析,不要急于求成. 本节组织结构如下: 一.介绍CCMenu. 二.介绍CCMenuItem,及其具体的六个子类. 三.代码实战. [一.菜单CCMenu] 菜单CCMenu是专门用来承载菜单按钮的CCLayer图

基于cocos2d-x的跑酷游戏项目教程

Cocos2d-x跑酷游戏项目教程 Cocos2d-x跑酷游戏项目教程 cocos2d-x特性 cocos2d-x采用MVC三层架构 流程控制( flow control ):方便管理不同的场景之间的流程 精灵(Sprite) 方便快速的显示控制一切可见的元素 节点(Node) 采用树状的继承关系,方便管理不同层次的游戏元素 动作( Action) 应用于节点的各种动画效果,动作元素 特效,平面地图,菜单,用户输入,文档,MIT许可,基于OpenGL的深度优化 简单跑酷游戏项目内容: 1.模型,

[伪] 级联菜单,两级菜单

[伪] 级联菜单,两级菜单 这段时间需要做一个类似效果的一个菜单样式,所以就查了各种文档,各种百度各种搜索,的确也是搜到了不少的第三方组件,比如(CascadingMenuViewLib)就是其中一个,但是,我脑子笨,研究了3天左右的时间,改不成我需要得样子,So...我用我自己的方式,实现了这个效果(对了,还没有完全实现,因为TextView被点中的样式我还没有设置,不过这都是小事情了..) 在这里,我提供一个思路,代码很简单,当然,我也会附一些上来,好的,我首先说一下思路 首先,我们的xml

基于Cocos2dx+Lua v3.x的RichLabel

RichLabel 简介 RichLabel基于Cocos2dx+Lua v3.x解析字符串方面使用了labelparser,它可以将一定格式的字符串,转换为lua中的表结构扩展标签极其简单,只需添加一个遵守规则的标签插件即可,无需改动已存在代码!!! (标签插件都在labels文件夹下) labelparser的详解labelparser在github上的源码RichLabel在github上的源码 支持图片(缩放,旋转,是否可见) 支持文本属性(字体,大小,颜色,阴影,描边,发光) 支持标签

【课程下载】基于Cocos2d-x游戏引擎实战开发炸弹超人

我这里有套课程想和大家分享,需要的朋友可以加我qq和我联系.QQ2059055336. 课程讲师:Jason.Z 课程分类:ios适合人群:初级课时数量:31课时更新程度:完毕 一.本课程是怎么样的一门课程(全面介绍) 1.1.课程的背景 Cocos2d-x 是一个支持多平台的 2D 手机游戏引擎,使用 C++ 开发,基于OpenGL ES,基于Cocos2d-iphone,支持 WOPhone, iOS 4.1, Android 2.1 及更高版本, WindowsXP & Windows7,

基于Cocos2dx + box2d 实现的愤慨的小鸟Demo

1. Demo初始界面 2. 游戏界面 3. 精确碰撞检測 4. 下载  压缩文件文件夹 AngryBird source    愤慨的小鸟Demo源码,基于Cocos2dx C++,以及box2d技术. run 可运行程序文件夹 点击打开链接

7.3 立体菜单

在本案例中,将实现如图1所示的立体菜单效果.当鼠标指针经过菜单项时,菜单按钮将显示出被按下的样式. 该实例文件位于网页学习网 CSS教程资源中的“第7章\03\3d-navi.htm”. 图1 立体菜单效果 一.基本设置 本案例仍然使用和上一个案例相同的HTML结构,基本代码HTML如下. XML/HTML 代码复制内容到剪贴板 <body> <div id="menu"> <a href="#"> Home</a>

基于Cocos2dx开发卡牌游戏Demo_放开那三国 2.0

PS:下载地址在最下面 1.登录 2.副本选择 3.地图 4. 选择敌人 5. 战斗 6. 战斗结算 7. 地图拓展 8. 武将拓展 9. 下载地址 主程序(包含资源文件):点击打开链接 源代码:正在上传... 基于Cocos2dx开发卡牌游戏Demo_放开那三国 2.0,布布扣,bubuko.com

基于cocos2dx的2D手游美术资源制作技术选型(2)--动作编辑器选择

Cocostudio是cocos2dx官方提供的游戏制作工具,其中包含了动作编辑器,其编辑好的动画导出的文件有三种:Atlastexture,json的AtalasTexture描述文件和plist动画描述文件,可以使用LibExtension Amature加载和播放. 但Cocostduio的动作编辑器也有一些不足的地方: 1. Cocostduio的崩溃率较高,很容易造成编辑过程中的数据丢失 2. Cocostudio的动作编辑方式对美术来说学习成本大,使用上也多不方便,美术对此非常抗拒