cocos2dx 工作积累

都200201年了 居然还有人在搞原生cocos2dx开发
以下都是自己毕业一年的一些工作中写的方法对象,如有错误或bug欢迎指正

工具方法类

#define __NodePos(node) node->getPosition()
#define __NodePosX(node) node->getPosition().x
#define __NodePosY(node) node->getPosition().y
#define __NodeSize(node) node->getContentSize()
#define __NodeSizeW(node) node->getContentSize().width
#define __NodeSizeH(node) node->getContentSize().height
#define __NodeRealSizeH(node) (node->getContentSize().height * node->getScaleY())
#define __NodeRealSizeW(node) (node->getContentSize().width * node->getScaleX())
#define __NodeRemove(node) if(node != nullptr) { node->removeFromParent(); }
#define __NodeHide(node) if(node != nullptr) { node->setVisible(false); }
#define __NodeShow(node) if(node != nullptr) { node->setVisible(true); }
#define __GetChild(parent, name) (parent->getChildByName(name))
#define __IsNodeNull(node) (node == nullptr)
#define __GetRECT(node) (cocos2d::Rect(__NodePosX(node) - __NodeSizeW(node) / 2, __NodePosY(node) - __NodeSizeH(node) / 2, __NodeSizeW(node), __NodeSizeH(node)))
#define __PI (3.14159226535f)
#define __VisibleSize (Director::getInstance()->getVisibleSize())
class ToolFunc
{
public:
	static void setButtonTittle(cocos2d::ui::Button ** btn, std::string key, int fontSize, int wid = 0, int hei = 0, const std::string& fontname = "", Color3B color = Color3B::BLACK, const int &outlineSize = 0, Color4B outlineColor = Color4B::BLACK)
	{
		(*btn)->setTitleFontName(fontname);
		(*btn)->setTitleFontSize(fontSize);
		(*btn)->setTitleText(key.c_str());
		(*btn)->getTitleRenderer()->setColor(color);
		(*btn)->getTitleRenderer()->setHorizontalAlignment(TextHAlignment::CENTER);
		(*btn)->getTitleRenderer()->setVerticalAlignment(TextVAlignment::CENTER);
		(*btn)->getTitleRenderer()->setPosition(Vec2(((*btn)->getContentSize().width) / 2 + wid, (*btn)->getContentSize().height / 2 + hei));
		if (outlineSize > 0)
		{
			(*btn)->getTitleRenderer()->enableOutline(outlineColor, outlineSize);
		}
		
	}
	
	// 创建骨骼动画
	static spine::SkeletonAnimation* createSpineByName(const std::string &filenname, const std::string &animationName, cocos2d::Node* parent, int zOrder, const std::string &nodename, bool _isLoop = true, bool _isDebug = false)
	{
		auto crab = spine::SkeletonAnimation::createWithJsonFile(filenname + ".json", filenname + ".atlas", 1.0F);
		crab->setName(nodename);
		crab->setAnimation(0, animationName, _isLoop);
		crab->setDebugBonesEnabled(_isDebug);
		parent->addChild(crab, zOrder);
		//crab->setCompleteListener([&](spTrackEntry* entry) {
		//});
		return crab;
	}

	// 创建csb动画
	static Node* createCsbAnimation(cocos2d::Node* _parent, Vec2 _nodePos, const std::string &_effectFile, const std::string &name, int zOrder = 1, bool isLoop = true, std::function<void()> _lastCallFunc = nullptr, float _delayTime = 0)
	{
		auto animation = CSLoader::createNode(_effectFile);
		if (animation != NULL)
		{
			animation->setName(name);
			animation->setPosition(_nodePos);
			_parent->addChild(animation, zOrder);
			auto timeLine = CSLoader::createTimeline(_effectFile);
			if (timeLine != nullptr)
			{
				timeLine->gotoFrameAndPlay(0, timeLine->getDuration(), isLoop);
				timeLine->setLastFrameCallFunc([=]() {
					if (_lastCallFunc != nullptr)
					{
						_lastCallFunc();
					}
					if (isLoop == false)
					{
						animation->removeFromParent();
					}
				});
				animation->runAction(timeLine);
			}
		}
		return animation;
	}
	
	// 洗牌算法
	template<typename T>
	static void randomVector(std::vector<T> &vec) {
		for (int i = vec.size() - 1; i >= 0; i--)
		{
			int j = CCRANDOM_0_1() * (i + 1);
			if (j == vec.size())
			{
				j--;
			}
			T temp = vec[i];
			vec[i] = vec[j];
			vec[j] = temp;
		}
	}

	// 设置label居中
	static void setLabelAlign(cocos2d::Label* label, int width) {
		if (label->getContentSize().width < width)
		{
			return;
		}
		label->setLineBreakWithoutSpace(true);
		label->setMaxLineWidth(width);
		label->setWidth(width);
		label->setHorizontalAlignment(TextHAlignment::LEFT);
	}

	template<typename T>
	static T getChildNode(cocos2d::Node* parent, std::vector<std::string> namelist)
	{
		auto _father = parent;
		for (int _index = 0; _index < namelist.size(); _index++)
		{
			if (_father == nullptr)
			{
				return nullptr;
			}
			_father = __GetChild(_father, namelist[_index]);
		}
		return dynamic_cast<T>(_father);
	}
	

	static Sprite* createSprite(const std::string& file, const std::string &name)
	{
		auto spr = Sprite::create(file);
		if (__IsNodeNull(spr))
		{
			return nullptr;
		}
		spr->setName(name);
		return spr;
	}
	static bool endWith(const string &str, const string &tail) {
		if (str.size() < tail.size())
		{
			return false;
		}
		return str.compare(str.size() - tail.size(), tail.size(), tail) == 0;
	}

	static bool startWith(const string &str, const string &head) {
		if (str.size() < head.size())
		{
			return false;
		}
		return str.compare(0, head.size(), head) == 0;
	}

	static Sprite* createSprite(const std::string& file, const std::string &name, cocos2d::Node* parent, const int &zOrder, const cocos2d::Vec2& pos)
	{
		auto spr = createSprite(file, name);
		if (__IsNodeNull(spr))
		{
			return nullptr;
		}
		parent->addChild(spr, zOrder);
		spr->setPosition(pos);
		return spr;
	}

	static cocos2d::ui::Button* createButton(const std::string &file, const std::string &name, const std::function<void(Ref*, cocos2d::ui::Widget::TouchEventType)>& callback)
	{
		auto btn = cocos2d::ui::Button::create(file);
		if (__IsNodeNull(btn))
		{
			return nullptr;
		}
		btn->setName(name);
		if (callback != nullptr)
		{
			btn->addTouchEventListener(callback);
		}
		return btn;
	}

	static cocos2d::ui::Button* createButton(const std::string &file, const std::string &name, const std::function<void(Ref*, cocos2d::ui::Widget::TouchEventType)>& callback, cocos2d::Node* parent, const int &zOrder, const cocos2d::Vec2& pos)
	{
		auto btn = createButton(file, name, callback);
		if (__IsNodeNull(btn))
		{
			return nullptr;
		}
		parent->addChild(btn, zOrder);
		btn->setPosition(pos);
		return btn;
	}

	static cocos2d::ui::ImageView* createImageView(const std::string &file, const std::string &name)
	{
		auto spr = cocos2d::ui::ImageView::create(file);
		if (__IsNodeNull(spr))
		{
			return nullptr;
		}
		spr->setName(name);
		return spr;

	}

	static cocos2d::ui::ImageView* createImageView(const std::string &file, const std::string &name, cocos2d::Node* parent, const int &zOrder, const cocos2d::Vec2& pos, const std::function<void(Ref*, cocos2d::ui::Widget::TouchEventType)>& callback = nullptr)
	{
		auto spr = createImageView(file, name);
		if (__IsNodeNull(spr))
		{
			return nullptr;
		}
		if (callback != nullptr)
		{
			spr->setTouchEnabled(true);
			spr->addTouchEventListener(callback);
		}
		parent->addChild(spr, zOrder);
		spr->setPosition(pos);
		return spr;
	}

	static Label* createLabel(const std::string& text, const std::string& font, float fontSize, const std::string &name = "", Color3B color = Color3B::WHITE)
	{
		Label* label = Label::createWithSystemFont(text, font, fontSize);
		label->setName(name);
		label->setColor(color);
		return label;
	}

	static Label* createLabel(const std::string& text, const std::string& font, float fontSize, Node* parent, int zOrder, Vec2 position = Vec2::ZERO, const std::string &name = "", Color3B color = Color3B::WHITE)
	{
		auto label = createLabel(text, font, fontSize, name);
		if (label != nullptr)
		{
			label->setPosition(position);
			label->setColor(color);
			parent->addChild(label, zOrder);
		}
		return label;
	}

	static cocos2d::ui::Text* createText(const std::string &text, const std::string& fontName, float fontSize, Color3B color = Color3B::WHITE, const std::string &name = "")
	{
		auto _text = cocos2d::ui::Text::create(text, fontName, fontSize);
		if (__IsNodeNull(_text))
		{
			return nullptr;
		}
		auto _textRender = dynamic_cast<cocos2d::Label*>(_text->getVirtualRenderer());
		_text->setName(name);
		_text->setColor(color);
		return _text;
	}

	static cocos2d::ui::Text* createText(const std::string &text, const std::string& fontName, float fontSize, cocos2d::Node* node, int zOrder, Color3B color = Color3B::WHITE, cocos2d::Vec2 pos = cocos2d::Vec2::ZERO, const std::string &name = "", const std::function<void(Ref*, cocos2d::ui::Widget::TouchEventType)>& callback = nullptr)
	{
		auto _text = createText(text, fontName, fontSize, color, name);
		if (__IsNodeNull(_text))
		{
			return nullptr;
		}
		node->addChild(_text, zOrder);
		_text->setPosition(pos);

		if (callback != nullptr)
		{
			_text->setTouchEnabled(true);
			_text->addTouchEventListener(callback);
		}

		return _text;
	}
};

这里提供创建SpriteLabelTextImageViewButton创建方法原因有两点

  1. 减少重复代码的编写,设置坐标、名字、父节点等参数的调用是很频繁的,只用一行可以设置很多必须的参数
  2. 若需要对这些基础节点做统一处理可以直接在这些函数中处理,而不用在工程所有创建的地方一个一个处理

