【Unity】工具类系列教程—— 代码自动化生成!
转载自:https://zhuanlan.zhihu.com/p/30716595?utm_medium=social&utm_source=qq
【为什么要做自动化工具】
工具类的创建是为了解决实际问题或者优化既有流程,我们来先看看一些项目里面经常遇到的问题。
程序代码有很多时候类是相似的,因此新建一个功能的时候你会直接复制了之前"相似"的代码块,然后删除掉无用的逻辑功能,但是有时候会出现漏网之鱼。
开完会,策划发过来一个功能案子,UI相关的界面非常的多,你费劲拼完了UI,写脚本类的时候将你用到的一些组件一个一个写了函数变量加进脚本中去,有时候一运行发现报错,结果是有一个函数没有赋值,要么是有一个按钮没有生成变量,创建到调试中间花费大量时间精力。
功能做完了,按理来说你应该放松下来喝一杯咖啡,但是你一更新发现UI预制体的红色冲突警告心直接凉了半截,解决预制体冲突后,又一遍一遍的将丢失的引用赋值到脚本上。
如果计算机能帮我们直接创建一个功能的基础脚本类,就不用每次去复制上次的代码了。然后再帮我们把那些乱七八糟又数不胜数的按钮、文字、图片组件都自动生成在脚本里面,然后自己去关联好引用,一下就能节省好多重复的活。
效果展示

【相关功能实现与解析】
完整的脚本:
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 提供程序可用的任何其他格式的数据。
资料地址https://docs.microsoft.com/zh-cn/dotnet/csharp/linq/linq-in-csharp*/
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还是很消耗性能的),可以在我们的脚本创建好后加入给预制体挂载脚本赋值的功能流程。
自动化思想是伟大的,可以用到自动化的地方还有很多,比如统计当前的资源加载列表,将策划的表文件生成类文件,生成版本控制脚本。
如果你对你的项目有改良性建议,你认为可以而且应该实现自动化任务,就可以尝试去实现,一旦你已经成功了,节省的可不仅仅是开发的时间。

如果没活不要作死
对游戏开发感兴趣的同学,欢迎围观我们:【皮皮关游戏开发教育】 ,会定期更新各种教程干货,更有别具一格的线下小班教育。在你学习进步的路上,有皮皮关陪你!~
我们的官网地址:http://levelpp.com/
我们的游戏开发技术交流群:610475807
我们的微信公众号:皮皮关
【Unity】工具类系列教程—— 代码自动化生成!的更多相关文章
- java工具类系列 (四.SerializationUtils)
java工具类系列 (四.SerializationUtils) SerializationUtils该类为序列化工具类,也是lang包下的工具,主要用于序列化操作 import java.io.Se ...
- java工具类–自动将数据库表生成javabean
最近和数据库的表打交道挺多的,因为暂时做的是接口活. 在这过程中发现要把表转换成对应的javabean类型,字段少的表还行,如果不小心碰到几十个字段的他妈的写起来就有点麻烦了,万一碰到几百个的呢,那不 ...
- 【Unity】UGUI系列教程——拼接一个简单界面
0.简介: 在目前的游戏市场上,手游依然是市场上的主力军,而只有快速上线,玩法系统完善的游戏才能在国内市场中占据份额.而在手游开发过程中,搭建UI系统是非常基本且重要的技能,极端的说如果对Unity的 ...
- Java工具类_表结构自动生成对应的实体类、Mapper.xml文件、Dao类
import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWrit ...
- 常用工具类系列之DateUtil
Date.long.Calendar之间的相互转换 //当前时间 Date date = DateUtil.date(); //当前时间 Date date2 = DateUtil.date(Cale ...
- U3D 代码自动化生成定制预置体的旋转问题
//定制预置体 //要求:1,模型面向U3D的Z轴正向(由MAX导出时是面向U3D的X负向的) //2,增加一些常用挂点,3增加一个圆形阴影片,4,添加包围盒 //根据这些要求制作预置休 static ...
- Android开发之dp转像素,像素转换为dp工具类,详细代码,带有源文件下载地址。
import android.content.Context; /** * @author 官网:http://blog.csdn.net/qq_21376985 * * David编写: 微博:ht ...
- 【Android代码片段之六】Toast工具类(实现带图片的Toast消息提示)
转载请注明出处,原文网址:http://blog.csdn.net/m_changgong/article/details/6841266 作者:张燕广 实现的Toast工具类ToastUtil封装 ...
- 生成count位随机数工具类
工具类 import java.util.Random; /** * 生成6位随机数字 */ public class GeneratorCode { /** * * @Title: getCode ...
随机推荐
- Java集合类总结 (五)
集合框架 为了避免进行不必要的随机访问操作,Java引入了一种标签接口RandomAccess, 这个接口没有任何方法,只是一个标签,用来标记一个集合是否应该进行随机访问操作: if (c insta ...
- Xcode更新至IOS 9 后错误处理
1.obtain an updated library from the vendor, or disable bitcode for this target. for architecture ar ...
- logback-记录日志
一:根节点<configuration>包含的属性: scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true. scanPeriod: 设置监测配 ...
- java學習書
轉載 成为Java顶尖程序员 ,看这11本书就够了 以下是我推荐给Java开发者们的一些值得一看的好书.但是这些书里面并没有Java基础.Java教程之类的书,不是我不推荐,而是离我自己学习 Java ...
- Thread Group(线程组)
线程组,可以理解用户池,用来产生线程(用户),每一个线程代表一个用户,在使用JMeter进行性能测试过程中,经常需要模拟多个用户进行测试,可以通过设置线程数代表多少个用户,通常一个线程组就代表一个测试 ...
- 从程序员的角度深入理解MySQL
前言 今天我将站在程序员的角度以MySQL为例探索数据库的奥秘! 数据库基本原理 我对DB的理解 1.数据库的组成:存储 + 实例 不必多说,数据当然需要存储:存储了还不够,显然需要提供程序对存储 ...
- kali 插耳机没声音
打开终端,然后输入命令:下载pulseaudio音量控制软件: apt install pavucontrol 然后终端输入指令:pavucontrol打开软件,发现在输出设备中有两个输出设备:一个N ...
- checkbox的常见问题
1.在使用checkbox时,最好不要阻止他原有的事件,要利用它原有的事件进行控制 2.尽量使用label for属性,不要对input元素的父元素或者input本身绑定事件,这样不能有效的避免冒泡事 ...
- Axure 原型图
Axure RP 9最新版软件及汉化包下载 https://www.axure.com.cn/78629/ 点击下载 lang语言包放在根目录即可 Axure各种版本注册码 | 最新Axure R ...
- [POI2007]ZAP-Queries 数学
题目描述 Byteasar the Cryptographer works on breaking the code of BSA (Byteotian Security Agency). He ha ...