Unity 代码自动化生成!

image

【相关功能实现与解析】

完整的脚本:

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类

Unity中的C#脚本代码本质上为包含字符串内容的文本文件,以.cs后缀保存。因此我们如果要自动生成脚本只用编辑好代码的文本内容,然后添加文件后缀保存文件就完成了。
我们替换其中的 #类名#、#查找#、#成员#,保存成xxx.cs的文件就可以生成一个脚本类出来。

AutoBuild类

GameObject[] selectobjs = Selection.gameObjects;
Unity中可以在Editor脚本调用Selection类得到当前选中的物体。因为存在多选情况,返回的物体为一个数组。

    var dicUIType = new Dictionary<string, string>();
       dicUIType.Add("Img", "Image");
       dicUIType.Add("Btn", "Button");
       dicUIType.Add("Txt", "Text");
       dicUIType.Add("Tran", "Transform");

外部按钮、图片、文本等组件物体的关键字与类型的映射,当子物体中名字存在“Img”、“Btn”就识别为Image和Button。

var mainNode = from trans in childList where trans.name.Contains('_') && dicUIType.Keys.Contains(trans.name.Split('_')[0]) select trans;

Linq的from 、where 、select 语句遍历寻找出前缀存在字典映射中的物体。
/查询是一种从数据源检索数据的表达式。 查询通常用专门的查询语言来表示。 随着时间的推移,人们已经为各种数据源开发了不同的语言;例如,用于关系数据库的 SQL 和用于 XML 的 XQuery。 因此,开发人员对于他们必须支持的每种数据源或数据格式,都不得不学习一种新的查询语言。 LINQ 通过提供一种跨各种数据源和数据格式使用数据的一致模型,简化了这一情况。 在 LINQ 查询中,始终会用到对象。 可以使用相同的基本编码模式来查询和转换 XML 文档、SQL 数据库、http://ADO.NET 数据集、.NET 集合中的数据以及对其有 LINQ 提供程序可用的任何其他格式的数据。
资料地址<u style=”text-decoration: none; border-bottom: 1px dashed grey;”>https://docs.microsoft.com/zh-cn/dotnet/csharp/linq/linq-in-csharp</u>
/

string unchangeStr = Regex.Split(classStr, splitStr, RegexOptions.IgnoreCase)[0];

正则表达式去分割字符串,因为当脚本已经存在,我们不能覆盖掉已经书写的代码,所以基础文本中有一个//auto来分割自动生成代码区域和手写区域。

 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();

创建流文件,当写入完成后关闭流。在Unity生成了物体必须调用 AssetDatabase.SaveAssets()和AssetDatabase.Refresh()才能即时的看到资源刷新。

【总结】

以上利用UI预制体代码自动生成为例讲解了自动化生成的方法,我这里是通过Find来查找物体引用的,当然可以利用Unity序列化参数的方法来赋值(就是拖拽操作的赋值方法),用后者可以节约UI第一次打开的性能(毕竟Unity的Find还是很消耗性能的),可以在我们的脚本创建好后加入给预制体挂载脚本赋值的功能流程。

自动化思想是伟大的,可以用到自动化的地方还有很多,比如统计当前的资源加载列表,将策划的表文件生成类文件,生成版本控制脚本。

如果你对你的项目有改良性建议,你认为可以而且应该实现自动化任务,就可以尝试去实现,一旦你已经成功了,节省的可不仅仅是开发的时间。

感谢皮皮关的分享,转载于https://zhuanlan.zhihu.com/p/30716595

原创文章,作者:忆中异,如若转载,请注明出处:https://www.miaoshou.cc/166725.html

(0)
上一篇 2021年8月27日 15:47
下一篇 2021年8月27日 15:47

相关推荐

  • 产品推广的渠道有哪些(产品推广的渠道推荐)

    产品推广的渠道有很多种,比如网站建设、微博、微信公众号、朋友圈、QQ群、贴吧论坛、博客、论坛、微博等。每个渠道都有各自的特点和优势。因此要根据自己的实际情况去选择最适合自己的渠道。…

    推广 2022年12月31日
  • 网络推广运营主要做什么( 这篇文章告诉你答案)

    互联网在近几年里,发展迅猛,网络推广运营工作人员在企业中越来越不可或缺。互联网给企业的生产制造和营销推广方式带来了很大冲击与改变。因此,在公司发展壮大的过程中出现了很多不同类型的网…

    2022年12月7日
  • Jenkins教程:使用Jenkins进行持续集成

    image   单台 Jenkins 服务器不足以满足某些要求,例如: 有时您可能需要几个不同的环境来测试您的构建。单个 Jenkins 服务器无法完成此操作。 如果定期构建更大、…

    推广 2021年8月27日
  • 《MySQL面试小抄》索引考点二面总结

    索引二面1 索引二面2 面试官考点之谈谈索引维护过程?页分裂?页合并? B+树为了维护索引有序性,在插入删除的时候需要做必要的维护,必要时候可能涉及到页分裂,页合并过程! 首先假设…

    推广 2021年8月28日
  • RocketMQ

    阿里巴巴消息中间件起源于2001年的五彩石项目,Notify在这期间应运而生,用于交易核心消息的流转。
    2010年,B2B开始大规模使用ActiveMQ作为消息内核,随着阿里业务的快速发展,急需一款支持顺序消息、拥有海量消息堆积能力的消息中间件,MetaQ1.0在2011年诞生。
    2012年,MetaQ已经发展到了3.0版本,并抽象出了通用的消息引擎RocketMQ。随后,对RocketMQ进行了开源,阿里的消息中间件正式走人了公众视野。
    2015年,RocketMQ已经经历了多年双十一的洗礼,在可用性、可靠性以及稳定性等方面都有出色的表现。与此同时,云计算大行其道,阿里消息中间件基于RocketMQ推出了AliwareMQ1.0,开始为阿里云上成千上万家企业提供消息服务。
    2016年,MetaQ在双十一期间承载了万亿级消息的流转,跨越了一个新的里程碑,同时RocketMQ进入Apache孵化。

    推广 2021年8月27日