最理想的状况是,对于所有的基础节点建议是创建自己的类去继承基础节点类型,这样加减功能、统一处理都很方便

一些用法

		auto test_label = Label::createWithSystemFont(m_language.getStringByKey("HomeScene_Ornamen"), "", 20);
		test_label->setOverflow(Label::Overflow::SHRINK);
		test_label->setDimensions(ConW(m_bgSpr) - 20, ConH(m_bgSpr) - 10);
    // 水平方向默认是左对齐,居中设置如下
    test_label->setHorizontalAlignment(TextHAlignment::CENTER);
    
    // 垂直方向默认是顶对齐,居中设置如下
    test_label->setVerticalAlignment(TextVAlignment::CENTER);
    
    // 如果设置想设置文本不换行 则添加下面的这句话
		test_label->enableWrap(false);

Warning:这个接口不能在Cocos2dx 3.6上使用
通过setDimensions设置test_label的矩形显示范围,当test_label按照原字体大小排列超过设置的矩形大小后,会自动减小字体大小,以保证内容在矩形中完整显示

		auto btn = cocos2d::ui::Button::create("msxt_humanMap/btn_selected.png");
		btn->setZoomScale(0);

setZoomScale设置按钮点击之后不放大,这里参数代表点击后放大倍数,1表示放大两倍,0.1表示放大1.1倍

		auto btn = cocos2d::ui::Button::create("msxt_humanMap/btn_selected.png");
		btn->setBright(false);

setBright 参数未false时按钮置灰,true的时候恢复原状

	m_pageView = cocos2d::ui::PageView::create();
	this->addChild(m_pageView, 2);
	m_pageView->setContentSize(Size(500, m_bg->getContentSize().height - 140 - 7));
	m_pageView->setPosition(Vec2(m_bg->getPositionX() - m_bg->getContentSize().width / 2 + 35, m_bg->getPositionY() - m_bg->getContentSize().height / 2));
  // 添加翻页的监听事件回调函数
	m_pageView->addEventListener((cocos2d::ui::PageView::ccPageViewCallback)CC_CALLBACK_2(HcrHowPlayiLayer::PageViewCallBack, this)); 
  
  // 添加页面
  m_pageView->addPage(Widget1);
  m_pageView->addPage(Widget2);
  m_pageView->addPage(Widget3);
  
  // 回调函数 动态修改当前页数
void HcrHowPlayiLayer::PageViewCallBack(Ref* sender, ui::PageView::EventType type)
{
	int nowIndex = m_pageView->getCurrentPageIndex();
	auto label_showPageIndex = dynamic_cast<Label*>(this->getChildByName("label_showPageIndex"));
	label_showPageIndex->setString(PUB_strformat("%d/3", nowIndex + 1));
}

滑动翻页
这里通过回调函数设置label的方式会延迟变换,只有当界面完全停住才会执行回调
如果想要准确实时的变换label,需要使用update动态监听当前界面序号(其实页面在滑动的时候当前pageindex已经改变了,但是等到滑动完全停止的时候才会调用回调)

	auto spr_progressBg = Sprite::create("hcrcc_loading/bac_progress.png");
	//spr_progressBg->setScale(0.8f);
	spr_progressBg->setPosition(Vec2(vSize.width/2, vSize.height / 5));
	this->addChild(spr_progressBg, 1);
  
	m_ui_progress = ProgressTimer::create(Sprite::create("hcrcc_loading/icon_progress.png"));
	m_ui_progress->setType(ProgressTimer::Type::BAR);//设置类型
	m_ui_progress->setBarChangeRate(Vec2(1, 0));//设置进度条的按照x方向移动
	m_ui_progress->setMidpoint(Vec2(0, 1));//设置进度的运动方向0到1
	m_ui_progress->setPercentage(0);		// 设置当前值
	m_ui_progress->setPosition(spr_progressBg->getPosition());
  
  // 进度条动画
  m_ui_progress->runAction(Sequence::create(ProgressFromTo::create(0.1f, nowPrecent, 100), CallFunc::create([=] {
  
		}), NULL));

进度条
setType设置进度条的类型:BAR 条形;RADIAL 扇形
setPercentage设置进度条百分比
ProgressTimer没有背景图片,需要额外设置一个Sprite或者ImageView作为背景图片
ProgressFromTo为进度条动画,表示在0.1s内从nowPrecent变换到100
ProgressTo::create(time, targetPrecent) 也为进度条动画,表示从0开始变换到targetPrecent

auto scrallview = cocos2d::ui::ScrollView::create();
scrallview->setContentSize(Size(420.0f, 200));
auto text = Label::createWithSystemFont(hcrDiaryImageAndText.diaryText, "", 31);
setLabelAlign(text, 420);
scrallview->setPosition(Vec2(vSize.width / 2 - 210 , btn_continue->getPositionY() + btn_continue->getContentSize().height/2 + 20));
text->setPosition(scrallview->getContentSize().width / 2, text->getContentSize().height / 2);
text->setColor(Color3B::WHITE);
//CCLOG("%lf", text->getContentSize().height);
scrallview->setInnerContainerSize(Size(420.0f, text->getContentSize().height + 10));
scrallview->getInnerContainer()->addChild(text, 1);

重点1:scrallview->setContentSize 为设置 ScrollView 的显示界面
重点2:scrallview->setInnerContainerSize 为设置滚动层的Size,需要比scrallview->setContentSize更长或者更宽
重点3:scrallview->getInnerContainer()->addChild 将对象添加到 ScrollView的对象层中
ScrollView可以理解为两层,一层是对象,一层是窗口,我们移动的时候移动的其实是对象层,因为对象层的移动我们从窗口看到的东西也会改变


ImageView* i0 = ImageView::create("btn0.png");
i0->setScale9Enabled(true);
i0->setPosition(Vec2(190, 115));
i0->setContentSize(Size(300,192));	
addChild(i0);

  • 当有精灵缩放时,需要保持四个角不变,避免失真的需求时,需要使用到Scale9Sprite类,本文译作九宫格类.
#define CC_SYNTHESIZE(varType, varName, funName)\
protected: varType varName; public: virtual inline varType get##funName(void) const { return varName; } virtual inline void set##funName(varType var){ varName = var; }

#define CC_SYNTHESIZE_PASS_BY_REF(varType, varName, funName)\
protected: varType varName; public: virtual inline const varType& get##funName(void) const { return varName; } virtual inline void set##funName(const varType& var){ varName = var; }

通过宏定义对象并且设置最简易的get和set方法,不用再手动设置
如果设置和获取时需要进行转换那还是得自己写

#define CC_BREAK_IF(cond)           if(cond) break

简易break宏

//骨骼动画Spine,需要包含Spine/spine-cocos2dx.h头文件,命名空间spine
auto skeleton=SkeletonAnimation::createWithFile("spine/spineboy.json", "spine/spineboy.atlas");//加载文件
skeleton->setAnimation(0, "walk", false);//运行骨骼动画,参数:( 动作例表id(从0开始),动作名,是否循环播放)
skeleton->setPosition(500,0);
skeleton->setScale(0.5f);//设置大小
skeleton->setTimeScale(0.5f);//设置运行速度
skeleton->setMix("walk", "run", 1.0f);//设置动画平缓切换用时
skeleton->addAnimation(0, "run", true);//在之前动作做完之后,添加一个动作
skeleton->setDebugBonesEnabled(true);//显示骨头
this->addChild(skeleton);
spBone * skele= skeleton->findBone("jinyumantangdagaung");       //获取龙骨节点名称
skeleton->setEndListener([=](int trackIndex) {
    if (trackIndex == 1) //播放次数,
    {
      //回调需要的
    }
	});
skeleton->setCompleteListener([&](spTrackEntry* entry) {

});
skeleton->update(0);
auto size = skeleton->getBoundingBox().size;										// 获得骨骼动画的宽高 需要先执行上面的update(0)

//骨骼动画armature,需要包含cocostudio/CocoStudio.h头文件,命名空间cocostudio
//加载动作资源,可以用异步加载,参数有点怪
ArmatureDataManager::getInstance()->addArmatureFileInfo("armature/Cowboy0.png", "armature/Cowboy0.plist", "armature/Cowboy.ExportJson");
auto am=Armature::create("Cowboy");//创建骨骼动画
am->setScale(0.6f);
am->getAnimation()->setSpeedScale(0.5f);//设置播放速度
am->getAnimation()->playWithIndex(0);//播放索引为0的动作
am->setPosition(500,300);
this->addChild(am);
am->getAnimation()->pause();     //播放
std::function<void(Armature*, MovementEventType, const std::string&)> armatureFun = [=](Armature* armature, MovementEventType type, const std::string& id)
		{
			if (type == MovementEventType::COMPLETE && id == "Animation4")
			{
				animationEvent(true);
				am->getAnimation()->stop();
			}
		};
am->getAnimation()->setMovementEventCallFunc(armatureFun);        //回调

骨骼动画
安卓打包添加 CC_USE_SPINE :=1USE_C2DX_LIB :=1两个到Application.mk文件中

自动排列节点

代码未完全验证,符合我需求就没有验证了,有问题欢迎评论

在这里插入图片描述
在这里插入图片描述

SuperArrangeNode

自动居中排序的Node节点

.h文件
// 自动排列的node
class SuperArrangeNode : public cocos2d::Node
{
public:
	enum SuperDirection {
		VERTICAL,
		HORIZONTAL,
	};
	CREATE_FUNC(SuperArrangeNode);
	bool init() override;
	void setNodeDisatnce(float _distance);									// 设置节点之间的距离
	void setSuperDirection(SuperArrangeNode::SuperDirection _type);			// 设置横向或纵向
	void insertOneChild(cocos2d::Node* _child, int _zOrder);				// 插入节点
	void removeOneChild(const std::string &_name);							// 删除节点
	inline int getChildNodeNum();											// 获得节点数目
	const Size& getContentSize() const;										// 获得该排列节点的长宽
	void updateNodePos();													// 主动更新节点内对象坐标

protected:
	inline Size getNodeRealSize(cocos2d::Node* _node);
	void removeNullNode();									// 删除空的节点

private:
	Size								m_size;
	float								m_nodeDistant;		// 作为一行或一列的间距
	SuperDirection						m_curDirection;		// 设置方向
	std::list<cocos2d::Node*>			m_insertNode;		// 存储添加为子节点的对象	
};
.cpp文件
bool SuperArrangeNode::init()
{
	if (!cocos2d::Node::init())
	{
		return false;
	}

	m_nodeDistant = 0;
	m_insertNode.clear();
	m_curDirection = SuperArrangeNode::SuperDirection::HORIZONTAL;

	return true;
}

