时下最热门的英雄联盟,用cocos2d-x尝试下它的皮肤选择菜单,也算是初学者练手了。

最终效果图


20150224125502179.jpg

英雄联盟皮肤选择


20150228161758746.jpg

设计说明

实现目标所需要的动作

移动(MoveTo),伸缩(ScaleTo),倾斜(OrbitCamera)

实现目标所需要函数(这是一个数学函数)

x/(x+a)

其中a为常量,用来计算上面三个动作的值

大小

与原版Menu不同,大小不是全屏的,默认是屏幕的(2/3),可以通过setContentSize()函数设置

_index变量

将所有的菜单项平铺构成一个长方形,_index表示目前在中间位置的点,如下图

显示方式

将菜单项距中心的距离(i-_indxe)作为函数变量x,具体内容查看LOLMenu::updatePosition();

操作说明

滑动四分之一菜单宽的距离为一个单位的_index,距离大于0.6小于1.0的部分进1

使用

使用这个菜单只要知道两个函数:

1.构造函数:LOLMenu::create()(由CREATE_FUNC创建)

2.添加MenuItem:void addMenuItem(cocos2d::MenuItem *item);

其它函数可以看代码

菜单代码

LOLMenu.h

#ifndef __LOL__TE_MENU_H__

#define __LOL__TE_MENU_H__

#include "cocos2d.h"

/*

*模仿英雄联盟的皮肤选择菜单

*不同点在于,英雄联盟当皮肤过多时,部分皮肤会移出边缘,不显示

*我会显示所以菜单项,向边缘移动会不断减缓

*/

class LOLMenu :public cocos2d::Layer{

public:

//构造方法

CREATE_FUNC(LOLMenu);

//添加菜单项

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 setIndex(int index);

//设置当前显示菜单项的索引号

float getIndex();

//返回被选中的item

cocos2d::MenuItem * getCurrentItem();

//数学计算式width*index/(abs(index)+CALC_A) ,其中CALC_A为常数

float calcFunction(float index, float width);

private:

//菜单菜单项的索引号

float _index;

//上一次菜单项的索引号

float _lastIndex;

//菜单项集合,_children顺序会变化,新建数组保存顺序

cocos2d::Vector _items;

//监听函数

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;

};

#endif

LOLMenu.cpp

#include "LOLMenu.h"

#include

#define PI acos(-1)

//菜单的缩小比例 最小的比例是1-MENU_SCALE

#define MENU_SCALE 0.3

//菜单的倾斜度 最多为45度

#define MENU_ASLOPE 60.0

//calcFunction(x) 为 x/(x+a),其中a为常数

#define CALC_A 1

//动画运行时间

#define ANIMATION_DURATION 0.3f

//菜单项的大小与屏幕的比例,当然可以通过setContentSize设置

#define CONTENT_SIZE_SCALE (2.0/3)

//菜单项长度与菜单长度的比例 滑动一个菜单项长度,菜单项变化一个

#define ITEM_SIZE_SCALE (1.0/4)

/*

代码里面还有可以设置的参数,这里没有一一例出或给出函数

*/

USING_NS_CC;

bool LOLMenu::init(){

if (!Layer::init())

return false;

_index=0;

_lastIndex = 0;

this->ignoreAnchorPointForPosition(false);

_selectedItem = nullptr;

auto size = Director::getInstance()->getWinSize();

this->setContentSize(size*CONTENT_SIZE_SCALE);

this->setAnchorPoint(Vec2(0.5f, 0.5f));

auto listener = EventListenerTouchOneByOne::create();

listener->onTouchBegan = CC_CALLBACK_2(LOLMenu::onTouchBegan, this);

listener->onTouchMoved = CC_CALLBACK_2(LOLMenu::onTouchMoved, this);

listener->onTouchEnded = CC_CALLBACK_2(LOLMenu::onTouchEnded, this);

getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this);

return true;

};

void LOLMenu::addMenuItem(cocos2d::MenuItem *item){

item->setPosition(this->getContentSize() / 2);

this->addChild(item);

_items.pushBack(item);

reset();

//如果希望开始没有移动效果,改成updatePosition函数即可

updatePositionWithAnimation();

return;

}

void LOLMenu::updatePosition(){

auto menuSize = getContentSize();

for (int i = 0; i < _items.size(); i++){

//设置位置

float x = calcFunction(i - _index, menuSize.width / 2);

_items.at(i)->setPosition(Vec2(menuSize.width/2+x, menuSize.height/2));

//设置zOrder,即绘制顺序

_items.at(i)->setZOrder(-abs((i - _index) * 100));

//设置伸缩比例

_items.at(i)->setScale(1.0-abs(calcFunction(i - _index, MENU_SCALE)));

//设置倾斜,Node没有setCamera函数,将OrbitCamera的运行时间设为0来达到效果

auto orbit1 = OrbitCamera::create(0, 1, 0, calcFunction(i - _lastIndex, MENU_ASLOPE), calcFunction(i - _lastIndex, MENU_ASLOPE) - calcFunction(i - _index, MENU_ASLOPE), 0, 0);

_items.at(i)->runAction(orbit1);

}

return;

}

