利用ActionManger拓展psd2gui

2024-3-16 · Dand

根据Prefab自动生成Lua代码

2023-6-7 · Dand

UGUI对比FGUI

前言 UI是游戏中很重要的一部分,工作大多是繁复的,所以有一套功能强大,操作简单直观的UI解决方案是非常有必要的。 上半年一直在用FGUI,和之前用的UGUI相比下来,各有优缺点,对比分析记录一下。 一、介绍 UGUI UGUI是Unity原生提供的UI方案,提供了基本的UI组件,如图片,文本,按钮,复选框,进度条等等,正是因为UGUI是面向普遍开发,所以没有封装很多高级的组件,比如虚拟列表。 FGUI FairyGUI(下来简称FGUI)号称易上手,直观,零代码,有一个独立的编辑器,封装了很多高级的UI组件,用起来不需要自己造轮子。 二、编辑器使用体验 UGUI 优点:稳定。 缺点:因为集成在Unity中,资源管理不太方便,需要项目定好规范,编写工具提交效率。 FGUI 优点:有单独的编辑器,所有资源统一管理,美术策划也能很快上手使用。 缺点:不稳定,经常会莫名卡住,导出的时候经常报错。 三、基础组件 基础组件上面因为FGUI实现很多的高级功能,所以使用起来体验要么一样,要么好点。 文本 FGUI文本支持ubb语法,富文本支持图文混排,超链接等等,自动大小的文本。 列表 FGUI实现了虚拟列表,树列表。 字体 字体上FGUI提供了工具制作美术字,只要把资源拖进去专用的编辑器就能很快做出一个美术字体。 动效系统 两者都有动效系统,UGUI上叫Animation,功能上差不多,都很强大。 四、对齐系统 对齐系统这一块,FGUI是很大的亮点。 UGUI UGUI的锚点系统以中心点为基准,对父节点对齐,提供了上下左右,拉伸对齐方式。 **优点:**简单直观UI默认的中心点在中心,符合人的正常思维。 缺点:不够灵活;同级UI之前的关联对齐要依赖其他布局组件;缺乏针对同级元素的对齐工具,不过可以自己拓展实现,可以参考我的另一篇文章。 FGUI 优点:FGUI的对齐系统叫做关联系统,第一个优点是不局限与与父节点对齐,可以对任意两个元素进行对齐。第二个优点是在拓展了对齐的维度,可以以上下左右边缘为基准,可以以上下左右为基准拉伸,复杂一点,但是学会以后可以很方便实现一些效果,比如一张图片随着文字的变长而往右移动。 缺点:垂直布局和水平布局功能太单一;锚点在左上角各种反人类。 五、FGUI特色功能 资源引用查找工具 可以轻易找到组件(图片)和其他组件的引用关系。 支持多国语言 原生支持多国语言,可以导出xml维护即可。 控制器系统 控制器系统一般用于控制显示一些UI元素的状态,可以配合动效和Tab使用,UGUI一般要通过代码控制或者自己实现一个类似的系统。 多平台 FGUI可以一套UI,应用到不同的游戏引擎去,比如Unity和Unreal,对于后期可能更换引擎的项目,节省掉很多成本。 插入3D物体 支持在UI层中插入任何3D物体,例如模型、粒子、骨骼动画等,UGUI中要通过修改特效和UI的渲染顺序实现,比较麻烦。 六、拓展性 两者都有拓展性,FGUI的拓展性在于代码是开源的,可以在底层定制适合项目的功能,但是编辑器是不开源的,所以编辑器里面的东西改不了,虽然编辑器提供了脚本拓展支持,但能实现的东西不多。 UGUI的拓展性在于可以轻松地对编辑器进行拓展,也可以加上各种效果,一般也不会动到底层的如网格重构和渲染的代码,真想修改的话可能要和Unity进行合作然后项目搞一个魔改版的Unity。 七、总结 总的来说两种UI解决方案都可以商用,不会有特别大的问题。 要是策划美术拼UI的话,可以选择FGUI,相反要是程序负责的话,两者都可以,但个人感觉Unity上的操作更加熟悉一点,程序员也可以在平时的UI流程中,吸收一些FGUI的思想,做些工具提高UI的开发效率。

2022-11-18 · Dand

UGUI对齐工具

