基于Cocos2d-x的英雄联盟皮肤选择菜单

 jvxiang   2015-08-05 14:00   359 人阅读  0 条评论

最终效果图

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<cocos2d::MenuItem *> _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 <math.h>  
#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)<size.width / 24 && _selectedItem)  
        _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);  
    }  
}

 

点击下载:可运行的程序(需要安装vs2013或相关dll文件)

 

PS.这种类型的菜单在网页里会常见一些,比如优酷的动漫专题或者App Store都可以看到类似的影子,由于是平面的,菜单项的切换不是很自然,我通过倾斜来变得自然。

本文地址:https://www.jvxiang.com/基于cocos2d-x的英雄联盟皮肤选择菜单.html
版权声明:本文为原创文章,版权归 jvxiang 所有,欢迎分享本文,转载请保留出处!
NEXT:已经是最新一篇了

 发表评论


表情