前言
项目这个东西果然还是做起来才会发现坑,尽量早填上好了
View Prefab
上一篇的vwTest这个UI预设体的根节点vwTest是一个Panel控件,内容是个背景框,这个想了下不太妥,应该改一下,包含具体内容的控件不应该在根节点中,现在把根节点改成透明panel,尺寸为整个View的尺寸。背景框相关的放到根节点下边。
添加UI
为了更方便地添加UI,我给SFSceneManager
加了几个静态方法,通过指定预设体、父节点的transform、层级顺序(越大越靠前),可以简化添加UI的步骤
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| static public GameObject addView(GameObject prefab, Transform trans = null, int sibIdx = -1) { if (prefab == null) { SFUtils.logWarning("prefab为空"); return null; } Transform parent = trans; if (parent == null) { parent = SFSceneManager.uiRoot.transform; } var GO = GameObject.Instantiate(prefab, parent) as GameObject; if (zOrder >= 0) { GO.SetSiblingIndex(sibIdx); } return GO; } static public GameObject addView(string viewName, Transform trans = null, int sibIdx = -1) { var prefab = Resources.Load(viewName) as GameObject; if (prefab == null) { SFUtils.logWarning(string.Format("找不到view:{0}", viewName)); return null; } return SFSceneManager.addView(prefab, trans, sibIdx); }
|
只提供第一个参数的话默认会加在整个UI界面的最前面,当然也可以指定加在某个自定义节点下面,也可以指定层级顺序
UI代码生成
设想的工作流程是这样:UI设计人员修改或创建UI Prefab,在Project视图里右键点击Prefab,会有一个导出生成代码的菜单项,点击它,就可以一键生成代码并且自动把View脚本挂载到Prefab上。为了实现这种效果,就必须扩展Unity的编辑器。
Unity提供了非常丰富的编辑器扩展接口,如添加菜单项,自定义Inspector面板,甚至自定义Scene视图中的辅助UI。
我们这里只使用添加菜单项的功能:在Assets中创建Editor文件夹,在里面创建一个C#脚本,文件名随意,然后写一个类,类名也随意:
1 2 3 4 5 6 7 8
| public class SFUIExporterMenu { [MenuItem("Assets/SF/Export UI")] private static void exportUI() { generateUICode(Selection.activeGameObject); } }
|
Selection.activeGameObject
表示当前在Project视图中选中的Prefab,如果选中的不是Prefab这个就是null。
MenuItem
特性表示这个方法用于扩展菜单项,参数"Assets/SF/Export UI"
表示这个自定义菜单项的位置,在Assets菜单中添加的话,在Asset上右键就可以看到相应的菜单项了
不过如果我手滑右键了其他非Prefab,显然这时是不应该允许导出的,因为这不是UI,所以还要加一个验证,选中非法的Asset时将菜单项置灰。
1 2 3 4 5 6
| [MenuItem("Assets/SF/Export UI", true)] private static bool exportUIValidation() { var GO = Selection.activeGameObject; return GO != null && GO.name.Substring(0, 2) == "vw"; }
|
特性的第二个参数true代表这个方法是用来验证合法性的,返回true为合法,false非法。
然后就是根据Prefab来生成代码并保存了
1 2 3 4 5 6 7
| string viewName = prefab.name.Substring(2); string viewFilepath = string.Format("Assets/Scripts/UI/SF%sView.cs", viewName); var viewFile = new FileInfo(viewFilepath); var sw = viewFile.CreateText(); sw.Write(viewContent); sw.Close();
|
自动挂载脚本
当然最后要把生成的View脚本挂载在Prefab上
1 2 3 4 5 6 7
| string componentName = "SF" + viewName + "View"; AssetDatabase.Refresh(); var component = prefab.GetComponent(componentName); if (component == null) { prefab.AddComponent(getTypeByName(componentName)); }
|
下面是根据名称获取类型的方法,因为Component.AddComponent(string)已经被官方在5.0版本中废弃了,我们必须通过其他的方式曲线救国,下面的方法参考了ぼちつく的博客,这个方法的运行效率必然很低,尤其是项目规模大起来之后,不过至少给编辑器用的扩展不会在游戏运行时的阶段执行,而且也不会太频繁,所以性能稍微差一点也是可以接受的
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| private static Type getTypeByName(string className) { foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) { foreach (Type type in assembly.GetTypes()) { if (type.Name == className) { return type; } } } return null; }
|
其他
研究编辑器扩展的时候发现了一个好玩儿的东西,继承UnityEditor.AssetModificationProcessor
并实现OnWillCreateAsset()
方法可以在创建Assets时收到通知,可以利用这个方法来给之后创建的脚本加上头部文件信息的注释。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class SFScriptHeaderGenerator : UnityEditor.AssetModificationProcessor { static private string header = "/**\n" + " * Created on ##DateTime## by ##UserName##\n" + " * All rights reserved.\n" + " */\n\n"; public static void OnWillCreateAsset(string path) { path = path.Replace(".meta", ""); if (path.EndsWith(".cs")) { string fullText = header; fullText = fullText.Replace("##DateTime##", System.DateTime.Now.ToString("yyyy/MM/dd")); fullText = fullText.Replace("##UserName##", System.Environment.UserName); fullText += File.ReadAllText(path); File.WriteAllText(path, fullText); } } }
|
效果如图:
完整代码
上面贴出的代码片段由于篇幅限制只保留了关键部分,完整的代码可在我的github上找到