推广

Unity 代码自动化生成!

iseeyu3年前 (2024-02-22)推广122

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.0291.com.cn/post/56380.html

相关文章

西安企业做百度推广,一个月花三千和花三万到底差在哪?

4|最近跟几个西安的老板聊天,发现一个挺有意思的现象:同样做百度推广,有人一个月花三千块,询盘不断;有人一个月砸三万,水花都没见着。问他们区别在哪,大多数人说不清楚,只知道'钱花了,没效果'。 5| 6|百度推广这东西,说复杂也复杂,说简单也简单。核心就一件事:让你的潜在客户搜某个词的时候,你的信息...

我想当策划,大学应该选什么专业?

我想当策划,大学应该选什么专业?

学,广告,传播,公关,这些专业其实都可以。另外也可以学像汉语言文学,以及一些跟文学相关的一些专业,会对你找工作有帮助。但是,你想从事这个这份职业,除了专业的学习之外,更多的是,你要在上学的时候积极参与一些更营销策划相关的社会实践。比如说你可以做一些兼职,比如说帮别人去写稿,...

服装市场营销策略分析,“定位不慎满盘皆输”

服装市场营销策略分析,“定位不慎满盘皆输”

不同的区域需要不同的策略,而不同的营销策略需要不同的销售思路来配合。因此,在研究女装产品销售思路之前,最好先对于市场与营销策略做一次深入地了解。1.市场区域化(market segmentation):目标营销的第一步,是将整个市场拆分成若干个同性质的小市场。2.目标市场的...

晚间悦读丨你的思维方式,决定你的人生高度!

晚间悦读丨你的思维方式,决定你的人生高度!

越长大,越发现,能够决定你人生高度的,往往是你的认知层次。 你的认知在哪个层级,你的人生就处在什么状态。你认知的高度,决定了你人生的高度。 那如何才能升级方式、提升认知能力呢?可以从这三个方面做出改变。 NO. 1 改变态度 有人...

编写项目营销策划方案六大要点

编写项目营销策划方案六大要点

现在商业发展非常迅速,的需求非常的大,但是要做好营销策划是有一定难度的,我把自己做营销策划的一些体会在这里分享给朋友们。营销策划方案六大要点我们先说项目营销策划方案这个话题。大部分的营销策划人都是在企业里工作,大部分项目之初都是来源于一个想法,创始人、老板或者创始团队的一个...

【商场营销策划】如何进行商场营销策划?

【商场营销策划】如何进行商场营销策划?

商场是策划者们关注的永恒话题,那么究竟该如何作好商场营销策划呢?掌握以下这几点营销策略设计原则,至关重要!1.义利兼顾的原则。商场在获取利润的同时要充分考虑到消费者的合法利益和商场的长远利益。在作商场活动营销策略方案的时候应该将这一点作为首要的原则。对商场有利而对顾客不利的...

现在,非常期待与您的又一次邂逅

我们努力让每一部企业宣传片和抖音短视频成为商业大片