【Unity编辑器扩展】PSD转UGUI Prefab——Aspose.PSD和Harmony库的使用

“拼UI是该策划负责还是程序负责?”一直是国内游戏行业极具争议的话题。

矛盾点也很清楚:

程序:拼UI繁琐,没有技术含量,应该交给策划拼,程序工作繁重应该把时间用在刀刃上。

策划:程序对拼UI的层次结构、命名要求复杂,拼UI需要技术思维,应该由程序负责。

总之各有道理,但是话说回来,既然UI设计师在设计UI界面时必然会拼好UI元素,各种图层、颜色、字体字号信息都有,为什么还要重复UI设计师的工作再次人工拼一次UI呢?

事实上市面上早已有蓝湖在解决这一痛点,PS安装蓝湖插件后,通过标记切图就能一键UI上传到蓝湖网站,项目组成员就能看到UI的布局、字体字号、UI元素间距/位置、批量下载切图等。蓝湖有免费版、付费版,如果担心资源安全,企业也可以购买内网部署服务,所以很多公司都在用蓝湖协作。但是,蓝湖是针对Android、iOS、Web设计,实质上对于Unity游戏UI来说,蓝湖只是解决了可以不让UI仔通过目测调位置、调字号等,蓝湖解析出psd图层的具体数值信息,还是需要人工拼UI。

UI设计师常用工具adobe illustrator和Photoshop,而adobe illustrator也可以导出为psd文件,所以写一个工具直接根据Photoshop文件(psd)自动生成拼好的UI界面prefab岂不美哉?

一,Aspose.PSD .Net库的使用

Aspose.PSD .Net库下载:https://download.csdn.net/download/final5788/87685311

Aspose官网:File Format APIs for Word Excel PDF Email PowerPoint Barcode Images OCR Note and 3D 

 一款收费的psd文件解析库,需要购买License。Aspose.PSD不依赖ps软件,使用C#脚本直接解析psd文件,支持多种图层解析、导出图层切图,还支持合并图层等,功能非常强大。

通过Aspose.PSD解析图层信息,甚至不需要UI设计师把图层栅格化成图片图层,可以直接将形状图层、填充色图层等直接导出成图片,对于文本层(TextLayer)可以直接读取到文本字体、字号、颜色等信息,直接对等生成Unity UGUI的Text文本组件或TextMeshProUGUI组件并自动设置好文本样式。

1. 用PS简单做个界面,导出psd放入Unity工程:

 2. 使用Aspose.PSD接口解析并导出碎图

下面是使用Aspose.PSD解析psd图层的简单示例:

using (var psd = Aspose.PSD.Image.Load(psdFile) as PsdImage)
            {
                foreach (var layer in psd.Layers)
                {
                    var layerTp = layer.GetType();
                    Debug.Log($"{layer.Name}->{layerTp.Name}");
                    switch (layerTp.Name)
                    {
                        case LayerType.Layer:
                        case LayerType.FillLayer:
                        case LayerType.TextLayer:

                            var fileName = Path.Combine(Path.GetDirectoryName(psdFile), Path.GetFileNameWithoutExtension(psdFile), $"{layer.DisplayName}.png");
                            var fileDir = Path.GetDirectoryName(fileName);
                            if (!Directory.Exists(fileDir)) Directory.CreateDirectory(fileDir);

                            layer.ToBitmap().Save(fileName, System.Drawing.Imaging.ImageFormat.Png);
                            break;
                        //case LayerType.FillLayer://填充色图层

                        //    break;
                        //case LayerType.TextLayer:
                        //    if (layer is TextLayer txtLayer)
                        //    {
                        //        Debug.Log($"文本层:{txtLayer.Text}, color:{txtLayer.TextColor}");
                        //    }
                        //    break;
                        //case LayerType.SectionDividerLayer:
                        //    break;
                        //case LayerType.LayerGroup://组
                        //    if (layer is LayerGroup gpLayer)
                        //    {

                        //    }
                        //    break;
                    }

                }
            }