void SuperArrangeNode::setNodeDisatnce(float _distance)
{
	if (std::abs(_distance - m_nodeDistant) > 1.0f)
	{
		m_nodeDistant = _distance;
		updateNodePos();
	}
}

void SuperArrangeNode::setSuperDirection(SuperArrangeNode::SuperDirection _type)
{
	if (m_curDirection != _type)
	{
		m_curDirection = _type;
		updateNodePos();
	}
}

void SuperArrangeNode::insertOneChild(cocos2d::Node* _child, int _zOrder)
{
	this->addChild(_child, _zOrder);
	m_insertNode.push_back(_child);
	_child->setPosition(Vec2::ZERO);
	updateNodePos();
}

void SuperArrangeNode::removeOneChild(const std::string &_name)
{
	for (auto _it = m_insertNode.begin(); _it != m_insertNode.end(); )
	{
		if ((*_it) == nullptr)
		{
			_it = m_insertNode.erase(_it);
		}
		else if ((*_it)->getName() == _name)
		{
			(*_it)->removeFromParent();
			_it = m_insertNode.erase(_it);
		}
		else {
			++_it;
		}
	}
	updateNodePos();
}

inline int SuperArrangeNode::getChildNodeNum()
{
	return m_insertNode.size();
}

const Size & SuperArrangeNode::getContentSize() const
{
	return m_size;
}

void SuperArrangeNode::updateNodePos()
{
	removeNullNode();
	float sumLength = 0;
	float maxHeight = 0;
	for (auto _item : m_insertNode)
	{
		Size realSize = getNodeRealSize(_item);
		switch (m_curDirection)
		{
		case SuperArrangeNode::VERTICAL:
			sumLength += realSize.height + m_nodeDistant;
			maxHeight = std::max(maxHeight, realSize.width);
			break;
		case SuperArrangeNode::HORIZONTAL:
			sumLength += realSize.width + m_nodeDistant;
			maxHeight = std::max(maxHeight, realSize.height);
			break;
		}
	}
	sumLength -= m_nodeDistant;	// 多加了一个长度 要减掉多加的
	switch (m_curDirection)
	{
	case SuperArrangeNode::VERTICAL:
		m_size = Size(maxHeight, sumLength);
		break;
	case SuperArrangeNode::HORIZONTAL:
		m_size = Size(sumLength, maxHeight);
		break;
	}
	sumLength /= 2;
	cocos2d::Node* beforeNode = nullptr;
	for (auto _item : m_insertNode)
	{
		Size realSize = getNodeRealSize(_item);
		if (nullptr == beforeNode)
		{
			switch (m_curDirection)
			{
			case SuperArrangeNode::VERTICAL:
				_item->setPosition(Vec2(0, sumLength - realSize.height / 2));
				break;
			case SuperArrangeNode::HORIZONTAL:
				_item->setPosition(Vec2(-sumLength + realSize.width / 2, 0));
				break;
			}
		}
		else {
			auto beforeNodeSize = getNodeRealSize(beforeNode);
			switch (m_curDirection)
			{
			case SuperArrangeNode::VERTICAL:
				_item->setPosition(Vec2(0, beforeNode->getPositionY() - beforeNodeSize.height / 2 - m_nodeDistant - realSize.height / 2));
				break;
			case SuperArrangeNode::HORIZONTAL:
				_item->setPosition(Vec2(beforeNode->getPositionX() + beforeNodeSize.width / 2 + m_nodeDistant + realSize.width / 2, 0));
				break;
			}
		}
		beforeNode = _item;
	}
}

void SuperArrangeNode::removeNullNode()
{
	for (auto _it = m_insertNode.begin(); _it != m_insertNode.end(); )
	{
		if ((*_it) == nullptr)
		{
			_it = m_insertNode.erase(_it);
		}
		else {
			++_it;
		}
	}
}

inline Size SuperArrangeNode::getNodeRealSize(cocos2d::Node * _node)
{
	return Size(_node->getContentSize().width * _node->getScaleX(), _node->getContentSize().height * _node->getScaleY());
}

SuperScrollView

自动排序的ScrollView

  • 设计思路
    • 内部维护的是一个SuperArrangeNode(上面实现)的对象数组
    • 当一个SuperArrangeNode的子节点数目大于设定数目时,创建新的SuperArrangeNode对象并插入数组
// .cpp文件
// 自动排列的滑动列表
class SuperScrollView : public cocos2d::ui::ScrollView
{
public:
	enum SuperDirection {
		VERTICAL,
		HORIZONTAL,
	};
	CREATE_FUNC(SuperScrollView);
	bool init() override;

	void setSuperDirection(SuperScrollView::SuperDirection _type);	// 设置方向
	void insertOneChild(cocos2d::Node* _child, int zOrder);			// 插入一个节点
	void removeOneChild(const std::string& _childName);				// 删除一个子节点
	void setNodeDistance(int _rowDistance, int _colDistance);		// 设置横向和纵向的距离
	void setNum(int _rowNum);										// 获得当前子节点的总数目
	void setShowBar(bool _isAutoShow);								// 是否展示滑动条

	void reverseChildNodes();										// 上下或左右翻转,实际上就是reverse数组
	bool isReverseChildNodes();										// 判断是否翻转过,如果翻转过为true
protected:
	void updateChildPosition();
	void arrangeFromMid(float sumlength);					// 当总长度不超过滑动列表的长度时 居中对齐
	void arrangeFromEdge();									// 当长度超过滑动列表的长度时 向下或者想左对齐
	void removeNullNode();

	inline Size getNodeRealSize(cocos2d::Node* _node);

private:
	bool								m_isReverse;
	bool								m_isShowBar;
	int									m_rowNum;			// 一行或一列的数目 默认为1
	int									m_nodeRowDistant;	// 作为一行的间距
	int									m_nodeColIdstant;	// 作为一列的间距
	SuperDirection						m_curDirection;		// 设置方向
	std::list<SuperArrangeNode*>		m_insertNode;		// 存储自动排列的子节点
};


bool SuperScrollView::init()
{
	if (!ScrollView::init())
	{
		return false;
	}
	m_curDirection = VERTICAL;
	m_isReverse = false;
	m_insertNode.clear();
	m_nodeColIdstant = 0;
	m_nodeRowDistant = 0;
	m_rowNum = 1;
	m_isShowBar = false;
	return true;
}

void SuperScrollView::setSuperDirection(SuperScrollView::SuperDirection _type)
{
	if (_type != m_curDirection)
	{
		m_curDirection = _type;

		switch (_type)
		{
		case SuperScrollView::VERTICAL:
			this->setDirection(ScrollView::Direction::VERTICAL);
			break;
		case SuperScrollView::HORIZONTAL:
			this->setDirection(ScrollView::Direction::HORIZONTAL);
			break;
		}

		updateChildPosition();
	}
}

void SuperScrollView::insertOneChild(cocos2d::Node * _child, int zOrder)
{
	_child->setPosition(Vec2::ZERO);
	//this->getInnerContainer()->addChild(_child, zOrder);
	bool _isInsert = false;
	for (auto _arrangeNode : m_insertNode)
	{
		if (_arrangeNode->getChildNodeNum() < m_rowNum)
		{
			_isInsert = true;
			_arrangeNode->insertOneChild(_child, zOrder);
		}
	}
	if (!_isInsert)
	{
		auto newArrangeNode = SuperArrangeNode::create();
		this->getInnerContainer()->addChild(newArrangeNode, 1);
		switch (m_curDirection)
		{
		case SuperScrollView::VERTICAL:
			newArrangeNode->setSuperDirection(SuperArrangeNode::SuperDirection::HORIZONTAL);
			newArrangeNode->setNodeDisatnce(m_nodeRowDistant);
			break;
		case SuperScrollView::HORIZONTAL:
			newArrangeNode->setSuperDirection(SuperArrangeNode::SuperDirection::VERTICAL);
			newArrangeNode->setNodeDisatnce(m_nodeColIdstant);
			break;
		}
		newArrangeNode->insertOneChild(_child, zOrder);
		m_insertNode.push_back(newArrangeNode);
	}
	updateChildPosition();
}

void SuperScrollView::removeOneChild(const std::string & _childName)
{
	for (auto node : m_insertNode)
	{
		node->removeOneChild(_childName);
	}
	removeNullNode();
	updateChildPosition();
}

void SuperScrollView::setNodeDistance(int _rowDistance, int _colDistance)
{
	m_nodeColIdstant = _colDistance;
	m_nodeRowDistant = _rowDistance;
}

void SuperScrollView::setNum(int _rowNum)
{
	m_rowNum = _rowNum;
}

void SuperScrollView::setShowBar(bool _isAutoShow)
{
	m_isShowBar = _isAutoShow;
}

void SuperScrollView::reverseChildNodes()
{
	m_isReverse = !m_isReverse;
	std::reverse(m_insertNode.begin(), m_insertNode.end());
	updateChildPosition();
	if (m_isReverse)
	{
		this->scrollToTop(0, false);
	}
}

bool SuperScrollView::isReverseChildNodes()
{
	return m_isReverse;
}

