都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;
}
};
这里提供创建Sprite
、Label
、Text
、ImageView
、Button
创建方法原因有两点
- 减少重复代码的编写,设置坐标、名字、父节点等参数的调用是很频繁的,只用一行可以设置很多必须的参数
- 若需要对这些基础节点做统一处理可以直接在这些函数中处理,而不用在工程所有创建的地方一个一个处理
最理想的状况是,对于所有的基础节点建议是创建自己的类去继承基础节点类型,这样加减功能、统一处理都很方便
一些用法
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 :=1
和USE_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
最近使用发现问题:
- 添加自定义节点时,在设置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转换工具,可以很容易的多国语言存储到一个文件中
当语言表生成完毕之后,使用ID
和ZH_text
或JA_text
可以唯一的获取指定文本内容并设置到游戏的Label
或text
中
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管理器来添加,而不是直接添加到场景中,这样集中统一的管理层级有很多好处
- 传入名字和参数,直接根据名字创建layer。当前的操作的layer不用知道怎么创建的细节,并且不用导入头文件,减少代码的耦合
- layer统一管理,当前场景有哪些layer,有哪些二级弹窗和三级弹窗,可以集中处理。比如当存在三级弹窗的时候隐藏所有二级弹窗,当三级弹窗关闭的时候显示二级弹窗。这些原本需要复杂判断的功能,在UI管理中很容易就能实现
- 很容易就能判断界面是否存在
但是上面的代码也有一些缺陷
- 所有的界面都需要在
addToParent
方法中通过名字枚举并写明创建方法,这在大量layer的游戏中是十分繁琐,并且麻烦的。试想,如果当前游戏有一百个弹窗,岂不是要写至少三百行的创建代码。(如果C++支持反射就好了) - 上述代码十分简陋,仅包含几种功能,勉强满足小游戏的管理需求。想要适配不同项目,还需要累加代码功能
- 该UI管理器不支持切换Scene,也就是说所有的场景切换、弹窗都在同一个Scene上进行,虽然小游戏没有什么问题,但是对于大游戏来说不切换场景是十分困难的