Unity & 蓝湖 关于UI工作流优化的思考

简介: Unity & 蓝湖 关于UI工作流优化的思考

我们Unity项目关于UI界面制作的工作流是这样的,UI设计人员将设计好的UI界面在Adobe XD中上传至蓝湖,Unity程序猿从蓝湖中下载切图资源包导入项目工程中,根据蓝湖中的效果图、样式信息进行界面的搭建:

image.gif

例如这张首页UI中的第一个按钮,其图层名称为"组646",位置为(209px,605px),大小为(225px,76px)

image.gif

首先可以看到位置信息中的685px是指这个图层到顶部的像素大小,可以判断其位置信息是在以左上角为原点的坐标系中的,而且在Unity中RectTransform组件以(0.5,0.5)为默认的轴心点,因此在考虑横坐标时需要计算上该图层宽度的一半,考虑纵坐标时需要计算上该图层高度的一半:

image.gif

以这个按钮图层为例,在Unity中我们将其RectTransform组件中的锚点设为左上角,Pivot轴心点使用默认的(0.5,0.5),则其横坐标则是209+225 * 0.5,纵坐标则是-(685 + 76 * 0.5),计算出结果为(321.5,-723):

image.gif

image.gif

有了这样的换算关系后,基于能偷懒则偷懒的原则,我开始思考将其转化为自动化的过程,于是写了这样一个工具,只需要在工具中输入蓝湖中该图层的位置及大小信息,点击确定即可将该按钮设置正确的位置及大小:

image.gif

代码如下:

usingUnityEngine;
usingUnityEditor;
usingUnityEngine.UI;
namespaceSK.Framework{
publicclassLanHu : EditorWindow    {
        [MenuItem("SKFramework/Tools/Lan Hu")]
privatestaticvoidOpen()
        {
GetWindow<LanHu>("蓝湖").Show();
        }
privateconstfloatlabelWidth=60f;
privatefloatx;
privatefloaty;
privatefloatwidth;
privatefloatheight;
privatevoidOnGUI()
        {
//如果未选中任何物体 returnif (Selection.activeTransform==null) return;
RectTransformrt=Selection.activeTransform.GetComponent<RectTransform>();
//如果选中的物体不是UI元素 returnif (rt==null) return;
GUILayout.Label("分辨率: 1920*1080");
EditorGUILayout.Space();
GUILayout.Label("样式信息", "BoldLabel");
GUILayout.BeginHorizontal();
GUILayout.Label("图层", GUILayout.Width(labelWidth));
varimage=rt.GetComponent<Image>();
if (image!=null&&image.sprite!=null) EditorGUILayout.TextField(image.sprite.name);
elseEditorGUILayout.HelpBox("未发现任何图层", MessageType.Warning);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("位置", GUILayout.Width(labelWidth));
x=EditorGUILayout.FloatField(x);
GUILayout.Label("px");
y=EditorGUILayout.FloatField(y);
GUILayout.Label("px");
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("大小", GUILayout.Width(labelWidth));
width=EditorGUILayout.FloatField(width);
GUILayout.Label("px");
height=EditorGUILayout.FloatField(height);
GUILayout.Label("px");
GUILayout.EndHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button("确定"))
            {
//调整位置及大小rt.anchorMin=newVector2(0, 1);
rt.anchorMax=newVector2(0, 1);
rt.pivot=Vector2.one* .5f;
rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, width);
rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height);
rt.anchoredPosition=newVector2(x+width/2f, -(y+height/2f));
//调整完成后自动设置锚点RectTransformprt=rt.parentasRectTransform;
Vector2anchorMin=newVector2(
rt.anchorMin.x+rt.offsetMin.x/prt.rect.width,
rt.anchorMin.y+rt.offsetMin.y/prt.rect.height);
Vector2anchorMax=newVector2(
rt.anchorMax.x+rt.offsetMax.x/prt.rect.width,
rt.anchorMax.y+rt.offsetMax.y/prt.rect.height);
rt.anchorMin=anchorMin;
rt.anchorMax=anchorMax;
rt.offsetMin=rt.offsetMax=Vector2.zero;
EditorUtility.SetDirty(rt);
            }
        }