void SuperScrollView::updateChildPosition()
{
	Size thisSize = this->getContentSize();
	float sumLength = 0.0f;
	for (auto listIt = m_insertNode.begin(); listIt != m_insertNode.end(); )
	{
		if ((*listIt) == nullptr)
		{
			listIt = m_insertNode.erase(listIt);
		}
		else {
			Size realSize = (*listIt)->getContentSize();
			switch (m_curDirection)
			{
			case SuperScrollView::VERTICAL:
				sumLength += realSize.height + m_nodeColIdstant;
				break;
			case SuperScrollView::HORIZONTAL:
				sumLength += realSize.width + m_nodeRowDistant;
				break;
			}
			++listIt;
		}
	};
	switch (m_curDirection)
	{
	case SuperScrollView::VERTICAL:
		sumLength -= m_nodeColIdstant;
		break;
	case SuperScrollView::HORIZONTAL:
		sumLength -= m_nodeRowDistant;
		break;
	}

	switch (m_curDirection)
	{
	case SuperScrollView::VERTICAL:
		if (this->getContentSize().height < sumLength)
		{
			this->getInnerContainer()->setContentSize(Size(this->getContentSize().width, sumLength));
			arrangeFromEdge();
		}
		else {
			this->getInnerContainer()->setContentSize(Size(this->getContentSize().width, this->getContentSize().height));
			arrangeFromMid(sumLength);
		}
		break;
	case SuperScrollView::HORIZONTAL:
		if (this->getContentSize().width < sumLength)
		{
			this->getInnerContainer()->setContentSize(Size(sumLength, this->getContentSize().height));
			arrangeFromEdge();
		}
		else {
			this->getInnerContainer()->setContentSize(Size(this->getContentSize().width, this->getContentSize().height));
			arrangeFromMid(sumLength);
		}
		break;
	}

}

void SuperScrollView::arrangeFromMid(float length)
{
	float topPos = length / 2;
	cocos2d::Node* beforeNode = nullptr;
	for (auto _item : m_insertNode)
	{
		if (beforeNode == nullptr)
		{
			switch (m_curDirection)
			{
			case SuperScrollView::VERTICAL:
				_item->setPosition(Vec2(this->getContentSize().width / 2, this->getContentSize().height / 2 + topPos - _item->getContentSize().height / 2));
				break;
			case SuperScrollView::HORIZONTAL:
				_item->setPosition(Vec2(this->getContentSize().width / 2 - topPos + _item->getContentSize().width / 2, this->getLayoutContentSize().height / 2));
				break;
			}
		}
		else
		{
			switch (m_curDirection)
			{
			case SuperScrollView::VERTICAL:
				_item->setPosition(Vec2(this->getContentSize().width / 2, beforeNode->getPositionY() - beforeNode->getContentSize().height / 2 - m_nodeColIdstant - _item->getContentSize().height / 2));
				break;
			case SuperScrollView::HORIZONTAL:
				_item->setPosition(Vec2(beforeNode->getPositionX() + beforeNode->getContentSize().width / 2 + m_nodeRowDistant + _item->getContentSize().width / 2, this->getLayoutContentSize().height / 2));
				break;
			}
		}
		beforeNode = _item;
	}
}

void SuperScrollView::arrangeFromEdge()
{
	cocos2d::Node* beforeNode = nullptr;
	if (m_isShowBar)
	{
		this->setScrollBarEnabled(true);
	}
	for (auto _item : m_insertNode)
	{
		if (beforeNode == nullptr)
		{
			switch (m_curDirection)
			{
			case SuperScrollView::VERTICAL:
				_item->setPosition(Vec2(this->getContentSize().width / 2, _item->getContentSize().height / 2));
				break;
			case SuperScrollView::HORIZONTAL:
				_item->setPosition(Vec2(_item->getContentSize().width / 2, this->getLayoutContentSize().height / 2));
				break;
			}
		}
		else {
			switch (m_curDirection)
			{
			case SuperScrollView::VERTICAL:
				_item->setPosition(Vec2(this->getContentSize().width / 2, beforeNode->getPositionY() + beforeNode->getContentSize().height / 2 + m_nodeColIdstant + _item->getContentSize().height / 2));
				break;
			case SuperScrollView::HORIZONTAL:
				_item->setPosition(Vec2(beforeNode->getPositionX() + beforeNode->getContentSize().width / 2 + m_nodeRowDistant + _item->getContentSize().width / 2, this->getContentSize().height / 2));
				//CCLOG("%f. %f. %f. %f.", beforeNode->getPositionX(), beforeNode->getContentSize().width, m_nodeRowDistant, _item->getContentSize().width);
				break;
			}
		}
		beforeNode = _item;
	}
}

void SuperScrollView::removeNullNode()
{
	for (auto _it = m_insertNode.begin(); _it != m_insertNode.end();)
	{
		if ((*_it)->getChildNodeNum() == 0)
		{
			_it = m_insertNode.erase(_it);
		}
		else {
			++_it;
		}
	}
}

inline Size SuperScrollView::getNodeRealSize(cocos2d::Node * _node)
{
	return Size(_node->getContentSize().width * _node->getScaleX(), _node->getContentSize().height * _node->getScaleY());
}

使用实例

// 使用实例

	auto arrangeNode = SuperArrangeNode::create();
	this->addChild(arrangeNode, 1);
	arrangeNode->setPosition(Director::getInstance()->getVisibleSize() / 2);
	arrangeNode->setNodeDisatnce(20);
	arrangeNode->setSuperDirection(SuperArrangeNode::VERTICAL);
	for (int i = 0; i < 5; i++)
	{
		auto spr = Sprite::create("CloseNormal.png");
		spr->setScale(0.5f);
		arrangeNode->insertOneChild(spr, 1);
	}
  
  auto arrangeView = SuperScrollView::create();
	this->addChild(arrangeView, 1);
	arrangeView->setContentSize(Director::getInstance()->getVisibleSize() / 2);
	arrangeView->setPosition(Vec2::ZERO);
	arrangeView->setNodeDistance(10, 50);
	arrangeView->setSuperDirection(SuperScrollView::HORIZONTAL);
	arrangeView->setNum(4);// 一行或者一列4个

	for (int i = 0; i < 25; i++)
	{
		auto spr = Sprite::create("CloseNormal.png");
		//spr->setScale(0.5f);
		arrangeView->insertOneChild(spr, 1);
	}

自动排列的Node和ScrollView
这里的SuperArrangeNode可以单独使用,SuperScrollView必须与SuperArrayNode搭配使用
就是一个单纯的坐标判断 没有复杂算法

tip

最近使用发现问题:

  1. 添加自定义节点时,在设置Item的Size之后记得设置锚点为中心点
void ypjyWarehouseEnoughItem::initUI()
{
	const float c_scale = 0.86f;
	auto bg = SuperSprite::create("gameimage/Item/ItemBg.png", this, 0, Vec2::ZERO, "bg");
	bg->setScale(c_scale);
	this->setContentSize(Size(__NodeSize(bg) * c_scale));
	this->setAnchorPoint(Vec2(0.5f, 0.5f));
}
// ypjyWarehouseEnoughItem 继承于Node

自动排队

// 能够移动的对象基类
class BasePerson : public cocos2d::Node {
public:
	CREATE_FUNC(BasePerson);

	virtual void action_StopMove() {}
	virtual void action_StartMove() {}
	virtual float getLineUpSpeed() { return m_fMoveSpeed; }		// 排队的移动速度

	CC_SYNTHESIZE(float, m_fMoveSpeed, MoveSpeed);				// 移动速度
	CC_SYNTHESIZE(bool, m_bIsInLineUp, IsInLineUp);				// 是否正在排队
protected:


private:
};

// 排队列表
class LineUpList
{
public:
	LineUpList();
	unsigned int getListSize();									// 获得当前队列中的对象个数
	void clearAllNode();										// 清除所有维护对象(并未从父节点中删除)
	void insetOneNode(BasePerson* _node);						// 插入一个对象
	void updateTargetPos(Vec2 _targetPos);						// 更新目标节点坐标
	cocos2d::Node* popFirstNode();								// 返回队首节点
	// 更新所有节点坐标
	void updateNodesPosition();									// 更新队列中所有节点的坐标
	/*
	设置队列的信息
	输入参数:起点坐标, 每秒移动像素,前后两人横向坐标差,前后两任纵向坐标差,队伍是否向下排列,队伍是否向右排列
	*/
	void setLineUpInfo(const Vec2& _startPos, float _speed, unsigned int _distanceX, unsigned int _distanceY, bool _toDown, bool _toRight);
protected:
	inline float getDistance(Vec2 _pos1, Vec2 _pos2);
private:
	bool						m_isDown;			
	bool						m_isRight;
	Vec2						m_nodeDistance;
	Vec2						m_startPos;
	std::list<BasePerson*>		m_lineUpNodes;
};

LineUpList::LineUpList()
{
	m_lineUpNodes.clear();
	m_nodeDistance = Vec2::ZERO;
	m_startPos = Vec2::ZERO;
	m_isDown = true;
	m_isRight = true;
}

unsigned int LineUpList::getListSize()
{
	return m_lineUpNodes.size();
}

void LineUpList::clearAllNode()
{
	m_lineUpNodes.clear();
}

void LineUpList::insetOneNode(BasePerson * _node)
{
	_node->setIsInLineUp(true);
	m_lineUpNodes.push_back(_node);
	updateNodesPosition();
}

void LineUpList::updateTargetPos(Vec2 _targetPos)
{
	m_startPos = _targetPos;
}

cocos2d::Node * LineUpList::popFirstNode()
{
	if (m_lineUpNodes.size() == 0)
	{
		return nullptr;
	}
	auto result = m_lineUpNodes.front();
	auto _person = dynamic_cast<BasePerson*>(result);
	if (_person != nullptr)
	{
		_person->setIsInLineUp(false);
	}
	m_lineUpNodes.pop_front();
	updateNodesPosition();
	return result;
}

void LineUpList::setLineUpInfo(const Vec2 & _startPos, float _speed, unsigned int _distanceX, unsigned int _distanceY, bool _toDown, bool _toRight)
{
	m_startPos = _startPos;
	m_nodeDistance = Vec2(_distanceX, _distanceY);
	m_isDown = _toDown;
	m_isRight = _toRight;
}

void LineUpList::updateNodesPosition()
{
	Vec2 _beforePos = m_startPos;
	for (auto _node : m_lineUpNodes)
	{
		if (_node == nullptr)
		{
			continue;
		}
		_node->stopAllActions();
		_node->action_StartMove();
		_node->runAction(Sequence::create(
			MoveTo::create(getDistance(_beforePos, _node->getPosition()) / _node->getLineUpSpeed(), _beforePos),
			CallFunc::create([=]() { _node->action_StopMove(); }),
			NULL));
		if (m_isRight)
		{
			_beforePos.x += m_nodeDistance.x;
		}
		else {
			_beforePos.x -= m_nodeDistance.x;
		}
		if (m_isDown)
		{
			_beforePos.y -= m_nodeDistance.y;
		}
		else {
			_beforePos.y += m_nodeDistance.y;
		}
	}
}

