用PSD文件自动生成NGUI界面

日常开发中,UI制作一般是美术在PS中制作效果图,然后编写文档告诉客户端UI使用了哪些图片,图片的像素大小,字体的大小等等,客户端程序再根据这些文档制作UI布局。耗时耗力!客户端程序员难以通过肉眼确定UI图片的具体位置,图片的大小也经常没有在文档中指定出来,制作非常麻烦。如果能直接将美术制作的PSD文件解析并自动在Unity中生成UI,可以节省很多时间精力。利用PS的脚本功能,我们可以编写PS脚本,将PS中的UI图层信息导出到XML文件中,这样Unity就能读取解析XML文件,并自动生成NGUI界面。



一:导出PS图层信息到XML文件。PS支持JS脚本,还自带了编辑器ExcendScript来调试,非常良心。相关API可以在此查询:http://www.adobe.com/cn/devnet/photoshop/scripting.html。以下就是导出XML的JS代码,保存为.JSX文件即可:

var doc = app.activeDocument
var out ="" //创建存储遍历结果的变量
var depth = 0;
var root = new XML("<root/>");

getLayers(doc.layers, 0)//遍历图层


var xml = root.toXMLString();

var file = new File();
var fileNew = file.saveDlg("Save new file", "*.xml");
if (fileNew)
{
    fileNew.open("w");
    fileNew.write(xml);
    fileNew.close();
}     

function getLayers(layers, count)
{
    for (var i =0; i<layers.length; i++)
    {
        if(!layers[i].visible)
        {
            continue;
        }
        if(layers[i].typename == "LayerSet")//判断是否是图层组
        {
            //out = out + "\n " +  repeat("    ",count)   +"[" + layers[i].name +"]"; 
            var group = new XML("<group/>");
             group.@name = layers[i].name;
             group.@parnet =  layers[i].parent.name;
             group.@opacity = layers[i].opacity.toFixed(0);
             root.appendChild(group);            
            getLayers(layers[i].layers, count+1);//递归            
         }
        else
         {
             //out = out + "\n " +  repeat("    ",count)   + layers[i].name + "," +  layers[i].bounds; 
             var layer = new XML("<layer/>");
             layer.@name = layers[i].name;
             layer.@index = depth;
             layer.@kind = layers[i].kind;
             layer.@parnet =  layers[i].parent.name;
             layer.@bounds =  layers[i].bounds;
             layer.@opacity = layers[i].opacity.toFixed(0);
             if(layers[i].kind == LayerKind.TEXT)
             { 
                  layer.@r  = layers[i].textItem.color.rgb.red.toFixed(0);
                  layer.@g  = layers[i].textItem.color.rgb.green.toFixed(0);
                  layer.@b  = layers[i].textItem.color.rgb.blue.toFixed(0);
                  layer.@txt = layers[i].textItem.contents;
                  layer.@font = layers[i].textItem.font;
              }
            
             root.appendChild(layer);
             
             depth++;
          }
    }
}
执行后(PS中文件->脚本->浏览载入运行或直接在ExcendScript中运行),生成的XML文件如下:

<root>
  <layer name="comm_1_icon_coppercup" index="0" kind="LayerKind.NORMAL" parnet="比赛奖励.psd" bounds="277 px,277 px,332 px,341 px" opacity="100"/>
  <layer name="comm_1_icon_silvercup" index="1" kind="LayerKind.SMARTOBJECT" parnet="比赛奖励.psd" bounds="277 px,193 px,332 px,257 px" opacity="100"/>
  <layer name="comm_1_icon_goldcup" index="2" kind="LayerKind.SMARTOBJECT" parnet="比赛奖励.psd" bounds="276 px,103 px,331 px,167 px" opacity="100"/>
  <group name="组 462" parnet="比赛奖励.psd" opacity="100"/>
</root>

layer节点是一个图层的信息,group节点是图层组。有了这些信息后,我们就能在Unity中解析并构建UI布局。

二:Unity中读取并构建UI布局。首先是读取文件:

string path = EditorUtility.OpenFilePanel("XML For UI", "", "xml");
        if (!string.IsNullOrEmpty(path))
        {
            FileStream aFile = new FileStream(path, FileMode.Open);
            if (aFile != null)
            {
                
                StreamReader sr = new StreamReader(aFile, Encoding.Default);
                string strXML = sr.ReadToEnd();
            }}
