在写了不少代码之后,才发现以前写的很多代码都是重复性的,虽然这样的重复劳动让程序员形成了自己的代码风格,但一直这样下去并不是十分明智的方式。
-----------------------------------------------------写在前面
本文将利用unity的编辑器扩展功能,结合开源的Rotorz列表生成器,实现一个简单但有意义的代码模板生成。其中生成时使用的模板来自于本人平时的编程习惯,如果需要使用这个脚本,可以修改前面定义的字符串模板便可。
使用方式:
[第一步:定义名称]
在hierarchy面板中定义好相关控件的名称,本文仅实现了image,text,toggle,button,slider,inputfield这几个常用控件的代码生成,根据自己的编程模式可以自行扩展,以减少不必要的重复劳动,而把精力放在实现具体的业务逻辑上。
(图一)
[第二步:记录控件]
一种是手动将这些控件拖入到编辑窗口中,这样比较慢,于是本文实现了快速记录指定可交互控件的方式。由于image和文字并不一定要在脚本中获取到并进行设置信息,所以可以区别于其他控件。
如图二所示窗口的左边是控件输入框
[第三步:复制代码]
利用类内写好的代码模板,已经可以生成出指定的c#代码了,如图二所示,点击其中的复制代码,就已经将生成的代码拷贝成功,只需要在指定的脚本中去黏贴就好了。此时并没有实现直接将生成的代码写入到指定的脚本文件中。
[第四步:定向生成]
在目标对象框中拖入NewBehaviourScript的GameObject对象,点击加载脚本,就可以快速将其身上的脚本读取出来。此时,将该脚本打钩,点击保存到脚本,相关的数据就成功写入到指定的脚本文件中了,如果图三所示。但目前还需要用编辑器打开一下才能看到指定的脚本。此时正好可以做适当的微调。
(图二)
(图三)
[第五步:连接到UI]
由于生成了代码,但由于还需要将控件在Inspector面板中进行绑定,于是本文也利用反射等知识将对象快速绑定到指定的位置,如果图四所示。到此就已经完成了对这个编辑器窗口的代码生成功能的使用。想想如果去写这么多行代码至少需要三分钟,这样自己生成再绑定好,最多也就一份钟。把节约的时候用来考虑功能的具体实现多好。
(图四)
关键点说明:
[问题一:字符串生成中]
本来想就用string.Format();就可以实现将指定的名称插入到指定的字符串中,但由于方法体中也有{}这样的符号,直接报错了,于是改成了Replace,稍微有点乱的地方:
private string GetCodeStr()
{
string str = "";
#region 记录全局变量
TraverseGraphic((gra) =>
{
if (gra is Image)
{
str += string.Format(imgFormat, gra.name);
}
else if (gra is Text)
{
str += string.Format(txtFormat, gra.name);
}
else if (gra is RawImage)
{
str += string.Format(rawimgFormat, gra.name);
}
});
TraverseSelectable((sele) =>
{
if (sele is Button)
{
str += string.Format(btnFormat, sele.name);
}
else if (sele is Toggle)
{
str += string.Format(togFormat, sele.name);
}
else if (sele is Slider)
{
str += string.Format(slidFormat, sele.name);
str += string.Format(sliderDataFormat, sele.name);
}
else if (sele is InputField)
{
str += string.Format(inptFormat, sele.name);
str += string.Format(inputDataFormat, sele.name);
}
});
#endregion
#region 记录事件注册
str += "\tprivate void Awake()\n\t{\n";
TraverseSelectable((sele) =>
{
if (sele is Button)
{
str += string.Format(onClickFormat, sele.name);
}
else if (sele is Toggle || sele is Slider || sele is InputField)
{
str += string.Format(onValueChangeFormat, sele.name);
}
});
str += "\t}\n";
#endregion
#region 记录方法
TraverseSelectable((sele) =>
{
if (sele is Button)
{
str += btnFuncFormat.Replace("{0}", sele.name);
}
else if (sele is Toggle)
{
str += togFuncFormat.Replace("{0}", sele.name);
}
else if (sele is Slider)
{
str += sliderFuncFormat.Replace("{0}", sele.name);
}
else if (sele is InputField)
{
str += inputFuncFormat.Replace("{0}", sele.name);
}
});
#endregion
return str;
}
[问题二:私有字段的赋值]
本来以为反射对私有字段操作无解,没想到是我研究的够深,反射原来这么强大,参考高手的博客=》
C#反射:让私有成员无所遁形
type.InvokeMember("m_" + sele.name,
BindingFlags.SetField |
BindingFlags.Instance |
BindingFlags.NonPublic,
null, Selected[i], new object[] { sele }, null, null, null);
[问题三:回调加递归调用实现遍历]
一开始找控件时,本来就使用了个简单的foreach(Transform in transform),但这样没有找到子层级的对象,于是就用了下面这个回调加递归进行遍历的方法。
public static void Recursive(Transform parent, UnityAction<Transform> Func)
{
Func(parent);
if (parent.childCount >= 0)
{
for (int i = 0; i < parent.childCount; i++)
{
Transform child = parent.GetChild(i);
Recursive(child, Func);
}
}
}
程序源码下载:下面这个上传到github上的程序源码,欢迎使用和指正