float LineUpList::getDistance(Vec2 _pos1, Vec2 _pos2)
{
	float _distantX = std::abs(_pos2.x - _pos1.x);
	float _distantY = std::abs(_pos2.y - _pos1.y);
	return std::sqrt((_distantX*_distantX + _distantY * _distantY));
}

替换UserDefault的方法

所谓替换方法其实就是自己维护一个本地化文件:txt、json、xml甚至二进制都是可以的

// .h
// 数据管理实例
class UserDataTool : public Singleton<UserDataTool >
{
public:
	//static ypjyUserDataTool* getInstance();

	ypjyUserDataTool();
	void SetUserData(std::string _key, int _val);
	void SetUserData(std::string _key, double _val);
	void SetUserData(std::string _key, std::string _val);
	void SetUserData(std::string _key, bool _val);

	int GetUserData_Int(std::string _key, int _defaultVal = 0);
	double GetUserData_Double(std::string _key, double _defaultVal = 0.0);
	std::string GetUserData_String(std::string _key, std::string _defaultVal = "");
	bool GetUserData_Bool(std::string _key, bool _defaultVal = false);

	bool HasRecordKey(std::string _key);

	void get_Int_Int_MapByKey(const std::string &_key, std::map<int, int> &_val);
	void set_Int_Int_MapByKey(const std::string &_key, const std::map<int, int> &_val);

	void get_Int_VecByKey(const std::string &_key, std::vector<int> &_val);
	void set_Int_VecByKey(const std::string &_key, const std::vector<int> &_val);

#ifndef ENV_GAMETEST
protected:
#endif // ENV_GAMETEST

	void		ReadJsonFile();
protected:
	std::string GetJsonString(const map<string, int> &mInt, const map<std::string, double> &mDouble, const map<string, string> &mString);
	void		SaveJsonFile();

	std::vector<int> valTo_VecInt(const rapidjson::Value &_val);
	std::map<int, int> valTo_Int_IntMap(const rapidjson::Value &_val);

	void clearAllData();
private:
	//static ypjyUserDataTool* userData;
	std::string _jsonFileName;

	std::map<std::string, bool>						m_boolValue;
	std::map<std::string, int>						m_intValue;
	std::map<std::string, double>					m_doubleValue;
	std::map<std::string, std::string>				m_stringValue;

	std::map<std::string, std::vector<int>>			m_intVecValue;
	std::map<std::string, std::map<int, int>>		m_intMapValue;
};
.cpp

UserDataTool::UserDataTool ()
{
	_jsonFileName = FileUtils::getInstance()->getWritablePath() + "ypjy/ypjyUserDataToolJson.json";
	//CCLOG("crate user data");
	ReadJsonFile();
}
void UserDataTool::SetUserData(std::string _key, int _val)
{
	if (m_intValue.find(_key) == m_intValue.end())
	{
		m_intValue.insert({ _key, _val });
		//CCLOG("add");
	}
	else {
		m_intValue[_key] = _val;
		//CCLOG("change");
	}
	//CCLOG("int size %d", m_intValue.size());
	SaveJsonFile();
}

void UserDataTool::SetUserData(std::string _key, double _val)
{
	if (m_doubleValue.find(_key) == m_doubleValue.end())
	{
		m_doubleValue.insert({ _key, _val });
	}
	else {
		m_doubleValue[_key] = _val;
	}
	SaveJsonFile();
}

void UserDataTool::SetUserData(std::string _key, std::string _val)
{
	if (m_stringValue.find(_key) == m_stringValue.end())
	{
		m_stringValue.insert({ _key, _val });
	}
	else {
		m_stringValue[_key] = _val;
	}
	SaveJsonFile();
}

void UserDataTool::SetUserData(std::string _key, bool _val)
{
	if (m_boolValue.find(_key) != m_boolValue.end())
	{
		m_boolValue.insert({_key, _val});
	}
	m_boolValue[_key] = _val;
	SaveJsonFile();
}

int UserDataTool::GetUserData_Int(std::string _key, int _defaultVal)
{
	if (m_intValue.find(_key) != m_intValue.end())
	{
		return m_intValue[_key];
	}
	return _defaultVal;
}

double UserDataTool::GetUserData_Double(std::string _key, double _defaultVal)
{
	if (m_doubleValue.find(_key) != m_doubleValue.end())
	{
		return m_doubleValue[_key];
	}
	return _defaultVal;
}

std::string UserDataTool::GetUserData_String(std::string _key, std::string _defaultVal)
{
	if (m_stringValue.find(_key) != m_stringValue.end())
	{
		return m_stringValue[_key];
	}
	return _defaultVal;
}

bool UserDataTool::GetUserData_Bool(std::string _key, bool _defaultVal)
{
	if (m_boolValue.find(_key) != m_boolValue.end())
	{
		return m_boolValue[_key];
	}
	return _defaultVal;
}

void UserDataTool::get_Int_Int_MapByKey(const std::string & _key, std::map<int, int>& _val)
{
	_val.clear();
	std::string _realKey = _key + "_intintmap";
	if (m_intMapValue.find(_realKey) != m_intMapValue.end())
	{
		_val = m_intMapValue[_realKey];
	}
}

void UserDataTool::set_Int_Int_MapByKey(const std::string & _key, const std::map<int, int>& _val)
{
	std::string _realKey = _key + "_intintmap";
	if (m_intMapValue.find(_realKey) == m_intMapValue.end())
	{
		m_intMapValue.insert({ _realKey, _val });
	}
	else {
		m_intMapValue[_realKey] = _val;
	}
	SaveJsonFile();
}

void UserDataTool::get_Int_VecByKey(const std::string & _key, std::vector<int>& _val)
{
	_val.clear();
	std::string _realKey = _key + "_intvec";
	if (m_intVecValue.find(_realKey) != m_intVecValue.end())
	{
		_val = m_intVecValue[_realKey];
	}
}

void UserDataTool::set_Int_VecByKey(const std::string & _key, const std::vector<int>& _val)
{
	std::string _realKey = _key + "_intvec";
	if (m_intVecValue.find(_realKey) == m_intVecValue.end())
	{
		m_intVecValue.insert({ _realKey, _val });
	}
	else {
		m_intVecValue[_realKey] = _val;
	}

	SaveJsonFile();
}

bool UserDataTool::HasRecordKey(std::string _key)
{
	bool result = false;
	do 
	{
		if (m_intValue.find(_key) != m_intValue.end())
		{
			result = true;
			break;
		}
		if (m_doubleValue.find(_key) != m_doubleValue.end())
		{
			result = true;
			break;
		}
		if (m_stringValue.find(_key) != m_stringValue.end())
		{
			result = true;
			break;
		}
	} while (false);
	return result;
}

void UserDataTool::ReadJsonFile()
{
	if (!FileUtils::getInstance()->isFileExist(_jsonFileName))
	{
		return;
	}
	clearAllData();
	std::string strJsonTest = FileUtils::getInstance()->getStringFromFile(_jsonFileName);
	rapidjson::Document docTest;
	docTest.Parse<0>(strJsonTest.c_str());

	if (!docTest.HasParseError())
	{
		for (rapidjson::Value::ConstMemberIterator itr = docTest.MemberBegin(); itr != docTest.MemberEnd(); itr++)
		{
			rapidjson::Value jKey;
			rapidjson::Value jValue;
			rapidjson::Document::AllocatorType allocator;
			jKey.CopyFrom(itr->name, allocator);
			jValue.CopyFrom(itr->value, allocator);
			std::string _key = jKey.GetString();

			// 这边直接 设置 不用调用接口 否则会执行save浪费时间
			if (jValue.IsString())
			{
				m_stringValue[_key] = jValue.GetString();
				//SetUserData(_key, jValue.GetString());
			}
			else if (jValue.IsBool())
			{
				m_boolValue[_key] = jValue.GetBool();
			}
			else if (jValue.IsDouble())
			{
				m_doubleValue[_key] = jValue.GetDouble();
				//SetUserData(_key, jValue.GetDouble());
			}
			else if (jValue.IsInt())
			{
				m_intValue[_key] = jValue.GetInt();
				//SetUserData(_key, jValue.GetInt());
			}
			else if (ToolFunc::endWith(_key, "_intvec") && jValue.IsArray())
			{
				m_intVecValue.insert({ _key, valTo_VecInt(jValue) });
			}
			else if (ToolFunc::endWith(_key, "_intintmap"))
			{
				m_intMapValue.insert({ _key, valTo_Int_IntMap(jValue) });
			}
		}
	}
}

std::vector<int> UserDataTool::valTo_VecInt(const rapidjson::Value &_val)
{
	std::vector<int> result;
	for (int index = 0; index < _val.Size(); index++)
	{
		result.push_back(_val[index].GetInt());
	}
	return result;
}

std::map<int, int> UserDataTool::valTo_Int_IntMap(const rapidjson::Value &_val)
{
	std::map<int, int> result;
	for (rapidjson::Value::ConstMemberIterator itr = _val.MemberBegin(); itr != _val.MemberEnd(); itr++)
	{
		rapidjson::Value jKey;
		rapidjson::Value jValue;
		rapidjson::Document::AllocatorType allocator;
		jKey.CopyFrom(itr->name, allocator);
		jValue.CopyFrom(itr->value, allocator);
		std::string _key = jKey.GetString();
		if (!jValue.IsInt())
		{
			continue;
		}
		int keyint = atoi(_key.c_str());
		int valInt = jValue.GetInt();
		result.insert({ keyint, valInt });
	}
	return result;
}

void UserDataTool::clearAllData()
{
	m_intMapValue.clear();
	m_stringValue.clear();
	m_intValue.clear();
	m_boolValue.clear();
	m_intVecValue.clear();
	m_doubleValue.clear();
}