然后解析XML并构建UI:

                XmlDocument doc = new XmlDocument();
                doc.LoadXml(strXML);
                XmlNode nodeRoot = doc.SelectSingleNode("root");
                int length = nodeRoot.ChildNodes.Count;
                foreach (XmlNode node in nodeRoot)
                {
                    XmlAttributeCollection attrColl = node.Attributes;
                    XmlAttribute attrName = (XmlAttribute)attrColl.GetNamedItem("name");
                    XmlAttribute attrIndex = (XmlAttribute)attrColl.GetNamedItem("index");
                    XmlAttribute attrKind = (XmlAttribute)attrColl.GetNamedItem("kind");
                    XmlAttribute attrParent = (XmlAttribute)attrColl.GetNamedItem("parnet");
                    XmlAttribute attrBounds = (XmlAttribute)attrColl.GetNamedItem("bounds");
                    XmlAttribute attrOpacity = (XmlAttribute)attrColl.GetNamedItem("opacity");
                    XmlAttribute attrR = (XmlAttribute)attrColl.GetNamedItem("r");
                    XmlAttribute attrG = (XmlAttribute)attrColl.GetNamedItem("g");
                    XmlAttribute attrB = (XmlAttribute)attrColl.GetNamedItem("b");
                    XmlAttribute attrTxt = (XmlAttribute)attrColl.GetNamedItem("txt");
                    XmlAttribute attrFont = (XmlAttribute)attrColl.GetNamedItem("font");
                    string name = attrName.Value;
                    string parent = attrParent.Value;
                    int opac = int.Parse(attrOpacity.Value);
		    	
                    if (node.Name == "group")
                    {
                        GameObject gobjParent = GetParent(parent);
                        GameObject gobjGroup = new GameObject(name);
                        gobjGroup.transform.parent = gobjParent.transform;
                        gobjGroup.transform.localPosition = Vector3.zero;
                        gobjGroup.transform.localScale = Vector3.one;
                    }
                    else if(node.Name == "layer")
                    {
                        int index = int.Parse(attrIndex.Value);
                        string kind = attrKind.Value;
                        string strBounds = attrBounds.Value;
                        Bounds bounds = GetBounds(strBounds);

                        if (kind.Contains(".TEXT"))
                        {
                            //文本
                            byte R = byte.Parse(attrR.Value);
                            byte G = byte.Parse(attrG.Value);
                            byte B = byte.Parse(attrB.Value);
                            string txt = attrTxt.Value;
                            string font = attrFont.Value;
                            GameObject gobjParent = GetParent(parent);
                            UILabel uiLab = NGUISettings.AddLabel(gobjParent);
                            uiLab.text = txt;
                            uiLab.color = new Color32(R, G, B,(byte)(opac / 100f * 255));
                            // 层级
                            uiLab.depth = length - index;
                            //大小
                            uiLab.transform.localPosition = new Vector3(bounds.center.x, bounds.center.y, 0);
                            uiLab.overflowMethod = UILabel.Overflow.ResizeFreely;
                            uiLab.fontSize = (int)bounds.size.y;
                            uiLab.applyGradient = false;
                            //uiLab.SetDimensions((int)bounds.size.x, (int)bounds.size.y);
                        }
                        else
                        {
                            //图片
                            GameObject gobjParent = GetParent(parent);
                            UIAtlas atlas = GetAtlas(name);
                            UISprite txu = NGUITools.AddSprite(gobjParent, atlas, name);
                            //层级
                            txu.depth = length - index;
                            //大小
                            txu.transform.localPosition = new Vector3(bounds.center.x, bounds.center.y, 0);
                            txu.SetDimensions((int)bounds.size.x, (int)bounds.size.y);
                            //alpha
                            txu.alpha = opac / 100f;
                        }
                    }
                 
                }

要根据parent属性确定父物体,

 private static GameObject GetParent(string parent)
    {
        GameObject gobjParent = null;
        if (parent.Contains(".psd"))
        {
            gobjParent = Selection.activeGameObject;
        }
        else
        {
            gobjParent = GameObject.Find(parent);
        }
       
        return gobjParent;
       
    }

根据name属性确定并载入图集:

 public static UIAtlas GetAtlas(string name)
    {
        string path = "Assets/ResourcesHot/Atlas/pcomm/pcomm.prefab";
        if (name.StartsWith("comm"))
        {
            path = "Assets/ResourcesHot/Atlas/pcomm/pcomm.prefab";
        }
        else if (name.StartsWith("battle"))
        {
            path = "Assets/ResourcesHot/Atlas/battle/battle.prefab";
        }
        else if (name.StartsWith("icon"))
        {
            path = "Assets/ResourcesHot/Atlas/icon/icon.prefab";
        }
       
        GameObject gobjAtlas = AssetDatabase.LoadMainAssetAtPath(path) as GameObject;
        UIAtlas atlas =  gobjAtlas.GetComponent<UIAtlas>();
        return atlas;
    }

根据bounds属性确定位置及大小:

private static Bounds GetBounds(string bounds)
    {
        string[] vals = bounds.Split(',');
        string valLeft = vals[0].Replace(" px", "");
        string valUp = vals[1].Replace(" px", "");
        string valRight = vals[2].Replace(" px", "");
        string valBottom = vals[3].Replace(" px", "");
        int left = int.Parse(valLeft);
        int right = int.Parse(valRight);
        int up = int.Parse(valUp);
        int bottom = int.Parse(valBottom);
        int width = right - left;
        int height = bottom - up;
        int x = Mathf.RoundToInt(left + width / 2f - mSceneWidth / 2f);
        int y = Mathf.RoundToInt(mSceneHeight / 2f - up - height / 2f);
        return new Bounds(new Vector3(x, y), new Vector3(width, height));
    }

PS中的bounds属性是这样的:bounds="277 px,277 px,332 px,341 px",分别为左上及右下点坐标,而且原点在画布左上角。我们需要处理成NGUI的坐标系。







猜你喜欢

转载自blog.csdn.net/leohijack/article/details/65629581