privatevoidOnSelectionChange()
        {
Repaint();
        }
    }
}

image.gif

以上是在这条偷懒路上的第一个产物,它依然需要手动输入图层的位置、大小信息,随后发现了蓝湖中的这些样式信息是可以点击复制的:

image.gif

于是我开始思考将float类型的输入框改为string类型的输入框,将复制来的信息直接粘贴到输入框中,只需要将字符串的最后两个字符px移除,再将其转化为float类型即可,于是有了第二个产物:

image.gif

代码如下:

usingUnityEngine;
usingUnityEditor;
usingUnityEngine.UI;
namespaceSK.Framework{
publicclassLanHu : EditorWindow    {
        [MenuItem("SKFramework/Tools/Lan Hu")]
privatestaticvoidOpen()
        {
GetWindow<LanHu>("蓝湖").Show();
        }
privateconstfloatlabelWidth=60f;
privatestringx;
privatestringy;
privatestringw;
privatestringh;
privatevoidOnGUI()
        {
//如果未选中任何物体 returnif (Selection.activeTransform==null) return;
RectTransformrt=Selection.activeTransform.GetComponent<RectTransform>();
//如果选中的物体不是UI元素 returnif (rt==null) return;
GUILayout.Label("分辨率: 1920*1080");
EditorGUILayout.Space();
GUILayout.Label("样式信息", "BoldLabel");
GUILayout.BeginHorizontal();
GUILayout.Label("图层", GUILayout.Width(labelWidth));
varimage=rt.GetComponent<Image>();
if (image!=null&&image.sprite!=null) EditorGUILayout.TextField(image.sprite.name);
elseEditorGUILayout.HelpBox("未发现任何图层", MessageType.Warning);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("位置", GUILayout.Width(labelWidth));
x=EditorGUILayout.TextField(x);
y=EditorGUILayout.TextField(y);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("大小", GUILayout.Width(labelWidth));
w=EditorGUILayout.TextField(w);
h=EditorGUILayout.TextField(h);
GUILayout.EndHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button("确定"))
            {
float.TryParse(x.Replace(x.Substring(x.Length-2, 2), string.Empty), outfloatxValue);
float.TryParse(y.Replace(y.Substring(y.Length-2, 2), string.Empty), outfloatyValue);
float.TryParse(w.Replace(w.Substring(w.Length-2, 2), string.Empty), outfloatwValue);
float.TryParse(h.Replace(h.Substring(h.Length-2, 2), string.Empty), outfloathValue);
//调整位置及大小rt.anchorMin=newVector2(0, 1);
rt.anchorMax=newVector2(0, 1);
rt.pivot=Vector2.one* .5f;
rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, wValue);
rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, hValue);
rt.anchoredPosition=newVector2(xValue+wValue/2f, -(yValue+hValue/2f));
//调整完成后自动设置锚点RectTransformprt=rt.parentasRectTransform;
Vector2anchorMin=newVector2(
rt.anchorMin.x+rt.offsetMin.x/prt.rect.width,
rt.anchorMin.y+rt.offsetMin.y/prt.rect.height);
Vector2anchorMax=newVector2(
rt.anchorMax.x+rt.offsetMax.x/prt.rect.width,
rt.anchorMax.y+rt.offsetMax.y/prt.rect.height);
rt.anchorMin=anchorMin;
rt.anchorMax=anchorMax;
rt.offsetMin=rt.offsetMax=Vector2.zero;
EditorUtility.SetDirty(rt);
            }
        }
privatevoidOnSelectionChange()
        {
Repaint();
        }
    }
}

image.gif

有了上面的工具后,我又开始了这样的思考,如果我们可以直接拿到一个界面中所有图层的样式信息,包括图层名称、位置信息、大小信息,这些信息形成一个Json数据或其他序列化数据文件,那么便可以通过编写工具一键生成这个界面的Prefab预制体。那么我们只需要定义这样一个数据结构,通过数据反序列化得到这些信息:

publicclassElement{
//图层名称publicstringname;
//横坐标publicstringx;
//纵坐标publicstringy;
//宽度publicstringwidth;
//高度publicstringheight;
//不透明度publicstringalpha;
}

image.gif

于是我开始向UI设计的同事咨询,他们从设计到上传蓝湖有没有中间产物生成,能不能从中间产物中获取一些有用的信息,但是同事回应说是通过插件直接从Adobe XD上传至蓝湖,因此这条路便行不通了。但是后来我觉得既然能有上传蓝湖这样的插件,那么编写一个生成数据文件的插件完全有可能吧,只不过没人去做这件事,所以我还是觉得这个想法是行得通的,奈何自己做不来。随后我又从前端着手,咨询了一些朋友,能否在蓝湖页面中获取这些信息,最后也是不了了之。

最终只能在Unity中去着手,将更多的工作量通过自动化去完成,于是又有了这样的思路:记录切图所在的文件夹的路径,从蓝湖中复制粘贴界面中所有图层的样式信息,点击生成,通过图层的名称在切图所在的文件夹中加载该切图,创建一个Image物体,通过图层的位置、大小信息去设置该物体的Rect Transform组件属性,最终生成prefab预制体:  image.gif

代码如下:

usingSystem;
usingUnityEngine;
usingUnityEditor;
usingUnityEngine.UI;
usingSystem.Collections.Generic;
namespaceSK.Framework{
publicclassLanHu : EditorWindow    {
        [MenuItem("SKFramework/Tools/Lan Hu")]
privatestaticvoidOpen()
        {
GetWindow<LanHu>("蓝湖").Show();
        }
privateclassElement        {
publicstringname;
publicstringx;
publicstringy;
publicstringwidth;
publicstringheight;
publicstringalpha;
        }
privatestringpath;
privateList<Element>elements;
privateconstfloatlabelWidth=60f;
privateVector2scroll;
privatevoidOnEnable()
        {
elements=newList<Element>();
scroll=Vector2.zero;
        }
privatevoidOnDisable()
        {
elements.Clear();
elements=null;
        }
privatevoidOnGUI()
        {
GUILayout.BeginHorizontal();
GUILayout.Label("切图文件夹路径:", GUILayout.Width(100f));
EditorGUILayout.TextField(path);
if(GUILayout.Button("浏览", GUILayout.Width(40f)))
            {
//Assets相对路径path=EditorUtility.OpenFolderPanel("选择切图文件夹", "", "").Replace(Application.dataPath, "Assets");
            }
GUILayout.EndHorizontal();
scroll=EditorGUILayout.BeginScrollView(scroll);
for (inti=0; i<elements.Count; i++)
            {
varelement=elements[i];
GUILayout.BeginVertical("Box");
GUILayout.BeginHorizontal();
GUILayout.Label("图层", GUILayout.Width(labelWidth));
element.name=EditorGUILayout.TextField(element.name);
if (GUILayout.Button("-", GUILayout.Width(20f)))
                {
elements.RemoveAt(i);
Repaint();
                }
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("位置", GUILayout.Width(labelWidth));
element.x=EditorGUILayout.TextField(element.x);
element.y=EditorGUILayout.TextField(element.y);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("大小", GUILayout.Width(labelWidth));
element.width=EditorGUILayout.TextField(element.width);
element.height=EditorGUILayout.TextField(element.height);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("透明", GUILayout.Width(labelWidth));
element.alpha=EditorGUILayout.TextField(element.alpha);
GUILayout.EndHorizontal();
GUILayout.EndVertical();
            }
EditorGUILayout.EndScrollView();
GUILayout.FlexibleSpace();
GUILayout.BeginHorizontal();
if (GUILayout.Button("添加"))
            {
elements.Add(newElement());
            }
if (GUILayout.Button("生成"))
            {
//Canvasvararray=path.Split('/');
varcanvas=newGameObject(array[array.Length-1]).AddComponent<Canvas>();
canvas.renderMode=RenderMode.ScreenSpaceCamera;
varcanvasScaler=canvas.gameObject.AddComponent<CanvasScaler>();
canvasScaler.uiScaleMode=CanvasScaler.ScaleMode.ScaleWithScreenSize;
canvasScaler.referenceResolution=newVector2(1920f, 1080f);
for (inti=0; i<elements.Count; i++)
                {
varelement=elements[i];
stringspritePath=path+"/"+element.name+"@2x.png";
varobj=AssetDatabase.LoadAssetAtPath<Sprite>(spritePath);
if (obj!=null)
                    {
varimage=newGameObject(obj.name).AddComponent<Image>();
image.transform.SetParent(canvas.transform, false);
image.sprite=obj;
RectTransformrt=image.transformasRectTransform;
float.TryParse(element.x.Replace(element.x.Substring(element.x.Length-2, 2), string.Empty), outfloatxValue);
float.TryParse(element.y.Replace(element.y.Substring(element.y.Length-2, 2), string.Empty), outfloatyValue);
float.TryParse(element.width.Replace(element.width.Substring(element.width.Length-2, 2), string.Empty), outfloatwValue);
float.TryParse(element.height.Replace(element.height.Substring(element.height.Length-2, 2), string.Empty), outfloathValue);
//调整位置及大小rt.anchorMin=newVector2(0, 1);
rt.anchorMax=newVector2(0, 1);
rt.pivot=Vector2.one* .5f;
rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, wValue);
rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, hValue);
rt.anchoredPosition=newVector2(xValue+wValue/2f, -(yValue+hValue/2f));
//调整完成后自动设置锚点RectTransformprt=rt.parentasRectTransform;
Vector2anchorMin=newVector2(
rt.anchorMin.x+rt.offsetMin.x/prt.rect.width,
rt.anchorMin.y+rt.offsetMin.y/prt.rect.height);
Vector2anchorMax=newVector2(
rt.anchorMax.x+rt.offsetMax.x/prt.rect.width,
rt.anchorMax.y+rt.offsetMax.y/prt.rect.height);
rt.anchorMin=anchorMin;
rt.anchorMax=anchorMax;
rt.offsetMin=rt.offsetMax=Vector2.zero;
                    }
else                    {
Debug.Log($"<color=yellow>加载切图失败 {spritePath}</color>");
                    }
                }
varprefab=PrefabUtility.SaveAsPrefabAsset(canvas.gameObject, $"Assets/{canvas.name}.prefab", outboolresult);
if (!result)
                {
Debug.Log($"<color=yellow>生成预制体失败 {canvas.name}</color>");
                }
else                {
EditorGUIUtility.PingObject(prefab);
                }
            }
GUILayout.EndHorizontal();
        }
    }
}

