Menu的自定义实现-------保卫萝卜造塔升级塔菜单实现

来源:互联网 发布:动物百科全书软件 编辑:程序博客网 时间:2024/04/29 23:44

cocos2dx原生的menu排版函数实现的很无完整,像最基本的Item的排序要想做得稍微漂亮一些就需要我们自己实现。

对于Menu我们可以用两种方法来实现:

1.大神级别。 继承自Control,自己来封装新的Menu类,要求我们能够友好的去抽象定义基类。

2.半仙级别。修改MenuItem的函数或者重写一套对于Item的排序函数。做到自己想要的排版。

3.菜鸟级别。继承自Node,在Node中添加成员变量Menu,针对不同的UI设置Item的位置和动画。


这里解析一下第三种方法,第一种方法确实是最好的,做到基类重用,但是一些动态的效果跟3一样写,没有一个好的架构就还是老老实实用3吧。

保卫萝卜的菜单分为建塔菜单和升级塔的菜单。比较难的是建造塔的菜单,这个菜单的实现有几个技术难点:

1.根据当前关卡的塔来创建item

2.塔如果超过4种要分两行

3.Menu要根据屏幕位置动态适应position

4.show和hide菜单的时候要实现动画,Item都是放大和缩小后才隐藏的。

(进入关卡前资源一定要实现动态异步加载,只加载本关所需要的素材,还有各种文件的异步加载,具体的参考我的上一篇博客)

先看下效果图吧:



这篇blog我不会贴代码了,只讲解一下框架逻辑。

首先定义一个类继承自MenuItem,简单封装一下Menuitem,让我们后面可以判断点击到的item是哪一种塔,因为我们要展示item是否为可点击状态。 如果金币不够的话是不允许建造塔的。

然后创建一个Node。

createMenuItem的时候是根据当前关卡的配置信息来创建的,创建所有Item到成员变量 cocos2d::Vector<cocos2d::MenuItem*> menuItems;中,以便我们后期执行Item动画。

 如果超过4种塔,我们就再添加一个Menu,在Node中添加两个Menu来控制排版。

定义成员函数void updateItemsPosition(int row, int col),根据当前点击砖块的位置,在Node中动态设置Menu的位置。


最后就是第四步了,实现show和hide动画。

这样就很简单了,只需动画执行完隐藏就好,如下:

void *****::hide(){if (!m_pRangeSprite->isVisible()){return;}ScaleTo *scaleTo = ScaleTo::create(CW_UPTOWERMENU_ANIM_DURATION, 0.0f);auto callc = CallFunc::create(CC_CALLBACK_0(UpTowerMenu::hideMe, this));m_pRangeSprite->runAction(CCSequence::create(scaleTo, callc, NULL));}

到这里就可以做出完全一样的菜单了,这是一种比较简单的方法,类似的升级菜单也是如此。


吐槽一下:

今天TX电话面试了,面试的时候各种抓瞎。

问我多线程渲染啊,函数式编程啊,还有怎么省电。。

当时被问的我就说不会了,没看过源码,不理解底层。。。

下来一看神马多线程渲染,就是异步加载,底层一个队列来异步加载资源的,源码也看过了。。。我以为是opengl绘图性能多线程。。屏幕分块渲染呢,完蛋了。

还有函数式编程,我说不会没听过。。。尼玛原来是lua闭包,lambda函数,IOSblock。。。。我竟然说不会!!!

省电这个就确实不太理解了,可能也是概念上的问题吧,不知道咋整了,这一面感觉好蛋疼。。。求过了!!!!




补:官网首页推荐了,我就贴一下源码吧!

BuyTowerMenu.h

/*************************************************Copyright:bro7Author:benDate:2014-07-30Description:保卫萝卜建造塔菜单动画的实现PS:锚点也会变小,非0-1,是按照比例来变化的,注意坑。**************************************************/#ifndef __BUYTOWERMENU_H__#define __BUYTOWERMENU_H__#include "cocos2d.h"#include "BuyTowerMenuItem.h"class BuyTowerMenu :public cocos2d::Node{public:BuyTowerMenu();~BuyTowerMenu();bool init();CREATE_FUNC(BuyTowerMenu);void hide();void show();cocos2d::Vector<cocos2d::MenuItem*> menuItems;private:void menuCallback(Ref* sender);void createMenu();void updateMenu();};#endif

BuyTowerMenu.cpp

