最近遇到了一个新需求,要求使用代码进行预制体上脚本的替换。最开始的想法是写一个编辑器替换脚本,然后把预制体一个一个拖出来替换保存。但是这样无疑效率太低了,于是就从网上学习了一套不用实例化也可以修改预制体脚本的功能。
先贴一下实现代码:
public static string fullPath = "Assets/prefabs";
//string removeStr = "E:\\Resource_mobile_2017";
public static void ExcuteAllPrefab(Font font, string removeStr)
{
List<string> prefabs_names = new List<string>();
if (Directory.Exists(fullPath))
{
DirectoryInfo direction = new DirectoryInfo(fullPath);
FileInfo[] files = direction.GetFiles("*", SearchOption.AllDirectories);
for (int i = 0; i < files.Length; i++)
{
if (files[i].Name.EndsWith(".prefab"))
{
string name = files[i].DirectoryName + "\\" + files[i].Name;
name = name.Remove(0, removeStr.Length+1);
prefabs_names.Add(name);
}
}
}
for (int i = 0; i < prefabs_names.Count; i++)
{
GameObject go = AssetDatabase.LoadAssetAtPath<GameObject>(prefabs_names[i]);
Text[] texts = go.GetComponentsInChildren<Text>(true);
foreach (var text in texts)
{
if (text.font == font)
{
//something what you what to do
}
}
EditorUtility.SetDirty(go);
}
}
这个函数实现的就是查找所有fullPath文件夹下的预制体,找到所有的Text组件,如果Text组件使用的Font是函数中传入的Font的话,那就做一些事情进行处理。
首先,先使用fullPath查找里面的所有文件,SearchOption是设置了所有的子文件夹都进行遍历,遍历拿到的文件的结尾如果是.prefab,那就说明是我们要找的预制体文件。
这里要注意一下AssetDatabase.LoadAssetAtPath<T>(string)
这个函数中的参数string传入的是从Asset
文件夹开始的项目相对路径,而我们拿到的FileInfo我们只能拿到文件名与全部的文件夹路径。所以我们要对路径进行处理。我这里是传入了一个从盘符到项目文件夹的路径用以剪裁,虽然可以实现,但是感觉可能还有更好的方案,如果以后发现了再回来补充把。
接下来就是使用AssetDatabase获取到预制体文件并转成我们熟悉的GameObject,然后就可以正常的处理脚本了。记得在处理之后EditorUtility.SetDirty(go);
,只有在设置之后,在关闭unity的时候才会进行自动保存(当然也可以自己手动保存)。
public class TMPChangeWindow : EditorWindow {
static TMPChangeWindow window;
public static Font fontIns;
string removeStr = "E:\\Test";
public static void Execute()
{
if (window == null)
{
window = (TMPChangeWindow)GetWindow(typeof(TMPChangeWindow));
}
window.Show();
}
private void OnGUI()
{
GUILayout.Label("查找所有带此字体的预制体。字体:");
fontIns = (Font)EditorGUILayout.ObjectField(fontIns, typeof(Font), true);
removeStr = EditorGUILayout.TextField("项目路径", removeStr);
if (GUILayout.Button("Find"))
{
UnityEngine.Debug.Log("开始替换.");
TextMeshChangeEditor.CheckAllPrefab(fontIns, removeStr);
}
}
}
此外,我们可以设置一个window来放一些我们可能会用到的信息,比如我们要用到的裁剪路径,或者是我们想要查找某个脚本是否引用了某个资源,都可以在window面板进行设置。这里的Excute函数就是打开面板,可以在其他类中直接调用这个静态函数打开面板。然后在点击button的时候再回调回我们想要的函数。
参考文章:
http://www.xuanyusong.com/archives/3727
https://www.cnblogs.com/penghuan/p/9857847.html
补充:Text Mesh Pro的坑,因为这个需求本来的目的就是使用TMP类替换text类型,但是发现替换之后预制体上明明已经设置好了文本对齐属性,但是实例化之后文本对齐属性又会回到默认值。原因是TMP组件在Awake时会调用默认设置,然后默认的枚举值与设置对齐属性时使用的枚举值是对应不上的!!!!
switch (uGuiAlignment)
{
default:
case TextAnchor.UpperLeft:
alignment = 0;
break;
case TextAnchor.UpperCenter:
alignment = (TextAlignmentOptions)1;
break;
case TextAnchor.UpperRight:
alignment = (TextAlignmentOptions)2;
break;
case TextAnchor.MiddleLeft:
alignment = (TextAlignmentOptions)16;
break;
case TextAnchor.MiddleCenter:
alignment = (TextAlignmentOptions)17;
break;
case TextAnchor.MiddleRight:
alignment = (TextAlignmentOptions)18;
break;
case TextAnchor.LowerLeft:
alignment = (TextAlignmentOptions)8;
break;
case TextAnchor.LowerCenter:
alignment = (TextAlignmentOptions)9;
break;
case TextAnchor.LowerRight:
alignment = (TextAlignmentOptions)10;
break;
}
最后附上最终的TextAnchor与枚举映射值(真坑啊)