用qt读写操作cocos studio UI数据文件(xml)

qt是一个功能很强大的c++库,其中就支持xml文件的读写,今天我们就Dom的方式来读写cocos studio 的ui数据文件(还有一种SAX方式)
cocos studio ui数据文件(及时.csd格式的)就是一个标准的xml文档,其描述的cocos2d-x引擎中的所用ui元素及其相关属性,我们就用qt读取并操作csd文件,将所有的text UI元素都变成有黑色描边像素为2的字体
下面展示一个目标文件
[xml]
<GameProjectFile>
<PropertyGroup Type="Layer" Name="l_rolename" ID="f6121bb9-8449-4d10-a3ec-ac853464229a" Version="2.3.2.3" />
<Content ctype="GameProjectContent">
<Content>
<Animation Duration="0" Speed="1.0000" />
<ObjectData Name="Layer" Tag="4278" ctype="GameLayerObjectData">
<Size X="72.0000" Y="24.0000" />
<Children>
<AbstractNodeData Name="ListView" ActionTag="1039694078" Tag="294" IconVisible="False" RightMargin="-128.0000" TopMargin="-8.0000" BackColorAlpha="102" ColorAngle="90.0000" ScrollDirectionType="0" ctype="ListViewObjectData">
<Size X="200.0000" Y="32.0000" />
<Children>
<AbstractNodeData Name="Text_Name" ActionTag="-1681367417" Tag="4279" IconVisible="False" RightMargin="152.0000" TopMargin="12.0000" FontSize="24" LabelText="名字" HorizontalAlignmentType="HT_Center" VerticalAlignmentType="VT_Bottom" OutlineSize="2" OutlineEnabled="True" ShadowOffsetX="0.0000" ShadowOffsetY="0.0000" ctype="TextObjectData">
<Size X="50.0000" Y="32.0000" />
<AnchorPoint />
<Position />
<Scale ScaleX="1.0000" ScaleY="1.0000" />
<CColor A="255" R="255" G="255" B="255" />
<PrePosition />
<PreSize X="0.0000" Y="0.0000" />
<FontResource Type="Normal" Path="fonts/wqyhei.ttf" Plist="" />
<OutlineColor A="255" R="0" G="0" B="0" />
<ShadowColor A="255" R="110" G="110" B="110" />
</AbstractNodeData>
<AbstractNodeData Name="Text_Add" ActionTag="150656575" ZOrder="1" Tag="295" IconVisible="False" LeftMargin="48.0000" RightMargin="134.0000" TopMargin="1.0000" FontSize="24" LabelText="+" OutlineSize="2" OutlineEnabled="True" ShadowOffsetX="0.0000" ShadowOffsetY="0.0000" ctype="TextObjectData"><!– 目标ui元素 TextObjectData–>
<Size X="18.0000" Y="32.0000" />
<AnchorPoint ScaleX="0.5000" ScaleY="0.5000" />
<Position X="59.0000" Y="16.0000" />
<Scale ScaleX="1.0000" ScaleY="1.0000" />
<CColor A="255" R="255" G="255" B="255" />
<PrePosition X="0.2850" Y="0.4875" />
<PreSize X="0.0000" Y="0.0000" />
<FontResource Type="Normal" Path="fonts/wqyhei.ttf" Plist="" />
<OutlineColor A="255" R="0" G="0" B="0" />
<ShadowColor A="255" R="110" G="110" B="110" />
</AbstractNodeData>
<AbstractNodeData Name="Text_Stage" ActionTag="-1707735990" ZOrder="2" Tag="4280" IconVisible="False" LeftMargin="66.0000" RightMargin="119.0000" TopMargin="8.0000" FontSize="27" LabelText="1" HorizontalAlignmentType="HT_Center" VerticalAlignmentType="VT_Center" OutlineSize="2" OutlineEnabled="True" ShadowOffsetX="0.0000" ShadowOffsetY="0.0000" ctype="TextObjectData">
<Size X="13.0000" Y="35.0000" />
<AnchorPoint />
<Position X="68.0000" Y="-3.0000" />
<Scale ScaleX="1.0000" ScaleY="1.0000" />
<CColor A="255" R="255" G="255" B="255" />
<PrePosition X="0.3300" />
<PreSize X="0.0000" Y="0.0000" />
<FontResource Type="Normal" Path="fonts/wqyhei.ttf" Plist="" />
<OutlineColor A="255" R="0" G="0" B="0" />
<ShadowColor A="255" R="110" G="110" B="110" />
</AbstractNodeData>
</Children>
<AnchorPoint />
<Position />
<Scale ScaleX="1.0000" ScaleY="1.0000" />
<CColor A="255" R="255" G="255" B="255" />
<PrePosition />
<PreSize X="2.7778" Y="1.3333" />
<SingleColor A="255" R="150" G="150" B="255" />
<FirstColor A="255" R="150" G="150" B="255" />
<EndColor A="255" R="255" G="255" B="255" />
<ColorVector ScaleY="1.0000" />
</AbstractNodeData>
</Children>
</ObjectData>
</Content>
</Content>
</GameProjectFile>
[/xml]