#include "BuyTowerMenu.h"#include "GameScene.h"#include "GlobalData.h"USING_NS_CC;const float gTileWidth = 80.0f;BuyTowerMenu::BuyTowerMenu(){}BuyTowerMenu::~BuyTowerMenu(){}bool BuyTowerMenu::init(){if (!Node::init()){return false;}//根据当前关卡的tower信息来创建menucreateMenu();return true;}void BuyTowerMenu::createMenu(){cocos2d::ValueVector towerTypes = GlobalData::getInstance()->getTowerTypes();float ratio = GlobalData::getInstance()->getRatio();for (auto tower : towerTypes){int buildGold = GlobalData::getInstance()->getTowerConfig().at(tower.asString()).asValueMap().at("price").asInt();auto towerNormalFile = GlobalData::getInstance()->getTowerConfig().at(tower.asString()).asValueMap().at("buy").asString();auto towerDisabledFile = GlobalData::getInstance()->getTowerConfig().at(tower.asString()).asValueMap().at("buy").asString();auto spNormal = Sprite::createWithSpriteFrameName(towerNormalFile);auto spDisable = Sprite::createWithSpriteFrameName(towerNormalFile);auto item = BuyTowerMenuItem::create( spNormal, spNormal, spDisable, CC_CALLBACK_1(BuyTowerMenu::menuCallback, this));item->setEnabledGold(buildGold);item->setTowerName(tower.asString());item->setPosition(0, 0);menuItems.pushBack(item);}//alignItemsInColumns 搞不定padding的问题 所以弃用  改为使用两个menu合成一个menu//用法enum->alignItemsInColumns(2, menuItems.size() - 2);const int num = 4;if (menuItems.size() <= num){auto menu = Menu::createWithArray(menuItems);menu->setScale(0.5f);menu->setPosition(0, 0);menu->setAnchorPoint(Point(0, 0));menu->alignItemsHorizontallyWithPadding(0);this->addChild(menu);this->setContentSize(Size(gTileWidth * ratio*menuItems.size(), gTileWidth*ratio));}else{Vector<cocos2d::MenuItem*> tempItems;for (int i = 0; i < num;i++){tempItems.pushBack(menuItems.at(i));}auto menu = Menu::createWithArray(tempItems);menu->alignItemsHorizontallyWithPadding(0);menu->setPosition(0, 0);menu->setScale(0.5f);menu->setAnchorPoint(Point(0, 0));this->addChild(menu);tempItems.clear();for (int i = num; i < menuItems.size();i++){tempItems.pushBack(menuItems.at(i));menuItems.at(i)->setPosition(menuItems.at(i - num)->getPosition() + Point(0, -gTileWidth));}menu = Menu::createWithArray(tempItems);menu->setPosition(0, 0);menu->setAnchorPoint(Point(0, 0));menu->setScale(0.5f);this->setContentSize(Size(gTileWidth *ratio* num, gTileWidth *ratio * 2));this->addChild(menu);}}void BuyTowerMenu::menuCallback(Ref* sender){auto item = dynamic_cast<BuyTowerMenuItem*>(sender);std::string strTowerName = item->getTowerName();GameScene::getInstance()->buildTower(strTowerName, GameScene::getInstance()->getBuildPostion());}void BuyTowerMenu::show(){//菜单展示的时候执行动画for (auto item : menuItems){item->runAction(ScaleTo::create(0.1f, 1.0f));}this->setVisible(true);}void BuyTowerMenu::hide(){if (!this->isVisible()){return;}for (auto item : menuItems){item->runAction(ScaleTo::create(0.1f, 0.0f));}this->setVisible(false);GameScene::getInstance()->clearFocusGrid();}//当金币变化的时候  接收金币变化通知更改菜单展示void BuyTowerMenu::updateMenu(){}


适应屏幕位置的代码,因为我是整个项目的代码很多变量大家可能看不懂,并且像这个函数最好写在Menu类里面,只需要坐标转换一下就OK了,不要像我放到GameScene。

//在砖块坐标gridCol,gridRow的位置展示Menuvoid GameScene::showAtGrid(int gridCol, int gridRow){//获取建造塔菜单的大小;Size contentSize = buyTowerMenu->getContentSize();if (gridRow >= 0 && gridRow <= 6 && gridCol >= 0 && gridCol <= 11){Grid *tg = m_map[gridCol][gridRow];Point curGirdPoint = m_GameMap->getPosFromGrids(Point(gridCol, gridRow));Point pos = Point(curGirdPoint.x + contentSize.width / 2 - TILEWIDTH / 4, curGirdPoint.y + contentSize.height);if (pos.x + contentSize.width / 2 > size.width - TILEHEIGHT / 2)pos.x = size.width - contentSize.width / 2;if (pos.y + contentSize.height / 2 >= size.height){pos.y = pos.y - contentSize.height - TILEHEIGHT / 2;}buyTowerMenu->setPosition(pos);buyTowerMenu->show();m_focusgrid = tg;buildPostion = m_focusgrid->getPosition();tg->playBlinkArmature();}}



3 0
原创粉丝点击