void UserDataTool::SaveJsonFile()
{
	if (!FileUtils::getInstance()->isDirectoryExist(FileUtils::getInstance()->getWritablePath() + "ypjy"))
	{
		FileUtils::getInstance()->createDirectory(FileUtils::getInstance()->getWritablePath() + "ypjy");
	}
	std::string strJson = "{}";
	std::string strJson = GetJsonString(m_intValue, m_doubleValue, m_stringValue);
	FileUtils::getInstance()->writeStringToFile(strJson, _jsonFileName);
}

std::string UserDataTool::GetJsonString(const map<std::string, int> &mInt, const map<std::string, double> &mDouble, const map<std::string, std::string> &mString)  // 注意这里的const
{
	rapidjson::Document document;
	rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
	rapidjson::Value root(rapidjson::kObjectType);
	for (map<std::string, bool>::const_iterator it = m_boolValue.begin(); it != m_boolValue.end(); ++it)
	{
		rapidjson::Value key(rapidjson::kStringType);
		key.SetString(it->first.c_str(), allocator);
		if (it->second)
		{
			rapidjson::Value value(rapidjson::kTrueType);
			root.AddMember(key, value, allocator);
		}
		else {
			rapidjson::Value value(rapidjson::kFalseType);
			root.AddMember(key, value, allocator);
		}
	}
	for (map<std::string, int>::const_iterator it = mInt.begin(); it != mInt.end(); ++it) // 注意这里要用const_iterator
	{
		rapidjson::Value key(rapidjson::kStringType);
		rapidjson::Value value(rapidjson::kStringType);
		key.SetString(it->first.c_str(), allocator);
		root.AddMember(key, it->second, allocator);
	}
	for (map<std::string, double>::const_iterator it = mDouble.begin(); it != mDouble.end(); ++it) // 注意这里要用const_iterator
	{
		rapidjson::Value key(rapidjson::kStringType);
		rapidjson::Value value(rapidjson::kStringType);
		key.SetString(it->first.c_str(), allocator);
		value.SetDouble(it->second);
		root.AddMember(key, value, allocator);
	}
	for (map<std::string, string>::const_iterator it = mString.begin(); it != mString.end(); ++it) // 注意这里要用const_iterator
	{
		rapidjson::Value key(rapidjson::kStringType);
		rapidjson::Value value(rapidjson::kStringType);
		key.SetString(it->first.c_str(), allocator);
		value.SetString(it->second.c_str(), allocator);
		root.AddMember(key, value, allocator);
	}
	for (auto _item : m_intMapValue)
	{
		rapidjson::Value child(rapidjson::kObjectType);
		rapidjson::Value childKey(rapidjson::kStringType);
		for (auto _mapItem : _item.second)
		{
			std::string _key = std::to_string(_mapItem.first);
			rapidjson::Value key(rapidjson::kStringType);
			key.SetString(_key.c_str(), allocator);
			child.AddMember(key, _mapItem.second, allocator);
		}
		childKey.SetString(_item.first.c_str(), allocator);
		root.AddMember(childKey, child, allocator);
	}
	for (auto _item : m_intVecValue)
	{
		rapidjson::Value child(rapidjson::kArrayType);
		rapidjson::Value childkey(rapidjson::kStringType);
		for (auto _vecItem : _item.second)
		{
			child.PushBack(_vecItem, allocator);
		}
		childkey.SetString(_item.first.c_str(), allocator);
		root.AddMember(childkey, child, allocator);
	}
	rapidjson::StringBuffer buffer;
	rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer);
	root.Accept(writer);
	return buffer.GetString();
}

这里维护的是一个Json文件,接口名就是其功能
相比UserDefalut都能保存int、bool、string和double,我这个额外可使用std::vector<int>std::map<int, int>

  • Singleton是一个单例模板类,因为这个模板类不是我写的我就不贴了,不过不继承这个单例模板完全可以,只要自己手写单例就行
  • endWith方法在最上面的工具类中有写

配合Json存储使用的测试方法

如果游戏数据不是从服务器中拉取,而且弱联网甚至不联网的小游戏,那么一些本地存储数据是必须的。比如用户金币数目、当前游玩关卡、解锁任务等数据需要存储在本地,但是这些数据所在的文件在手机上是不可见的,这就需要一台小型服务器(tomcat或者IIS都行)来获取手机存储的文件内容

static void sendJsonConfigToServer()
{
	auto timeSturct = getTodayTimeString();
	std::string _key = "ypjy_";
	std::string _val = FileUtils::getInstance()->getStringFromFile(FileUtils::getInstance()->getWritablePath() + "ypjy/ypjyUserDataToolJson.json");
	std::string ipAndPort = "10.10.8.23:81";
	std::string requestPath = "http://" + ipAndPort + "/SitServer.ashx";

	auto request = new cocos2d::network::HttpRequest();
	// 设置请求连接
	request->setUrl(requestPath.c_str());
	// 设置请求方式
	request->setRequestType(cocos2d::network::HttpRequest::Type::POST);
	std::string data = "param=updateConfig&key=" + _key + "&val=" + _val;
	request->setRequestData(data.c_str(), data.size());
	request->setResponseCallback([=](cocos2d::network::HttpClient* client, cocos2d::network::HttpResponse *response) {});
	// 获取唯一的clien实例
	auto client = cocos2d::network::HttpClient::getInstance();
	// 发送请求
	client->send(request);

	request->release();
}

使用cocos2dx的原生网络通信API,将数据发送到服务器中

static void saveJsonConfigToLocal(std::function<void(std::string)> _callFunc)
{
	std::string requestPath = "http://10.10.8.23:81/SitServer.ashx?param=ypjy_";

	auto request = new cocos2d::network::HttpRequest();
	// 设置请求连接
	request->setUrl(requestPath.c_str());
	// 设置请求方式
	request->setRequestType(cocos2d::network::HttpRequest::Type::GET);
	request->setResponseCallback([=](cocos2d::network::HttpClient* client, cocos2d::network::HttpResponse *response) {
		if (response->isSucceed())
		{
			std::vector<char> *data = response->getResponseData();
			std::stringstream oss;
			for (int i = 0; i < data->size(); i++)
			{
				oss << (*data)[i];
			}
			std::string getVal = oss.str().c_str();
			_callFunc(getVal);
		}
		else {
		
		}
	});
	// 获取唯一的clien实例
	auto client = cocos2d::network::HttpClient::getInstance();
	// 发送请求
	client->send(request);

	request->release();
}

从服务器中拉取数据并写回存储文件,从而实现不用重新安装包也可以刷新本地存储的功能,并且存档的值可以随意修改(比如给很多金币等)

我这里使用的最简易的.Net做服务器后台,前端是原生html + javaescript,因为主要是一个数据展示和写入的文本框,所以很简单,后台数据存储就是单纯的json文件,存储键值对罢了

在这里插入图片描述

发送数据通过设置key来修改value
获得数据也是通过key来拉取value

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Newtonsoft.Json;

namespace FireConfig
{
    public class ConfigItem
    {
        public string key { get; set; }
        public string val { get; set; }
    }
    /// <summary>
    /// SitServer 的摘要说明
    /// </summary>
    public class SitServer : IHttpHandler
    {
        public string getConfigFileString()
        {
            string path = HttpContext.Current.Server.MapPath("~/allConfig.json");
            string jsonString = System.IO.File.ReadAllText(path);
            return jsonString;
        }

        public void setConfigFileString(string val)
        {
            string path = HttpContext.Current.Server.MapPath("~/allConfig.json");
            System.IO.File.WriteAllText(path, val);
        }

        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";
            string jsonString = getConfigFileString();
            List<ConfigItem> items = JsonConvert.DeserializeObject<List<ConfigItem>>(jsonString);
            string resutl = "";                         // 定义返回值
            string param = context.Request["param"];    // 获取参数
            if ("GetAll" == param) // 获得全部key-value值
            {
                resutl = jsonString;
            }
            else if ("updateConfig" == param)
            {
                string _key = context.Request.Form["key"];
                string _val = context.Request.Form["val"];
                if (_key != "" && _val != "")
                {
                    updateConfig(_key, _val);
                }

                resutl = "{\"status\":\"success\"}";
            }
            else if ("removeConfig" == param)
            {
                string _key = context.Request.Form["key"];
                if (_key != "")
                {
                    removeConfig(_key);
                }

                resutl = "{\"status\":\"success\"}";
            }
            else if ("addConfig" == param)
            {
                string _key = context.Request.Form["key"];
                string _val = context.Request.Form["val"];
                if (_key != "" && _val != "")
                {
                    addConfig(_key, _val);
                }

                resutl = "{\"status\":\"success\"}";
            }
            else if ("resetAll" == param)
            {
                resetAllVal();
                resutl = "{\"status\":\"success\"}";
            }
            else
            {
                resutl = getKeyValue(param);
            }
            context.Response.Write(resutl);
        }

        public void updateConfig(string _key, string _val)
        {
            List<ConfigItem> items = JsonConvert.DeserializeObject<List<ConfigItem>>(getConfigFileString());
            foreach (var item in items)
            {
                if (item.key == _key)
                {
                    item.val = _val;
                }
            }
            string newJsonString = JsonConvert.SerializeObject(items);
            setConfigFileString(newJsonString);
        }

        public void removeConfig(string _key)
        {
            List<ConfigItem> items = JsonConvert.DeserializeObject<List<ConfigItem>>(getConfigFileString());
            List<ConfigItem> newItems = new List<ConfigItem>();
            foreach(var item in items)
            {
                if(item.key != _key)
                {
                    newItems.Add(item);
                }
            }
            string newJsonString = JsonConvert.SerializeObject(newItems);
            setConfigFileString(newJsonString);
        }

        public void addConfig(string _key, string _val)
        {
            List<ConfigItem> items = JsonConvert.DeserializeObject<List<ConfigItem>>(getConfigFileString());
            ConfigItem item = new ConfigItem();
            item.key = _key;
            item.val = _val;
            items.Add(item);
            string newJsonString = JsonConvert.SerializeObject(items);
            setConfigFileString(newJsonString);
        }

        public string getKeyValue(string _key)
        {
            List<ConfigItem> items = JsonConvert.DeserializeObject<List<ConfigItem>>(getConfigFileString());
            foreach (var item in items)
            {
                if (item.key == _key)
                {
                    return item.val;
                }
            }
            return "";
        }