在这段文本中 ctype=”TextObjectData” 的元素的就是我们的要找到并修改的ui元素

我们建个qt ui 工程。在.pro文件中添加 QT += xml 引入xml解析库

[cpp]
#include"mainwindow.h"
#include"ui_mainwindow.h"
MainWindow::MainWindow(QWidget*parent):
QMainWindow(parent),
ui(newUi::MainWindow)
{
ui->setupUi(this);
this->ShowFiles(1,"D:/qtworkspace/change_text_outline/cocosstudio"); //传入所有csd文件路径
}

MainWindow::~MainWindow()
{
deleteui;
}

boolMainWindow::matchLine(QStringpath)
{
bool ret=false;
QFile file(path);
if(!file.open(QIODevice::ReadWrite|QIODevice::Text))
{
return1;
}

QTextStream in(&file);//文本流操作

QString line=in.readAll();//读取到字符串
QDomDocument document;//dom 对象
QString strError;
int errLin=0,errCol=0;
if(!document.setContent(line,false,&strError,&errLin,&errCol))//设置dom内容,并做失败处理
{
printf("parsefilefailedatline%dcolumn%d,error:%s!\n",errLin,errCol,strError);
return false;
}

if(document.isNull())
{
printf("documentisnull!\n");
return false;
}

QDomElement root=document.documentElement();

this->changeItem(root);

QFile f(path);//重新打开文件将更改后的文本写入
if(!f.open(QFile::WriteOnly|QFile::Text))
return false;
QTextStream out(&f);
document.save(out,4);//写入保存
f.close();

file.close();
return ret;
}
voidMainWindow::changeItem(QDomElement&elementRef)//查找目标节点并更改属性
{
QDomElement element=elementRef.firstChildElement();//获取该节点的第一个子节点
while(!element.isNull()){//遍历子节点
if(element.hasAttribute("ctype"))
{
if(element.attributeNode("ctype").value().toStdString()=="TextObjectData") //找到ctype == TextObjectData的节点
{
this->isChange=true;
qDebug()<<element.attributeNode("ctype").value().toStdString().c_str();
element.setAttribute("OutlineSize",2);
element.setAttribute("OutlineEnabled","True");
QDomElement nodec=element.namedItem("OutlineColor").toElement(); //获取到描边属性节点
if(!nodec.isNull())
{
nodec.setAttribute("A",255); //设置描边颜色
nodec.setAttribute("R",0);
nodec.setAttribute("B",0);
nodec.setAttribute("G",0);
}
}
}
this->changeItem(element);//因为ui控有可能是嵌套的所以递归处理子节点
element=element.nextSiblingElement();
}

}
voidMainWindow::ShowFiles(intlevel,constQString&path)//递归遍历文件夹及其下面的文件
{
//这个函数可以执行任何任务,
//这里只是简单地输出各个文件(夹)的名字
QDirdir(path);
QStringListlist;
QStringList::Iteratoriter;
QStringtemp(level,’-‘);

list=dir.entryList(QDir::Dirs,QDir::Name);
for(iter=list.begin();iter!=list.end();++iter)
{
if("."==*iter||
".."==*iter)
continue;
//qDebug()<<temp+*iter+"(folder)";
ShowFiles(level+1,path+"/"+*iter);
}

list=dir.entryList(QDir::Files,QDir::Name);
for(iter=list.begin();iter!=list.end();++iter)
{

QStringtempstr=path+"/"+*iter;
//this->labelOut->setText(tempstr);

//this->labelOut->setText(this->strOut);
if(tempstr.right(3)=="csd")
{
this->isChange=false;
matchLine(tempstr);
if(this->isChange)
{
qDebug()<<temp+path+"/"+*iter;
}
}
}
}

