【为什么要做自动化工具】
工具类的创建是为了解决实际问题或者优化既有流程,我们来先看看一些项目里面经常遇到的问题。
如果计算机能帮我们直接创建一个功能的基础脚本类,就不用每次去复制上次的代码了。然后再帮我们把那些乱七八糟又数不胜数的按钮、文字、图片组件都自动生成在脚本里面,然后自己去关联好引用,一下就能节省好多重复的活。
效果展示

【相关功能实现与解析】
完整的脚本:
using UnityEngine;
using UnityEditor;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Text;
public class AutoBuildTemplate
{
   public static string UIClass =
@"using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System;
public class #类名# : MonoBehaviour
{
//auto
   public void Start()
    {
        #查找#
    }
    #成员#
}
";
}
public class AutoBuild
{
   [MenuItem("生成/创建或刷新界面")]
   public static void BuildUIScript()
   {
       var dicUIType = new Dictionary<string, string>();
       dicUIType.Add("Img", "Image");
       dicUIType.Add("Btn", "Button");
       dicUIType.Add("Txt", "Text");
       dicUIType.Add("Tran", "Transform");
       GameObject[] selectobjs = Selection.gameObjects;
       foreach (GameObject go in selectobjs)
       {
           //选择的物体
           GameObject selectobj = go.transform.root.gameObject;
           //物体的子物体
           Transform[] _transforms = selectobj.GetComponentsInChildren<Transform>(true);
           List<Transform> childList = new List<Transform>(_transforms);
           //UI需要查询的物体
           var mainNode = from trans in childList where trans.name.Contains('_') && dicUIType.Keys.Contains(trans.name.Split('_')[0]) select trans;
           var nodePathList = new Dictionary<string, string>();
           //循环得到物体路径
           foreach (Transform node in mainNode)
           {
               Transform tempNode = node;
               string nodePath = "/" + tempNode.name;
               while (tempNode != tempNode.root)
               {
                   tempNode = tempNode.parent;
                   int index = nodePath.IndexOf('/');
                   nodePath = nodePath.Insert(index, "/" + tempNode.name);
               }
               nodePathList.Add(node.name, nodePath);
           }
           //成员变量字符串
           string memberstring = "";
           //查询代码字符串
           string loadedcontant = "";
           foreach (Transform itemtran in mainNode)
           {
               string typeStr = dicUIType[itemtran.name.Split('_')[0]];
               memberstring += "public " + typeStr + " " + itemtran.name + " = null;\r\n\t";
               loadedcontant += itemtran.name + " = " + "gameObject.transform.Find(\"" + nodePathList[itemtran.name] + "\").GetComponent<" + typeStr + ">();\r\n\t\t";
           }
           string scriptPath = Application.dataPath + "/Scripts/" + selectobj.name + ".cs";
           string classStr = "";
           //如果已经存在了脚本,则只替换//auto下方的字符串
           if (File.Exists(scriptPath))
           {
               FileStream classfile = new FileStream(scriptPath, FileMode.Open);
               StreamReader read = new StreamReader(classfile);
               classStr = read.ReadToEnd();
               read.Close();
               classfile.Close();
               File.Delete(scriptPath);
               string splitStr = "//auto";
               string unchangeStr = Regex.Split(classStr, splitStr, RegexOptions.IgnoreCase)[0];
               string changeStr = Regex.Split(AutoBuildTemplate.UIClass, splitStr, RegexOptions.IgnoreCase)[1];
               StringBuilder build = new StringBuilder();
               build.Append(unchangeStr);
               build.Append(splitStr);
               build.Append(changeStr);
               classStr = build.ToString();
           }
           else
           {
               classStr = AutoBuildTemplate.UIClass;
           }
           classStr = classStr.Replace("#类名#", selectobj.name);
           classStr = classStr.Replace("#查找#", loadedcontant);
           classStr = classStr.Replace("#成员#", memberstring);
           FileStream file = new FileStream(scriptPath, FileMode.CreateNew);
           StreamWriter fileW = new StreamWriter(file, System.Text.Encoding.UTF8);
           fileW.Write(classStr);
           fileW.Flush();
           fileW.Close();
           file.Close();
           Debug.Log("创建脚本 " + Application.dataPath + "/Scripts/" + selectobj.name + ".cs 成功!");
           AssetDatabase.SaveAssets();
           AssetDatabase.Refresh();
       }
   }
}
脚本解析:
AutoBuildTemplate类
AutoBuild类
    var dicUIType = new Dictionary<string, string>();
       dicUIType.Add("Img", "Image");
       dicUIType.Add("Btn", "Button");
       dicUIType.Add("Txt", "Text");
       dicUIType.Add("Tran", "Transform");
var mainNode = from trans in childList where trans.name.Contains('_') && dicUIType.Keys.Contains(trans.name.Split('_')[0]) select trans;
string unchangeStr = Regex.Split(classStr, splitStr, RegexOptions.IgnoreCase)[0];
 classStr = classStr.Replace("#类名#", selectobj.name);
  FileStream file = new FileStream(scriptPath, FileMode.CreateNew);
           StreamWriter fileW = new StreamWriter(file, System.Text.Encoding.UTF8);
           fileW.Write(classStr);
           fileW.Flush();
           fileW.Close();
           file.Close();
AssetDatabase.SaveAssets();
           AssetDatabase.Refresh();
【总结】
以上利用UI预制体代码自动生成为例讲解了自动化生成的方法,我这里是通过Find来查找物体引用的,当然可以利用Unity序列化参数的方法来赋值(就是拖拽操作的赋值方法),用后者可以节约UI第一次打开的性能(毕竟Unity的Find还是很消耗性能的),可以在我们的脚本创建好后加入给预制体挂载脚本赋值的功能流程。
自动化思想是伟大的,可以用到自动化的地方还有很多,比如统计当前的资源加载列表,将策划的表文件生成类文件,生成版本控制脚本。
如果你对你的项目有改良性建议,你认为可以而且应该实现自动化任务,就可以尝试去实现,一旦你已经成功了,节省的可不仅仅是开发的时间。
感谢皮皮关的分享,转载于https://zhuanlan.zhihu.com/p/30716595