void LOLMenu::updatePositionWithAnimation(){

//先停止所有可能存在的动作

for (int i = 0; i < _items.size(); i++)

_items.at(i)->stopAllActions();

auto menuSize = getContentSize();

for (int i = 0; i < _items.size(); i++){

_items.at(i)->setZOrder(-abs((i - _index)*100));

float x = calcFunction(i - _index, menuSize.width / 2);

auto moveTo = MoveTo::create(ANIMATION_DURATION, Vec2(menuSize.width / 2 + x, menuSize.height / 2));

_items.at(i)->runAction(moveTo);

auto scaleTo = ScaleTo::create(ANIMATION_DURATION, (1 - abs(calcFunction(i - _index, MENU_SCALE))));

_items.at(i)->runAction(scaleTo);

auto orbit1 = OrbitCamera::create(ANIMATION_DURATION, 1, 0, calcFunction(i - _lastIndex, MENU_ASLOPE), calcFunction(i - _index, MENU_ASLOPE) - calcFunction(i - _lastIndex, MENU_ASLOPE), 0, 0);

_items.at(i)->runAction(orbit1);

}

scheduleOnce(schedule_selector(LOLMenu::actionEndCallBack), ANIMATION_DURATION);

return;

}

void LOLMenu::reset(){

_lastIndex = 0;

_index = 0;

}

void LOLMenu::setIndex(int index){

_lastIndex = _index;

this->_index = index;

}

float LOLMenu::getIndex(){

return _index;

}

MenuItem * LOLMenu::getCurrentItem(){

if (_items.size() == 0)

return nullptr;

return _items.at(_index);

}

bool LOLMenu::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 LOLMenu::onTouchEnded(Touch* touch, Event* event){

auto size = getContentSize();

auto xDelta = touch->getLocation().x - touch->getStartLocation().x;

rectify(xDelta>0);

if (abs(xDelta)

_selectedItem->activate();

updatePositionWithAnimation();

return;

}

void LOLMenu::onTouchMoved(Touch* touch, Event* event){

auto xDelta = touch->getDelta().x;

auto size = getContentSize();

_lastIndex = _index;

_index -= xDelta / (size.width *ITEM_SIZE_SCALE);

updatePosition();

return;

}

void LOLMenu::rectify(bool forward){

auto index = getIndex();

if (index < 0)

index = 0;

if (index>_items.size() - 1)

index = _items.size() - 1;

if (forward){

index = (int)(index + 0.4);

}

else

index = (int)(index + 0.6);

setIndex((int)index);

}

void LOLMenu::actionEndCallBack(float dx){

_selectedItem = getCurrentItem();

if (_selectedItem)

_selectedItem->selected();

}

float LOLMenu::calcFunction(float index, float width){

return width*index / (abs(index) + CALC_A);

}

演示代码

LOLMenuDemo.h

#ifndef __LOLMenu_SCENE_H__

#define __LOLMenu_SCENE_H__

#include "cocos2d.h"

class LOLMenuDemo : 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(LOLMenuDemo);

};

#endif // __HELLOWORLD_SCENE_H__

LOLMenuDemo.cpp

#include "LOLMenuDemo.h"

#include "LOLMenu.h"

USING_NS_CC;

Scene* LOLMenuDemo::createScene()

{

// 'scene' is an autorelease object

auto scene = Scene::create();

// 'layer' is an autorelease object

auto layer = LOLMenuDemo::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 LOLMenuDemo::init()

{

//////////////////////////////

// 1. super init first

if (!Layer::init())

{

return false;

}

Size visibleSize = Director::getInstance()->getVisibleSize();

Vec2 origin = Director::getInstance()->getVisibleOrigin();

auto item1 = MenuItemImage::create("4_LOL_MENU/item1_0.png", "4_LOL_MENU/item1_0.png", CC_CALLBACK_1(LOLMenuDemo::menuItem1Callback, this));

auto item2 = MenuItemImage::create("4_LOL_MENU/item2_0.png", "4_LOL_MENU/item2_0.png", CC_CALLBACK_1(LOLMenuDemo::menuItem2Callback, this));

auto item3 = MenuItemImage::create("4_LOL_MENU/item3_0.png", "4_LOL_MENU/item3_0.png", CC_CALLBACK_1(LOLMenuDemo::menuItem3Callback, this));

auto item4 = MenuItemImage::create("4_LOL_MENU/item4_0.png", "4_LOL_MENU/item4_0.png", CC_CALLBACK_1(LOLMenuDemo::menuItem4Callback, this));

auto item5 = MenuItemImage::create("4_LOL_MENU/item5_0.png", "4_LOL_MENU/item5_0.png", CC_CALLBACK_1(LOLMenuDemo::menuItem5Callback, this));

LOLMenu *menu = LOLMenu::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[100];

sprintf(str, "4_LOL_MENU/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();

return true;

}

void LOLMenuDemo::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 LOLMenuDemo::menuItem1Callback(cocos2d::Ref* pSender){

hideAllSprite();

sprite[0]->setVisible(true);

}

void LOLMenuDemo::menuItem2Callback(cocos2d::Ref* pSender){

hideAllSprite();

sprite[1]->setVisible(true);

}

void LOLMenuDemo::menuItem3Callback(cocos2d::Ref* pSender){

hideAllSprite();

sprite[2]->setVisible(true);

}

void LOLMenuDemo::menuItem4Callback(cocos2d::Ref* pSender){

hideAllSprite();

sprite[3]->setVisible(true);

}

void LOLMenuDemo::menuItem5Callback(cocos2d::Ref* pSender){

hideAllSprite();

sprite[4]->setVisible(true);

}

void LOLMenuDemo::hideAllSprite(){

for (auto p : sprite){

if (p->isVisible())

p->setVisible(false);

}

}