前言 上半年的大半年时间都在用FGUI,最近又用回了UGUI,发现FGUI的一些工具还是很高效的,比如对齐工具,想着说移植到UGUI里面去,上网搜了一圈还真有,原理并不复杂,就是进行一些数学计算出最终的位置。 不过也没有完全适合项目需求,针对原项目基础上添加了三个功能,下面介绍一下。 一、撤销功能 原项目的工具在修改后,习惯性就按下Ctrl+Z,发现并不能撤销原来的操作,查询了Unity的API后,发现有个Undo的类,可以实现撤销功能,有很多方法,主要用到了RecordObject,代码如下 private static void SetTranPos(Transform tran, Vector3 pos) { Undo.RecordObject(tran, "modify posstion"); tran.position = pos; } 二、Selection.GameObjects不是按选择顺序的 API中的Selection.GameObjects用于获取当前选中的物件,数组的顺序和我选择的顺序无关,所以不能实现灵活的对齐,查询网上文章可以用Selection.Objects替代,里面的第一个元素就是Ctrl选中的第一个元素,以此类推,代码如下 //按选中顺序获取GameObjects private static GameObject[] GetOrderedSelctionObjs() { return Selection.objects.OfType<GameObject>().ToArray(); } 三、添加对父节点对齐 功能就是和标题一样,原项目是没有的,代码如下 //如果只选中一个,对父节点对齐 if (rects.Count == 1) { if (rects[0].parent != null && rects[0].parent.GetComponent<RectTransform>() != null) { rects.Insert(0, rects[0].parent.GetComponent<RectTransform>()); } } 四、代码 UGUIAlign using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; public enum AlignType { Top = 1, Left = 2, Right = 3, Bottom = 4, HorizontalCenter = 5, //水平居中 VerticalCenter = 6, //垂直居中 Horizontal = 7, //横向分布 Vertical = 8, //纵向分布 } public class UGUIAlign : Editor { [MenuItem("GameObject/UI/Align/Left 【左对齐】")] static void AlignLeft() { Align(AlignType.Left); } [MenuItem("GameObject/UI/Align/HorizontalCenter 【水平居中】")] static void AlignHorizontalCenter() { Align(AlignType.HorizontalCenter); } [MenuItem("GameObject/UI/Align/Right 【右对齐】")] static void AlignRight() { Align(AlignType.Right); } [MenuItem("GameObject/UI/Align/Top 【顶端对齐】")] static void AlignTop() { Align(AlignType.Top); } [MenuItem("GameObject/UI/Align/VerticalCenter 【垂直居中】")] static void AlignVerticalCenter() { Align(AlignType.VerticalCenter); } [MenuItem("GameObject/UI/Align/Bottom 【底端对齐】")] static void AlignBottom() { Align(AlignType.Bottom); } [MenuItem("GameObject/UI/Align/Horizontal 【横向分布】")] static void AlignHorizontal() { Align(AlignType.Horizontal); } [MenuItem("GameObject/UI/Align/Vertical 【纵向分布】")] static void AlignVertical() { Align(AlignType.Vertical); } public static void Align(AlignType type) { List<RectTransform> rects = new List<RectTransform>(); GameObject[] objects = GetOrderedSelctionObjs(); if (objects != null && objects.Length > 0) { for (int i = 0; i < objects.Length; i++) { RectTransform rect = objects[i].GetComponent<RectTransform>(); if (rect != null) rects.Add(rect); } } //如果只选中一个,对父节点对齐 if (rects.Count == 1) { if (rects[0].parent != null && rects[0].parent.GetComponent<RectTransform>() != null) { rects.Insert(0, rects[0].parent.GetComponent<RectTransform>()); } } if (rects.Count > 1) { Align(type, rects); } } //按选中顺序获取GameObjects private static GameObject[] GetOrderedSelctionObjs() { return Selection.objects.OfType<GameObject>().ToArray(); } public static void Align(AlignType type, List<RectTransform> rects) { RectTransform tenplate = rects[0]; float w = tenplate.sizeDelta.x * tenplate.lossyScale.x; float h = tenplate.sizeDelta.y * tenplate.lossyScale.y; float x = tenplate.position.x - tenplate.pivot.x * w; float y = tenplate.position.y - tenplate.pivot.y * h; switch (type) { case AlignType.Top: for (int i = 1; i < rects.Count; i++) { RectTransform trans = rects[i]; float th = trans.sizeDelta.y * trans.lossyScale.y; Vector3 pos = trans.position; pos.y = y + h - th + trans.pivot.y * th; SetTranPos(trans, pos); } break; case AlignType.Left: for (int i = 1; i < rects.Count; i++) { RectTransform trans = rects[i]; float tw = trans.sizeDelta.x * trans.lossyScale.x; Vector3 pos = trans.position; pos.x = x + tw * trans.pivot.x; SetTranPos(trans, pos); } break; case AlignType.Right: for (int i = 1; i < rects.Count; i++) { RectTransform trans = rects[i]; float tw = trans.sizeDelta.x * trans.lossyScale.x; Vector3 pos = trans.position; pos.x = x + w - tw + tw * trans.pivot.x; SetTranPos(trans, pos); } break; case AlignType.Bottom: for (int i = 1; i < rects.Count; i++) { RectTransform trans = rects[i]; float th = trans.sizeDelta.y * trans.lossyScale.y; Vector3 pos = trans.position; pos.y = y + th * trans.pivot.y; SetTranPos(trans, pos); } break; case AlignType.HorizontalCenter: for (int i = 1; i < rects.Count; i++) { RectTransform trans = rects[i]; float tw = trans.sizeDelta.x * trans.lossyScale.x; Vector3 pos = trans.position; pos.x = x + 0.5f * w - 0.5f * tw + tw * trans.pivot.x; SetTranPos(trans, pos); } break; case AlignType.VerticalCenter: for (int i = 1; i < rects.Count; i++) { RectTransform trans = rects[i]; float th = trans.sizeDelta.y * trans.lossyScale.y; Vector3 pos = trans.position; pos.y = y + 0.5f * h - 0.5f * th + th * trans.pivot.y; SetTranPos(trans, pos); } break; case AlignType.Horizontal: float minX = GetMinX(rects); float maxX = GetMaxX(rects); rects.Sort(SortListRectTransformByX); float distance = (maxX - minX) / (rects.Count - 1); for (int i = 1; i < rects.Count - 1; i++) { RectTransform trans = rects[i]; Vector3 pos = trans.position; pos.x = minX + i * distance; SetTranPos(trans, pos); } break; case AlignType.Vertical: float minY = GetMinY(rects); float maxY = GetMaxY(rects); rects.Sort(SortListRectTransformByY); float distanceY = (maxY - minY) / (rects.Count - 1); for (int i = 1; i < rects.Count - 1; i++) { RectTransform trans = rects[i]; Vector3 pos = trans.position; pos.y = minY + i * distanceY; SetTranPos(trans, pos); } break; } } private static void SetTranPos(Transform tran, Vector3 pos) { Undo.RecordObject(tran, "modify posstion"); tran.position = pos; } private static int SortListRectTransformByX(RectTransform r1, RectTransform r2) { float w = r1.sizeDelta.x * r1.lossyScale.x; float x1 = r1.position.x - r1.pivot.x * w; w = r2.sizeDelta.x * r2.lossyScale.x; float x2 = r2.position.x - r2.pivot.x * w; if (x1 >= x2) return 1; else return -1; } private static int SortListRectTransformByY(RectTransform r1, RectTransform r2) { float w = r1.sizeDelta.y * r1.lossyScale.y; float y1 = r1.position.y - r1.pivot.y * w; w = r2.sizeDelta.y * r2.lossyScale.y; float y2 = r2.position.y - r2.pivot.y * w; if (y1 >= y2) return 1; else return -1; } private static float GetMinX(List<RectTransform> rects) { if (null == rects || rects.Count == 0) return 0; RectTransform tenplate = rects[0]; float minx = tenplate.position.x; float tempX = 0; for (int i = 1; i < rects.Count; i++) { tempX = rects[i].position.x; if (tempX < minx) minx = tempX; } return minx; } private static float GetMaxX(List<RectTransform> rects) { if (null == rects || rects.Count == 0) return 0; RectTransform tenplate = rects[0]; float maxX = tenplate.position.x; float tempX = 0; for (int i = 1; i < rects.Count; i++) { tempX = rects[i].position.x; if (tempX > maxX) maxX = tempX; } return maxX; } private static float GetMinY(List<RectTransform> rects) { if (null == rects || rects.Count == 0) return 0; RectTransform tenplate = rects[0]; float minY = tenplate.position.y; float tempX = 0; for (int i = 1; i < rects.Count; i++) { tempX = rects[i].position.y; if (tempX < minY) minY = tempX; } return minY; } private static float GetMaxY(List<RectTransform> rects) { if (null == rects || rects.Count == 0) return 0; RectTransform tenplate = rects[0]; float maxY = tenplate.position.y; float tempX = 0; for (int i = 1; i < rects.Count; i++) { tempX = rects[i].position.y; if (tempX > maxY) maxY = tempX; } return maxY; } } 五、代码下载 https://github.com/dandkong/UGUIAlign ...

2022-11-16 · Dand