        public void resetAllVal()
        {
            string path = HttpContext.Current.Server.MapPath("~/allConfig.json");
            System.IO.File.WriteAllText(path, "[{\"key\":\"\",\"val\":\"\"}]");
        }
        
        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

上面是.Net后台的处理请求代码

// index.html
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>自定义在线配置拉取</title>

    <!-- Bootstrap -->
    <link href="./Content/bootstrap.css" rel="stylesheet">

    <!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
    <script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>

    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

    <!-- 可选的 Bootstrap 主题文件(一般不用引入) -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">

    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>

<body>
    <table class="table table-striped">
        <thead>
            <tr>
                <th>key值</th>
                <th>value值</th>
                <th>操作</th>
            </tr>
            <tr>
                <th>
                    <input type="text" placeholder="key值" id="addKey">
                </th>
                <th>
                    <input type="text" placeholder="value值" id="addValue">
                </th>
                <th>
                    <input value="添加" type="button" id="addItem" onclick="createByAddBtn()">
                    <input value="重置所有" type="button" id="addItem" onclick="resetAllKeyVal()">
                </th>
            </tr>
        </thead>
        <tbody id="configShowTable">

        </tbody>
    </table>
</body>
<script>
    $(function () {
        getConfig();
    })
    function getConfig()
    {
        $.ajax({
            url: "SitServer.ashx?param=GetAll",
            dataType: "json",
            success: function (result) {
                updateTableInfo(result);
            }
        });
    }

    function setFireConfigFile(key, val) {
        key = $.trim(key);
        val = $.trim(val);
        $.ajax({
            url: "SitServer.ashx?param=updateConfig",
            dataType: "json",
            type: "post",
            data: { "key": key, "val": val },
            success: function (result) {
                getConfig()
            },
            error : function(e)
            {
                console.log(e);
            }
        });
    }

    function setFireConfigBtn(keyId, valId)
    {
        var key = $("#" + keyId).text();
        var val = $("#" + valId).val();
        key = $.trim(key);
        val = $.trim(val);
        setFireConfigFile(key, val);
    }

    function removeConfigFile(keyId) {
        var key = $("#" + keyId).text();
        $.ajax({
            url: "SitServer.ashx?param=removeConfig",
            dataType: "json",
            type: "post",
            data: { "key": key },
            success: function (result) {
                getConfig()
            },
            error: function (e) {
                console.log(e);
            }
        });
    }

    function addConfigFile(key, val) {
        key = $.trim(key);
        val = $.trim(val);
        sendData = { "key": key, "val": val };
        console.log(sendData);
        $.ajax({
            url: "SitServer.ashx?param=addConfig",
            dataType: "json",
            type : "post",
            data: sendData,
            success: function (result) {
                getConfig()
            },
            error: function (e) {
                console.log(e);
            }
        });
    }

    function createByAddBtn()
    {
        var key = $("#addKey").val();
        var val = $("#addValue").val();
        key = $.trim(key);
        val = $.trim(val);
        $("#addKey").val("");
        $("#addValue").val("");
        addConfigFile(key, val);
    }

    function updateTableInfo(getJson) {
        $("#configShowTable").empty();
        console.log(getJson);
        for (var i = 0; i < getJson.length; i++) {
            var key = $.trim(getJson[i]["key"]);
            var val = $.trim(getJson[i]["val"]);
            var tr = $("<tr></tr>");
            var th1 = $("<th><span id='" + "keyID" + i + "'>" + key + "</span></th>");
            var th2 = $("<th><input type='text' value='" + val + "'id='" + "valID" + i + "'></th>");
            var updateBtn = $("<input type='button' value='更新' οnclick='setFireConfigBtn(\"" + "keyID" + i + "\",\"" + "valID" + i + "\")'>");
            var deleteBtn = $("<input type='button' value='删除' οnclick='removeConfigFile(\"" + "keyID" + i + "\")'>");
            var th3 = $("<th></th>");
            th3.append(updateBtn);
            th3.append(deleteBtn);
            tr.append(th1);
            tr.append(th2);
            tr.append(th3)

            $("#configShowTable").append(tr);
        }
    }

    function resetAllKeyVal()
    {
        $.ajax({
            url: "SitServer.ashx?param=resetAll",
            dataType: "json",
            type: "get",
            success: function (result) {
                getConfig()
            },
            error: function (e) {
                console.log(e);
            }
        });
    }
</script>
</html>

前端显示代码

Tip
如果数据拉取不到或者404,尝试看看windows防火墙有没有开放对应端口

配置文件

游戏都有其配置文件,作为MCVS中的model,其被service调用和处理,作为Controller的数据处理来源和View的数据显示来源
我自己处理数据从最开始的按固定数据格式从txt中读取数据,到从json读取数据,到现在自动化数据读取

在这里插入图片描述

  • 自己定义数据结构和读取方式,虽然比较灵活自由,但是自己编写大量数据还要自己解析这些数据会死人的

在这里插入图片描述

  • 自己编写json文件和读写方法,虽然美观了不少但是数据量大的话手写很困难

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • Excel通过限制数据位置来逆向生成对应的json、结构体、读取方法。在保证灵活性的前提下提升了生成速率,减少了工作量

这个工具的代码比较多,需要可以私聊。当然自己写也很快,我自己是QT和pyqt都实现过

多语言处理

cocos2dx做中文的时候,在win32上会乱码,所以一般需要u8"中文"这种写法,或者将中文内容写入到文本(xml、json)中,代码中的数据从文本中获得
然后为了在尽量少的修改代码的情况下实现多国语言的切换,也需要将不同国家的文本写入到文本(xml、json),然后根据默认语言和用户选择语言从文本中读取对应内容

借由上述的Json转换工具,可以很容易的多国语言存储到一个文件中

在这里插入图片描述

在这里插入图片描述
当语言表生成完毕之后,使用IDZH_textJA_text可以唯一的获取指定文本内容并设置到游戏的Labeltext

UI管理器

游戏是有分层结构的,游戏场景自然是最底层,UI在场景之上,二级弹框在UI层之上,三级弹框在二级弹框之上,消息弹框在最顶层。而之前提到的层都加载到Scene上,

这里引入一个UI管理器,专门用来管理这种。这个UI管理器为一个单例模式,独立存在,其包含几个cocos2d::Node节点,这几个节点的父节点为当前的Scene场景,并且这几个Node的层级互不相同。

//所有的界面 枚举
namespace ypjyUIEnum
{
	static std::string GameLayer = "GameLayer";
	static std::string GameUILayer = "GameUILayer";
};

//界面传递的参数
union UI_Args
{
	int			v_int;
	UI_Args(int i) {
		v_int = i;
	}
};

class MyUIManager : public Singleton<MyUIManager>
{
public:
	// 初始化
	void initUIManager();
	//打开关闭UI
	cocos2d::Layer* addToSecondStage(const std::string &UIname, UI_Args* uiargs = nullptr);
	cocos2d::Layer* addToThirdStage(const std::string &UIname, UI_Args* uiargs = nullptr);
	cocos2d::Layer* addToMainLandStage(const std::string &UIname, UI_Args* uiargs = nullptr);
	cocos2d::Layer* addToMessageStage(const std::string &UIname, UI_Args* uiargs = nullptr);
	cocos2d::Layer* addToUIStage(const std::string &UIname, UI_Args* uiargs = nullptr);
	cocos2d::Layer* addToEffectStage(const std::string &UIname, UI_Args* uiargs = nullptr); 
	cocos2d::Layer* addToParent(cocos2d::Node* parent, std::string UIname, UI_Args* uiargs = nullptr);

	//对应UI只打开一次
	cocos2d::Layer* addToSecondStageOnce(const std::string &UIname, UI_Args* uiargs = nullptr);
	cocos2d::Layer* addToThirdStageOnce(const std::string &UIname, UI_Args* uiargs = nullptr);
	cocos2d::Layer* addToMainLandStageOnce(const std::string &UIname, UI_Args* uiargs = nullptr);
	cocos2d::Layer* addToMessageStageOnce(const std::string &UIname, UI_Args* uiargs = nullptr);
	cocos2d::Layer* addToUIStageOnce(const std::string &UIname, UI_Args* uiargs = nullptr);
	cocos2d::Layer* addToEffectStageOnce(const std::string &UIname, UI_Args* uiargs = nullptr);
	cocos2d::Layer* addToParentOnce(cocos2d::Node* parent, std::string UIname, UI_Args* uiargs = nullptr);
	// 关闭UI
	void CloseUI(std::string UIname);

	//是否存在UI(在场景中)
	cocos2d::Layer* existUI_SecondStage(const std::string &UIname);
	cocos2d::Layer* existUI_ThirdStage(const std::string &UIname);
	cocos2d::Layer* existUI_MainLandStage(const std::string &UIname);
	cocos2d::Layer* existUI_MessageStage(const std::string &UIname);
	cocos2d::Layer* existUI_UIStage(const std::string &UIname);
	cocos2d::Layer* existUI_EffectStage(const std::string &UIname);
	cocos2d::Layer* existUI(const std::string& UIname);

