開發的時候必不可少的需要要獲取一個界面的子對象組件,雖然很簡單但是如果每次獲取都敲一次代碼,也是一鍵很恐怖的事情。
現在項目用了一個工具,對着界面點一下,就會導出匹配這個界面結構的類。只要初始化這個類,就可以很方便的獲取這個界面子對象的組件。
工具思路是這樣的:
1、根據子對象的命名來確定需要獲取的組件。比如后綴_txt表示UILabel,_go表示GameObject。
2、遍歷所有子對象,將帶后綴的子對象緩存起來。
3、根據緩存的子對象,按照一定字符串拼接規則,寫到本地腳本。
一個界面結構和導出的界面腳本結果如下


只要把界面的資源(Gameobject)傳進去,就可以直接獲取子對象的組件,大大提高了開發效率。當然了,工程里對這個腳本進行了一系列封裝和抽象,這里是單獨摘出來演示的。
(ps:這是我第一個項目,所以不清楚其他項目怎么處理這個問題的,如果有更好的思路務必告訴我)
閑話少說,直接上干貨。(注釋寫的比較全,就不秀我那渣渣的表達能力了)
工具腳本:
using System.Collections.Generic; using System.IO; using System.Text; using UnityEditor; using UnityEngine; public class CreateSprite { //當前操作的對象 private static GameObject CurGo; //后綴對應的組件類型 public static Dictionary<string, string> typeMap = new Dictionary<string, string>() { { "sp", typeof(UISprite).Name }, { "txt", typeof(UILabel).Name }, { "btn", typeof(UIButton).Name }, { "go", typeof(GameObject).Name}, }; //腳本模版 private static CreateSpriteUnit info; //在Project窗口下,選中要導出的界面,然后點擊GameObject/導出腳本 [MenuItem("GameObject/導出腳本")] public static void CreateSpriteAction() { GameObject[] gameObjects = Selection.gameObjects; //保證只有一個對象 if (gameObjects.Length==1) { info = new CreateSpriteUnit(); CurGo = gameObjects[0]; ReadChild(CurGo.transform); info.classname = CurGo.name + "UIPanel"; info.WtiteClass(); info = null; CurGo = null; } else { EditorUtility.DisplayDialog("警告", "你只能選擇一個GameObject", "確定"); } } //遍歷所有子對象,GetChild方法只能獲取第一層子對象。 public static void ReadChild(Transform tf) { foreach (Transform child in tf) { string[] typeArr = child.name.Split('_'); if (typeArr.Length > 1) { string typeKey = typeArr[typeArr.Length - 1]; if (typeMap.ContainsKey(typeKey)) { info.evenlist.Add(new UIInfo(child.name, typeKey, buildGameObjectPath(child).Replace(CurGo.name + "/",""))); } } if (child.childCount > 0) { ReadChild(child); } } } //獲取路徑,這個路徑是帶當前對象名的,需要用Replace替換掉頭部 private static string buildGameObjectPath(Transform obj) { var buffer = new StringBuilder(); while (obj != null) { if (buffer.Length > 0) buffer.Insert(0, "/"); buffer.Insert(0, obj.name); obj = obj.parent; } return buffer.ToString(); } } //導出腳本的模版 public class CreateSpriteUnit { public string classname; public string template = @" using UnityEngine; using System.Collections; using System.Collections.Generic; public class @ClassName { @fields public void OnAwake(GameObject viewGO) { @body1 } public void OnDestroy() { @body2 } } "; //緩存的所有子對象信息 public List<UIInfo> evenlist = new List<UIInfo>(); /// <summary> /// 把拼接好的腳本寫到本地。 /// (自己可以個窗口支持改名和選擇路徑,真實工程里是帶這些功能的) /// </summary> public void WtiteClass() { bool flag = true; bool throwOnInvalidBytes = false; UTF8Encoding encoding = new UTF8Encoding(flag, throwOnInvalidBytes); bool append = false; StreamWriter writer = new StreamWriter(Application.dataPath + "/" + classname + ".cs", append, encoding); writer.Write(GetClasss()); writer.Close(); AssetDatabase.Refresh(); } //腳本拼接 public string GetClasss() { var fields = new StringBuilder(); var body1 = new StringBuilder(); var body2 = new StringBuilder(); for (int i = 0; i < evenlist.Count; i++) { fields.AppendLine("\t" + evenlist[i].field); body1.AppendLine("\t\t" + evenlist[i].body1); body2.AppendLine("\t\t" + evenlist[i].body2); } template = template.Replace("@ClassName", classname).Trim(); template = template.Replace("@body1", body1.ToString()).Trim(); template = template.Replace("@body2", body2.ToString()).Trim(); template = template.Replace("@fields", fields.ToString()).Trim(); return template; } } //子對象信息 public class UIInfo{ public string field; public string body1; public string body2; public UIInfo(string name, string typeKey, string path) { field = string.Format("public {0} {1};", CreateSprite.typeMap[typeKey], name); if (typeKey == "go") { body1 = string.Format("{0} = viewGO.transform.Find(\"{1}\").gameObject;", name, path, CreateSprite.typeMap[typeKey]); } else { body1 = string.Format("{0} = viewGO.transform.Find(\"{1}\").GetComponent<{2}>();", name, path, CreateSprite.typeMap[typeKey]); } body2 = string.Format("{0} = null;", name); } }

