背景和历史版本在下面这篇博客中查看:
Unity & 蓝湖 关于UI工作流优化的思考
最新版本:
本文旨在让不会使用Unity的其他人员在简单了解该工具后,可以帮助研发人员搭建Unity中的UI预制体,研发人员稍作调整即可用,以减轻研发人员的工作压力。
一个UI视图的预制体的制作步骤如下:
1.在蓝湖中下载该视图的所有相关切图
2.将下载的切图资源包解压缩后,拖入到Unity中Project窗口的Assets目录中的任一文件夹内
3.选中所有切图,在Inspector窗口修改Texture Type为Sprite类型,并点击右下角的Apply
4.在顶部菜单栏SKFramework中找到LanHu,打开窗口
5.点击浏览按钮选择该视图的切图所在文件夹
6.点击创建,创建一个Canvas画布,也可以选择场景中已有的Canvas
7.添加
点击添加按钮,添加一项UI视图元素
在蓝湖中点击切图的样式信息中的内容即可复制
回到Unity,点击粘贴按钮,将从蓝湖中复制的内容粘贴到对应参数中
8.删除
点击”-“号按钮,可以将该项进行移除
点击清空按钮,可以清空当前所有的配置信息
9.收缩
配置信息过多时,点击收缩按钮,可以关闭所有折叠栏
10.展开
点击展开按钮,可以打开所有折叠栏
11.生成
点击生成后,工具会根据填写的配置信息,在切图所在文件夹中加载指定切图,并将其设置到指定位置、设置指定大小,最终将生成的UI视图创建为prefab预制体。
随着预制体的生成,工具还会将该视图的所有配置信息以资产的形式保存下来
12.导入
当想要修改一个UI视图的某一元素时,点击导入按钮,将该视图的配置资产文件进行导入,修改配置内容后重新生成即可。
13.预览生成的UI视图
打开Scene窗口中的2D选项
在Hierarchy窗口找到Canvas中的UI视图,双击聚焦查看
工具完整代码:
using System;namespace SK.Framework
{/// <summary>/// 蓝湖界面UI元素/// </summary>[Serializable]public class LanHuViewElement {/// <summary>/// 图层名称/// </summary>public string name;/// <summary>/// 位置x/// </summary>public string x;/// <summary>/// 位置y/// </summary>public string y;/// <summary>/// 宽度/// </summary>public string width;/// <summary>/// 高度/// </summary>public string height;/// <summary>/// 不透明度/// </summary>public string opacity;/// <summary>/// 像素倍数/// </summary>public string pixel = "x1";/// <summary>/// 构造函数/// </summary>public LanHuViewElement(string name, string x, string y, string width, string height, string opacity, string pixel){this.name = name; this.x = x;this.y = y;this.width = width;this.height = height;this.opacity = opacity;this.pixel = pixel;}}
}
using UnityEngine;
using System.Collections.Generic;namespace SK.Framework
{public class LanHuView : ScriptableObject{/// <summary>/// 存放切图的文件夹路径/// </summary>public string path;public List<LanHuViewElement> elements = new List<LanHuViewElement>(0);}
}
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;namespace SK.Framework
{/// <summary>/// 蓝湖UI界面搭建工具/// </summary>public class LanHu : EditorWindow{[MenuItem("SKFramework/LanHu")]private static void Open(){GetWindow<LanHu>("LanHu").Show();}private string path;private List<LanHuViewElement> elements;private Vector2 scroll;private const float labelWidth = 70f;private Dictionary<LanHuViewElement, bool> foldoutDic;private CanvasScaler canvasScaler;private void OnEnable(){path = "Assets";elements = new List<LanHuViewElement>();foldoutDic = new Dictionary<LanHuViewElement, bool>();}private void OnGUI(){OnTopGUI();OnElementsGUI();OnMenuGUI();}private void OnTopGUI(){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();GUILayout.BeginHorizontal();GUILayout.Label("Canvas Scaler", GUILayout.Width(100f));canvasScaler = (CanvasScaler)EditorGUILayout.ObjectField(canvasScaler, typeof(CanvasScaler), true);if (canvasScaler == null){if (GUILayout.Button("创建", GUILayout.Width(40f))){var canvas = new GameObject("Canvas").AddComponent<Canvas>();canvas.renderMode = RenderMode.ScreenSpaceCamera;canvasScaler = canvas.gameObject.AddComponent<CanvasScaler>();canvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;canvasScaler.referenceResolution = new Vector2(1920f, 1080f);EditorGUIUtility.PingObject(canvas);}}GUILayout.EndHorizontal();}private void OnElementsGUI(){EditorGUILayout.Space();GUI.enabled = canvasScaler != null;scroll = EditorGUILayout.BeginScrollView(scroll);for (int i = 0; i < elements.Count; i++){var element = elements[i];if (!foldoutDic.ContainsKey(element)){foldoutDic.Add(element, true);}foldoutDic[element] = EditorGUILayout.Foldout(foldoutDic[element], element.name, true);if (!foldoutDic[element]) continue;GUILayout.BeginVertical("Box");GUILayout.BeginHorizontal();GUILayout.Label("图层", GUILayout.Width(labelWidth));element.name = EditorGUILayout.TextField(element.name);if (GUILayout.Button("粘贴", GUILayout.Width(40f))){element.name = GUIUtility.systemCopyBuffer;}if (GUILayout.Button("-", GUILayout.Width(20f))){foldoutDic.Remove(element);elements.RemoveAt(i);Repaint();}GUILayout.EndHorizontal();GUILayout.BeginHorizontal();GUILayout.Label("位置", GUILayout.Width(labelWidth));element.x = EditorGUILayout.TextField(element.x);if (GUILayout.Button("粘贴", GUILayout.Width(40f))){element.x = GUIUtility.systemCopyBuffer;}element.y = EditorGUILayout.TextField(element.y);if (GUILayout.Button("粘贴", GUILayout.Width(40f))){element.y = GUIUtility.systemCopyBuffer;}GUILayout.EndHorizontal();GUILayout.BeginHorizontal();GUILayout.Label("大小", GUILayout.Width(labelWidth));element.width = EditorGUILayout.TextField(element.width);if (GUILayout.Button("粘贴", GUILayout.Width(40f))){element.width = GUIUtility.systemCopyBuffer;}element.height = EditorGUILayout.TextField(element.height);if (GUILayout.Button("粘贴", GUILayout.Width(40f))){element.height = GUIUtility.systemCopyBuffer;}GUILayout.EndHorizontal();GUILayout.BeginHorizontal();GUILayout.Label("不透明度", GUILayout.Width(labelWidth));element.opacity = EditorGUILayout.TextField(element.opacity);if (GUILayout.Button("粘贴", GUILayout.Width(40f))){element.opacity = GUIUtility.systemCopyBuffer;}GUILayout.EndHorizontal();GUILayout.BeginHorizontal();GUILayout.Label("像素倍数", GUILayout.Width(labelWidth));if (GUILayout.Button(element.pixel)){GenericMenu gm = new GenericMenu();gm.AddItem(new GUIContent("x1"), element.pixel == "x1", () => element.pixel = "x1");gm.AddItem(new GUIContent("x2"), element.pixel == "x2", () => element.pixel = "x2");gm.ShowAsContext();}GUILayout.EndHorizontal();GUILayout.EndVertical();}EditorGUILayout.EndScrollView();}private void OnMenuGUI(){GUILayout.FlexibleSpace();GUILayout.BeginHorizontal();if (GUILayout.Button("导入", "ButtonLeft")){string presetPath = EditorUtility.OpenFilePanel("选择预设文件", Application.dataPath, "asset");if (File.Exists(presetPath)){var import = AssetDatabase.LoadAssetAtPath<LanHuView>(presetPath.Replace(Application.dataPath, "Assets"));if (import != null){elements.Clear();foldoutDic.Clear();path = import.path;for (int i = 0; i < import.elements.Count; i++){elements.Add(import.elements[i]);}Repaint();}}}if (GUILayout.Button("添加", "ButtonMid")){elements.Add(new LanHuViewElement("", "0px", "0px", "1920px", "1080px", "100%", "x1"));}if (GUILayout.Button("清空", "ButtonMid")){if (EditorUtility.DisplayDialog("提醒", "确定删除当前所有配置信息?", "确定", "取消")){elements.Clear();foldoutDic.Clear();}}if (GUILayout.Button("展开", "ButtonMid")){for (int i = 0; i < elements.Count; i++){foldoutDic[elements[i]] = true;}}if (GUILayout.Button("收缩", "ButtonMid")){for (int i = 0; i < elements.Count; i++){foldoutDic[elements[i]] = false;}}if (GUILayout.Button("生成", "ButtonRight")){var array = path.Split('/');var view = new GameObject(array[array.Length - 1]).AddComponent<RectTransform>();view.transform.SetParent(canvasScaler.transform, false);SetRectTransform(view, 0, 0, canvasScaler.referenceResolution.x, canvasScaler.referenceResolution.y);for (int i = 0; i < elements.Count; i++){var element = elements[i];string spritePath = string.Format("{0}/{1}{2}.png", path, element.name, element.pixel == "x1" ? string.Empty : "@2x");var obj = AssetDatabase.LoadAssetAtPath<Sprite>(spritePath);if (obj != null){var image = new GameObject(obj.name).AddComponent<Image>();image.transform.SetParent(view.transform, false);image.sprite = obj;RectTransform rt = image.transform as RectTransform;float.TryParse(element.x.Replace(element.x.Substring(element.x.Length - 2, 2), string.Empty), out float xValue);float.TryParse(element.y.Replace(element.y.Substring(element.y.Length - 2, 2), string.Empty), out float yValue);float.TryParse(element.width.Replace(element.width.Substring(element.width.Length - 2, 2), string.Empty), out float wValue);float.TryParse(element.height.Replace(element.height.Substring(element.height.Length - 2, 2), string.Empty), out float hValue);/*float.TryParse(element.x, out float xValue);float.TryParse(element.y, out float yValue);float.TryParse(element.width, out float wValue);float.TryParse(element.height, out float hValue);*/SetRectTransform(rt, xValue, yValue, wValue, hValue);}else{Debug.Log($"<color=yellow>加载切图失败 {spritePath}</color>");}}//创建预设文件var preset = CreateInstance<LanHuView>();for (int i = 0; i < elements.Count; i++){preset.elements.Add(elements[i]);}preset.path = path;AssetDatabase.CreateAsset(preset, string.Format("Assets/{0}.asset", view.name));AssetDatabase.Refresh();Selection.activeObject = preset;//创建Prefabvar prefab = PrefabUtility.SaveAsPrefabAsset(view.gameObject, $"Assets/{view.name}.prefab", out bool result);if (!result){Debug.Log($"<color=yellow>生成预制体失败 {view.name}</color>");}else{EditorGUIUtility.PingObject(prefab);}}GUILayout.EndHorizontal();}private void SetRectTransform(RectTransform rt, float x, float y, float width, float height){//调整位置及大小rt.anchorMin = new Vector2(0, 1);rt.anchorMax = new Vector2(0, 1);rt.pivot = Vector2.one * .5f;rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, width);rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height);rt.anchoredPosition = new Vector2(x + width / 2f, -(y + height / 2f));//调整完成后自动设置锚点RectTransform prt = rt.parent as RectTransform;Vector2 anchorMin = new Vector2(rt.anchorMin.x + rt.offsetMin.x / prt.rect.width,rt.anchorMin.y + rt.offsetMin.y / prt.rect.height);Vector2 anchorMax = new Vector2(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;}}
}
本文链接:https://my.lmcjl.com/post/20253.html
4 评论