	//显示隐藏
	void HideUI(const std::string &UIname);
	void ShowUI(const std::string &UIname);
	void FadeOutUI(const std::string &UIname);

protected:
	cocos2d::Node* getLevelNode(const std::string &nodename, int zOrder = 0);
private:
	Scene* getRunningScene();
};

void MyUIManager::initUIManager()
{

}

cocos2d::Layer * MyUIManager::addToSecondStage(const std::string & UIname, UI_Args * uiargs)
{
	auto _node = getLevelNode("SecondStage_Node", 10);
	return addToParent(_node, UIname, uiargs);
}

cocos2d::Layer * MyUIManager::addToThirdStage(const std::string & UIname, UI_Args * uiargs)
{
	auto _node = getLevelNode("ThirdStage_Node", 15);
	return addToParent(_node, UIname, uiargs);
}

cocos2d::Layer * MyUIManager::addToMainLandStage(const std::string & UIname, UI_Args * uiargs)
{
	auto _node = getLevelNode("MainLand_Node", 1);
	return addToParent(_node, UIname, uiargs);
}

cocos2d::Layer * MyUIManager::addToMessageStage(const std::string & UIname, UI_Args * uiargs)
{
	auto _node = getLevelNode("MessageStage_Node", 50);
	return addToParent(_node, UIname, uiargs);
}

cocos2d::Layer * MyUIManager::addToUIStage(const std::string & UIname, UI_Args * uiargs)
{
	auto _node = getLevelNode("UIStage_Node", 5);
	return addToParent(_node, UIname, uiargs);
}

cocos2d::Layer * MyUIManager::addToEffectStage(const std::string & UIname, UI_Args * uiargs)
{
	auto _node = getLevelNode("EffectStage_Node", 3);
	return addToParent(_node, UIname, uiargs);
}

cocos2d::Layer * MyUIManager::addToParent(cocos2d::Node * parent, std::string UIname, UI_Args * uiargs)
{
	if (parent == nullptr)
	{
		return nullptr;
	}
	ypjyBaseLayer* layer = nullptr;
	if (ypjyUIEnum::GameLayer == UIname)
	{
		layer = MyGameLayer::create();
		parent->addChild(layer);
	}
	else if (ypjyUIEnum::GameUILayer == UIname)
	{
		layer = MyGameUILayer::create();
		parent->addChild(layer);
		if (nullptr != uiargs) {
			static_cast<ypjyCheapSalePopupLayer*>(layer)->setSaleId(uiargs->v_int);
		}
	}
	if (layer != nullptr)
	{	//设置节点名
		layer->setName(UIname);
	}

	__FireEvent(ypjyEventsNameManage::OpenLayer, &UIname);
	return layer;
}

cocos2d::Layer * MyUIManager::addToSecondStageOnce(const std::string & UIname, UI_Args * uiargs)
{
	auto _layer = existUI_SecondStage(UIname);
	if (_layer)
	{
		return _layer;
	}
	return addToSecondStage(UIname, uiargs);
}

cocos2d::Layer * MyUIManager::addToThirdStageOnce(const std::string & UIname, UI_Args * uiargs)
{
	auto _layer = existUI_ThirdStage(UIname);
	if (_layer)
	{
		_layer->setVisible(true);
		return _layer;
	}
	return addToThirdStage(UIname, uiargs);
}

cocos2d::Layer * MyUIManager::addToMainLandStageOnce(const std::string & UIname, UI_Args * uiargs)
{
	auto _layer = existUI_MainLandStage(UIname);
	if (_layer)
	{
		_layer->setVisible(true);
		return _layer;
	}
	return addToMainLandStage(UIname, uiargs);
}

cocos2d::Layer * MyUIManager::addToMessageStageOnce(const std::string & UIname, UI_Args * uiargs)
{
	auto _layer = existUI_MessageStage(UIname);
	if (_layer)
	{
		_layer->setVisible(true);
		return _layer;
	}
	return addToMessageStage(UIname, uiargs);
}

cocos2d::Layer * MyUIManager::addToUIStageOnce(const std::string & UIname, UI_Args * uiargs)
{
	auto _layer = existUI_UIStage(UIname);
	if (_layer)
	{
		_layer->setVisible(true);
		return _layer;
	}
	return addToUIStage( UIname, uiargs);
}

cocos2d::Layer * MyUIManager::addToEffectStageOnce(const std::string & UIname, UI_Args * uiargs)
{
	auto _layer = existUI_EffectStage(UIname);
	if (_layer)
	{
		_layer->setVisible(true);
		return _layer;
	}
	return addToEffectStage(UIname, uiargs);
}

cocos2d::Layer * MyUIManager::addToParentOnce(cocos2d::Node * parent, std::string UIname, UI_Args * uiargs)
{
	auto _layer = dynamic_cast<cocos2d::Layer*>(parent->getChildByName(UIname));
	if (_layer)
	{
		_layer->setVisible(true);
		return _layer;
	}
	return addToParent(parent, UIname, uiargs);
}

cocos2d::Layer * MyUIManager::existUI_SecondStage(const std::string & UIname)
{
	auto _scene = getRunningScene();
	auto _node = _scene->getChildByName("SecondStage_Node");
	if (_node)
	{
		return dynamic_cast<cocos2d::Layer*>(_node->getChildByName(UIname));
	}
	return nullptr;
}

cocos2d::Layer * MyUIManager::existUI_ThirdStage(const std::string & UIname)
{
	auto _scene = getRunningScene();
	auto _node = _scene->getChildByName("ThirdStage_Node");
	if (_node)
	{
		return dynamic_cast<cocos2d::Layer*>(_node->getChildByName(UIname));
	}
	return nullptr;
}

cocos2d::Layer * MyUIManager::existUI_MainLandStage(const std::string & UIname)
{
	auto _scene = getRunningScene();
	auto _node = _scene->getChildByName("MainLand_Node");
	if (_node)
	{
		return dynamic_cast<cocos2d::Layer*>(_node->getChildByName(UIname));
	}
	return nullptr;
}

cocos2d::Layer * MyUIManager::existUI_MessageStage(const std::string & UIname)
{
	auto _scene = getRunningScene();
	auto _node = _scene->getChildByName("MessageStage_Node");
	if (_node)
	{
		return dynamic_cast<cocos2d::Layer*>(_node->getChildByName(UIname));
	}
	return nullptr;
}

cocos2d::Layer * MyUIManager::existUI_UIStage(const std::string & UIname)
{
	auto _scene = getRunningScene();
	auto _node = _scene->getChildByName("UIStage_Node");
	if (_node)
	{
		return dynamic_cast<cocos2d::Layer*>(_node->getChildByName(UIname));
	}
	return nullptr;
}

cocos2d::Layer * MyUIManager::existUI_EffectStage(const std::string & UIname)
{
	auto _scene = getRunningScene();
	auto _node = _scene->getChildByName("EffectStage_Node");
	if (_node)
	{
		return dynamic_cast<cocos2d::Layer*>(_node->getChildByName(UIname));
	}
	return nullptr;
}

cocos2d::Layer* MyUIManager::existUI(const std::string & UIname)
{
	cocos2d::Layer* _uiLayer = nullptr;
	do 
	{
		_uiLayer = existUI_MainLandStage(UIname);
		if (_uiLayer)
		{
			break;
		}
		_uiLayer = existUI_SecondStage(UIname);
		if (_uiLayer)
		{
			break;
		}
		_uiLayer = existUI_ThirdStage(UIname);
		if (_uiLayer)
		{
			break;
		}
		_uiLayer = existUI_MessageStage(UIname);
		if (_uiLayer)
		{
			break;
		}
		_uiLayer = existUI_UIStage(UIname);
		if (_uiLayer)
		{
			break;
		}
	} while (false);
	return _uiLayer;
}

void MyUIManager::HideUI(const std::string & UIname)
{
	auto _uiLayer = existUI(UIname);
	if (_uiLayer)
	{
		_uiLayer->setVisible(false);
	}
}

void MyUIManager::ShowUI(const std::string & UIname)
{
	auto _uiLayer = existUI(UIname);
	if (_uiLayer)
	{
		_uiLayer->setVisible(true);
	}
}

void MyUIManager::FadeOutUI(const std::string & UIname)
{
	auto _uiLayer = dynamic_cast<ypjyBaseLayer*>(existUI(UIname));
	if (_uiLayer)
	{
		_uiLayer->action_fadeOut();
	}
}

void MyUIManager::CloseUI(std::string UIname)
{
	auto _scene = getRunningScene();
	auto _specialLayer =dynamic_cast<ypjyBaseLayer*>( _scene->getChildByName(UIname));
	if (_specialLayer)
	{
		_specialLayer->Action_onExit();
		return;
	}
	std::vector<std::string> _nodesname = {"EffectStage_Node", "ThirdStage_Node", "MainLand_Node", "MessageStage_Node", "SecondStage_Node", "UIStage_Node"};
	for (auto _name : _nodesname)
	{
		auto _node = _scene->getChildByName(_name);
		if (_node == nullptr)
		{
			continue;
		}
		auto _layer = dynamic_cast<ypjyBaseLayer*>(_node->getChildByName(UIname));
		if (_layer)
		{
			CCLOG("%s -> %s", _name.c_str(), UIname.c_str());
			_layer->Action_onExit();
			__FireEvent(ypjyEventsNameManage::CloseLayer, &UIname);
		}
	}
}

 cocos2d::Node * MyUIManager::getLevelNode(const std::string & nodename, int zOrder)
 {
	 auto _scene = getRunningScene();
	 if (_scene == nullptr)
	 {
		 return nullptr;
	 }
	 auto _node = _scene->getChildByName(nodename);
	 if (_node == nullptr)
	 {
		 _node = cocos2d::Node::create();
		 _node->setName(nodename);
		 _scene->addChild(_node, zOrder);
	 }
	 return _node;
 }

 Scene* MyUIManager::getRunningScene()
{
	auto s = Director::getInstance()->getRunningScene();
	if (s) {
		return s;
	}
	return nullptr;
}

在游戏中所有的弹窗层级添加都通过UI管理器来添加,而不是直接添加到场景中,这样集中统一的管理层级有很多好处

  1. 传入名字和参数,直接根据名字创建layer。当前的操作的layer不用知道怎么创建的细节,并且不用导入头文件,减少代码的耦合
  2. layer统一管理,当前场景有哪些layer,有哪些二级弹窗和三级弹窗,可以集中处理。比如当存在三级弹窗的时候隐藏所有二级弹窗,当三级弹窗关闭的时候显示二级弹窗。这些原本需要复杂判断的功能,在UI管理中很容易就能实现
  3. 很容易就能判断界面是否存在

但是上面的代码也有一些缺陷

  1. 所有的界面都需要在addToParent方法中通过名字枚举并写明创建方法,这在大量layer的游戏中是十分繁琐,并且麻烦的。试想,如果当前游戏有一百个弹窗,岂不是要写至少三百行的创建代码。(如果C++支持反射就好了)
  2. 上述代码十分简陋,仅包含几种功能,勉强满足小游戏的管理需求。想要适配不同项目,还需要累加代码功能
  3. 该UI管理器不支持切换Scene,也就是说所有的场景切换、弹窗都在同一个Scene上进行,虽然小游戏没有什么问题,但是对于大游戏来说不切换场景是十分困难的
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值