class LayerType
        {
            internal const string Layer = "Layer";
            internal const string FillLayer = "FillLayer";
            internal const string TextLayer = "TextLayer";
            internal const string SectionDividerLayer = "SectionDividerLayer";
            internal const string LayerGroup = "LayerGroup";
        }

 把psd图层保存成图片文件:

imgLayer.ToBitmap().Save(fileName, System.Drawing.Imaging.ImageFormat.Png);

 甚至可以用之前写过的图片压缩工具,在导出碎图时自动对碎图进行压缩。

程序成功导出碎图,但是图片上带有水印,文本图层导出成图片后也有瑕疵:

 Aspose好歹让正经试用一下啊,这样也不知道到底是因为没购买License故意制造的“瑕疵”,还是插件本身酒有瑕疵?

如果有Licenses需要先初始化证书:

new Aspose.PSD.License().SetLicense(new MemoryStream(Convert.FromBase64String("Your Licenses")));

所谓“先尝后买”,那就先鉴定插件是否有瑕疵,如果没有瑕疵咱再购买License。

用dnSpy打开dll发现,是我单纯了,不愧是技术型软件公司,混淆和反追踪几乎做到了极致,难怪这么自信,竟然敢以离线License的方式变现。

那就换种思路继续鉴定。。。

二、Harmony库的使用

不要误会,它跟华为的鸿蒙没有关系,Harmony是一个开源,可以运行时对C#函数打补丁的插件,支持.net和mono。

Harmony库开源地址:GitHub - pardeike/Harmony: A library for patching, replacing and decorating .NET and Mono methods during runtime

 思路:在网上找到一个公开的License,验证时Aspose.PSD报错提示,大概意思是许可证最大支持到xxx时间点之前发布的Aspose.PSD.dll,你的dll发布时间为xxx, 意思就是许可证过期无效。这条提示就暴露了:

1. 许可证字符串中保存着一个有效日期;

2. dll中保存着一个发布日期;

3. SetLicense时把许可证日期和dll发布日期进行了比较以判定是否验证成功。

根据以上线索定位到许可证日期是从Xml中得来的,报错提示贴心的给出了这个准确的许可证过期日期。

Ok,线索齐了。通过Harmony库给系统类System.Xml.XmlElement.InnerText的Getter方法注入个补丁,判断文本内容如果与许可证过期日期相同则把日期强制改为未来日期,这样就永远不会过期了。

Harmony使用方法:

首先定义补丁类:

在补丁类上加上:HarmonyPatch(Type declaringType, string methodName, MethodType methodType)

[HarmonyPatch(typeof(System.Xml.XmlElement), nameof(System.Xml.XmlElement.InnerText), MethodType.Getter)]
    class MagicPatch
    {
        static void Prefix()
        {

        }
        static void Postfix(ref string __result)
        {
            if (__result == "20210827")
            {
                __result = "20250827";
            }
        }
    }

其中静态方法Prefix:原函数调用前触发;Postfix:原函数调用时触发;注意:函数名不能变,固定Prefix和Postfix;

想要访问原函数的返回值必须在Postfix中,如上代码,ref string __result就是InnerText Get方法返回值的引用,可以直接修改引用的值。

定义完补丁类后就需要调用Harmony接口注入补丁后生效:

[InitializeOnLoadMethod]
        static void InitAsposeLicense()
        {
            if (licenseInitiated) return;
            var harmonyHook = new Harmony("Test");
            harmonyHook.PatchAll();

            new Aspose.PSD.License().SetLicense(new MemoryStream(Convert.FromBase64String("You License")));

            licenseInitiated = true;
            harmonyHook.UnpatchAll();
        }

PatchAll会自动注入HarmonyPatch标记过的补丁类,为了不影响其它使用Xml的InnerText,补丁用完不需要的时候,调用UnpatchAll取消所有补丁。

好的,就是这么简单。所有工作准备就绪,开始测试:

 再次导出psd图层,图片一切都正常了,证明Aspose.PSD库能够完美工作,存在的”瑕疵“是没有许可证的原因,可以放心大胆地购买许可证了。

猜你喜欢

转载自blog.csdn.net/final5788/article/details/130114695