zoukankan      html  css  js  c++  java
  • Menu的自己定义实现-------保卫萝卜造塔升级塔菜单实现

    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动画。

    这样就非常easy了,仅仅需动画运行完隐藏就好,例如以下:

    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:bro7
    Author:ben
    Date:2014-07-30
    Description:保卫萝卜建造塔菜单动画的实现
    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信息来创建menu
    	createMenu();
    	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的位置展示Menu
    void 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();
    	}
    }



  • 相关阅读:
    webapi 中使用 protobuf
    apache httpclient cache 实现可缓存的http客户端
    编译nginx时提示undefined reference to 'pcre_free_study' 的问题及解决
    深入理解JVM内存回收机制(不包含垃圾收集器)
    从JDK源码理解java引用
    Buffer的创建及使用源码分析——ByteBuffer为例
    二叉树的子结构、深度以及重建二叉树
    数据结构——树与二叉树的遍历
    Java NIO之Buffer的使用
    Java多线程之synchronized详解
  • 原文地址:https://www.cnblogs.com/hrhguanli/p/3918187.html
Copyright © 2011-2022 走看看