image.gif

目录
相关文章
|
4月前
|
存储 设计模式 监控
运用Unity Profiler定位内存泄漏并实施对象池管理优化内存使用
【7月更文第10天】在Unity游戏开发中,内存管理是至关重要的一个环节。内存泄漏不仅会导致游戏运行缓慢、卡顿,严重时甚至会引发崩溃。Unity Profiler作为一个强大的性能分析工具,能够帮助开发者深入理解应用程序的内存使用情况,从而定位并解决内存泄漏问题。同时,通过实施对象池管理策略,可以显著优化内存使用,提高游戏性能。本文将结合代码示例,详细介绍如何利用Unity Profiler定位内存泄漏,并实施对象池来优化内存使用。
259 0
|
2月前
|
前端开发 图形学 开发者
【独家揭秘】那些让你的游戏瞬间鲜活起来的Unity UI动画技巧:从零开始打造动态按钮,提升玩家交互体验的绝招大公开!
【9月更文挑战第1天】在游戏开发领域,Unity 是最受欢迎的游戏引擎之一,其强大的跨平台发布能力和丰富的功能集让开发者能够迅速打造出高质量的游戏。优秀的 UI 设计对于游戏至关重要,尤其是在手游市场,出色的 UI 能给玩家留下深刻的第一印象。Unity 的 UGUI 系统提供了一整套解决方案,包括 Canvas、Image 和 Button 等组件,支持添加各种动画效果。
132 3
|
3月前
|
存储 分布式计算 供应链
Spark在供应链核算中应用问题之通过Spark UI进行任务优化如何解决
Spark在供应链核算中应用问题之通过Spark UI进行任务优化如何解决
|
3月前
|
前端开发 开发工具 图形学
PicoVR Unity SDK⭐️三、详解与UI的交互方式
PicoVR Unity SDK⭐️三、详解与UI的交互方式
|
3月前
|
前端开发 图形学
Unity精华☀️UI和物体可见性的判断方法
Unity精华☀️UI和物体可见性的判断方法
|
3月前
|
开发者 图形学 iOS开发
掌握Unity的跨平台部署与发布秘籍,让你的游戏作品在多个平台上大放异彩——从基础设置到高级优化,深入解析一站式游戏开发解决方案的每一个细节,带你领略高效发布流程的魅力所在
【8月更文挑战第31天】跨平台游戏开发是当今游戏产业的热点,尤其在移动设备普及的背景下更为重要。作为领先的游戏开发引擎,Unity以其卓越的跨平台支持能力脱颖而出,能够将游戏轻松部署至iOS、Android、PC、Mac、Web及游戏主机等多个平台。本文通过杂文形式探讨Unity在各平台的部署与发布策略,并提供具体实例,涵盖项目设置、性能优化、打包流程及发布前准备等关键环节,助力开发者充分利用Unity的强大功能,实现多平台游戏开发。
97 0
|
3月前
|
开发者 图形学 前端开发
绝招放送:彻底解锁Unity UI系统奥秘,五大步骤教你如何缔造令人惊叹的沉浸式游戏体验,从Canvas到动画,一步一个脚印走向大师级UI设计
【8月更文挑战第31天】随着游戏开发技术的进步,UI成为提升游戏体验的关键。本文探讨如何利用Unity的UI系统创建美观且功能丰富的界面,包括Canvas、UI元素及Event System的使用,并通过具体示例代码展示按钮点击事件及淡入淡出动画的实现过程,助力开发者打造沉浸式的游戏体验。
95 0
|
3月前
|
开发者 图形学 UED
深度解析Unity游戏开发中的性能瓶颈与优化方案:从资源管理到代码执行,全方位提升你的游戏流畅度,让玩家体验飞跃性的顺滑——不止是技巧,更是艺术的追求
【8月更文挑战第31天】《Unity性能优化实战:让你的游戏流畅如飞》详细介绍了Unity游戏性能优化的关键技巧,涵盖资源管理、代码优化、场景管理和内存管理等方面。通过具体示例,如纹理打包、异步加载、协程使用及LOD技术,帮助开发者打造高效流畅的游戏体验。文中提供了实用代码片段,助力减少内存消耗、提升渲染效率,确保游戏运行丝滑顺畅。性能优化是一个持续过程,需不断测试调整以达最佳效果。
90 0
|
3月前
|
小程序 UED 开发者
揭秘支付宝小程序成功之道:UI/UX设计原则与用户体验优化秘籍大公开!
【8月更文挑战第27天】支付宝小程序在移动互联网中扮演着重要角色,优秀的UI/UX设计能显著提升用户满意度。本文首先强调了设计的一致性、简洁性、易用性和响应性原则,确保用户获得顺畅体验。接着,介绍了最佳实践,包括利用支付宝设计组件库保持界面统一、优化加载速度、适应多设备显示、设置清晰导航以及重视用户反馈。最后,提供了一个简单示例展示如何应用支付宝设计组件。遵循这些指导原则,开发者能够构建既美观又实用的小程序。
78 0
|
3月前
|
图形学
小功能⭐️Unity获取点击到的UI
小功能⭐️Unity获取点击到的UI