[/cpp]
这样我们就很方便的把所有的csd文件做了处理。理论上我们可以做很多处理,比如我们和以自动生成一些我们想要的ui模板等等,我们可以尽情的发挥想象了

cocos2d-x 3.7 实现图片按钮 过滤点击透明区域 在lua中使用

首先在widget头文件添加:
[cpp]
virtual bool AlphaTouchCheck(const Vec2 &point);
virtual bool getAlphaTouchEnable();
virtual void setAlphaTouchEnable(bool isAlphaTouch);
bool _isAlphaTouchEnable = true;
[/cpp]
在widget cpp文件中简单实现
[cpp]
bool Widget::AlphaTouchCheck(const Vec2 &point)
{
return true;
}

bool Widget::getAlphaTouchEnable()
{
return _isAlphaTouchEnable;
}

void Widget::setAlphaTouchEnable(bool isAlphaTouch)
{
_isAlphaTouchEnable = isAlphaTouch;
}
[/cpp]
widget中的判断先默认返回true

把开启的开关放在widget中,方便以后layout 和 imageview的不规则点击。开关默认关闭

接下来就是imageview中重载AlphaTouchCheck 函数
[cpp]
bool ImageView::AlphaTouchCheck(const Vec2& point)
{
if (getAlphaTouchEnable())
{
Image* normalImage = new Image();
normalImage->initWithImageFile(_normalImageString);
auto data = normalImage->getData();
if (data == NULL)
{
return true;
}
auto locationInNode = this->convertToNodeSpace(point);
//图片的0,0 是左上角 所以要和触摸点的Y转换一下 也就是“(normalImage->getHeight() – (int)(locationInNode.y) – 1)”
//该data值是把二维数组展开成一个一维数组, 因为每个像素值由RGBA组成, 所以每隔4个char为一个RGBA, 并且像素以横向排列
int pa = 4 * ((normalImage->getHeight() – (int)(locationInNode.y) – 1) * normalImage->getWidth() + (int)(locationInNode.x)) + 3;
unsigned int ap = data[pa];
if (ap < 20)
{
CC_SAFE_DELETE(normalImage);
return false;
}
else
{
CC_SAFE_DELETE(normalImage);
return true;
}
}
return true;
}
[/cpp]

std::string _normalImageString 是记录创建时候的图片名。

编译工程
接下来在lua中使用
[lua]
imageButton:addTouchEventListener(function(sender, type)
if type == ccui.TouchEventType.began then
self.beginPos = gFun.scene.getMousePos();
self.effect = true;
if imageButton:AlphaTouchCheck(gFun.scene.getMousePos()) then –传入鼠标点击位置
imageButton:setScale(0.95);
end
elseif type == ccui.TouchEventType.moved then
if getDistance(self.beginPos, gFun.scene.getMousePos()) > 20 then –判断是不是有滑动
self.effect = false;
end
elseif type == ccui.TouchEventType.canceled then

imageButton:setScale(1);
elseif type == ccui.TouchEventType.ended then
if not self.effect then return end

imageButton:setScale(1);
if imageButton:AlphaTouchCheck(gFun.scene.getMousePos()) then –传入鼠标点击位置
–do something;
end
end
end
)

[/lua]