【Unity编辑器扩展】UI变量代码自动生成工具(编辑器扩展干货/大幅提高效率)_TopGames的博客-CSDN博客_unity编辑器扩展工具
比如,项目的UI界面Prefab都拼好了,突然要求更换所有文字字体. 再比如,我用Ferr2D Terrain Tool费劲九牛二虎之力拖出了一百多关地形,突然发现Unity自带的SpriteShape2D更好用,又节省资源.如果一个一个手动修改,还不得把人累死,更何况也很难保证不存在漏网之鱼. OK,所以我觉得,懒惰挺好的,因为它是我寻找高效工作方式的动力?
首先,在做批处理之前必须要清楚一个问题,那就是手动修改Prefab的步骤. 批处理无非就是用代码自动化重复这些步骤,强大的Unity编辑器已经留好了这些操作的API, 通过手动可以执行的操作也都可以通过API来完成.
一.批量更换字体, 其实就是修改Text的font属性, 不涉及Prefab删/增节点
0.先做一个界面,方便选择字体文件:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using UnityEditor;
public class PrefabTools : EditorWindow
{
private static Font toFont;
private static TMP_FontAsset tmFont;
[MenuItem("Game Framework/Replace Font")]
public static void ShowWin()
{
EditorWindow.CreateInstance<PrefabTools>().Show();
}
private void OnGUI()
{
GUILayout.Space(10);
toFont = (Font)EditorGUILayout.ObjectField(new GUIContent("Font:"),toFont, typeof(Font), true, GUILayout.MinWidth(100f));
tmFont = (TMP_FontAsset)EditorGUILayout.ObjectField(new GUIContent("TMP_FontAsset:"),tmFont, typeof(TMP_FontAsset), true, GUILayout.MinWidth(100f));
terrTemplate = (GameObject)EditorGUILayout.ObjectField(new GUIContent("SpriteShape Template"), terrTemplate, typeof(GameObject), true, GUILayout.MinWidth(100f));
terrPfb = (GameObject)EditorGUILayout.ObjectField(new GUIContent("Terr Prefab"), terrPfb, typeof(GameObject), true, GUILayout.MinWidth(100f));
GUILayout.Space(10);
if (GUILayout.Button("Replace Font"))
{
//ReplaceFont();
}
if (GUILayout.Button("Replace SpriteShape"))
{
//ReplaceTerr();
//CreateJoints();
//FindAllScripts();
}
}
}
1.找到所有UI界面Prefab:
string[] sdirs = { "Assets/MainGame/Prefabs/UI" };
var asstIds = AssetDatabase.FindAssets("t:Prefab", sdirs);
for (int i = 0; i < asstIds.Length; i++)
{
string path = AssetDatabase.GUIDToAssetPath(asstIds[i]);
var pfb = AssetDatabase.LoadAssetAtPath<GameObject>(path);
}
2.获取Prefab上所有Text组件,修改font
var texts = pfb.GetComponentsInChildren<Text>(true);
foreach (var item in texts)
{
item.font = toFont;
}
var tmTexts = pfb.GetComponentsInChildren<TextMeshProUGUI>(true);
foreach (var item in tmTexts)
{
item.font = tmFont;
}
3.保存修改后的Prefab
PrefabUtility.SavePrefabAsset(pfb, out bool success);
当然还可以显示一个进度条,给用户一些反馈.
完整方法代码:
public static void ReplaceFont()
{
EditorUtility.DisplayProgressBar("Progress", "Replace Font...", 0);
var asstIds = AssetDatabase.FindAssets("t:Prefab", sdirs);
int count = 0;
for (int i = 0; i < asstIds.Length; i++)
{
string path = AssetDatabase.GUIDToAssetPath(asstIds[i]);
var pfb = AssetDatabase.LoadAssetAtPath<GameObject>(path);
//var pfb = PrefabUtility.InstantiatePrefab(pfbFile) as GameObject;//不涉及增删节点,不用实例化
var texts = pfb.GetComponentsInChildren<Text>(true);
foreach (var item in texts)
{
item.font = toFont;
}
var tmTexts = pfb.GetComponentsInChildren<TextMeshProUGUI>(true);
foreach (var item in tmTexts)
{
item.font = tmFont;
}
PrefabUtility.SavePrefabAsset(pfb, out bool success);
if (success)
{
count++;
}
EditorUtility.DisplayProgressBar("Replace Font Progress", pfb.name, count / (float)asstIds.Length);
}
EditorUtility.ClearProgressBar();
}
二.批量Ferr2D Terrain地形转2D SpriteShape
我用的Unity版本是2019.2.3f1, 2D SpriteShape目前还是预览版, 但是不影响它的方便好用. Unity论坛有详细介绍:SpriteShape
原理: 实际上就是把已经拖好的Ferr2D Terrain地形的顶点给SpriteShape用.
需要注意的是,这里需要对prefab增/删节点, Unity是不允许修改prefab实例的结构的,它会提示让你打开prefab进行修改. 而这个双击打开Prefab的操作就是对应API, PrefabUtility.LoadPrefabContents(asset_path), 通过这个API打开Prefab就可以任意修改prefab结构了.
完整方法代码:
public static void ReplaceTerr()
{
EditorUtility.DisplayProgressBar("Progress", "Replace SpriteShape...", 0);
string[] dirs = { "Assets/MainGame/Prefabs/Entity/Levels" };
var asstIds = AssetDatabase.FindAssets("t:Prefab", dirs);
int count = 0;
for (int i = 0; i < asstIds.Length; i++)
{
string path = AssetDatabase.GUIDToAssetPath(asstIds[i]);
var lvRoot = PrefabUtility.LoadPrefabContents(path);
var terrs = lvRoot.GetComponentsInChildren<Ferr2DT_PathTerrain>();
bool changed = terrs.Length > 0;
for (int j = 0; j < terrs.Length; j++)
{
var terr = terrs[j];
var spShape = PrefabUtility.InstantiatePrefab(terrTemplate, terr.transform.parent) as GameObject;
spShape.name = string.Format("SpriteShape_{0}", j);
spShape.transform.position = terr.transform.position;
spShape.transform.rotation = terr.transform.rotation;
spShape.transform.localScale = Vector3.one;
var spline = spShape.GetComponent<UnityEngine.U2D.SpriteShapeController>().spline;
spline.Clear();
var pathDt = terr.PathData;
var points = pathDt.GetPathRaw();
int rawIndex = spline.GetPointCount() - 1;
for (int pindex = 0; pindex < points.Count; pindex++)
{
var p = points[pindex] * terr.transform.localScale;
spline.InsertPointAt(pindex, p);
}
spShape.GetComponent<UnityEngine.U2D.SpriteShapeController>().BakeCollider();
var logic = terr.GetComponent<PhysicsTarget>();
if (null != logic)
{
if (UnityEditorInternal.ComponentUtility.CopyComponent(logic))
{
UnityEditorInternal.ComponentUtility.PasteComponentAsNew(spShape);
}
}
}
for (int index = terrs.Length - 1; index >= 0; index--)
{
DestroyImmediate(terrs[index].gameObject);
}
if (changed)
{
PrefabUtility.SaveAsPrefabAsset(lvRoot, path);
}
PrefabUtility.UnloadPrefabContents(lvRoot);
count++;
EditorUtility.DisplayProgressBar("Replace SpriteShape Progress", path, count / (float)asstIds.Length);
}
EditorUtility.ClearProgressBar();
}
代码中还有两个比较常用的API, 就是组件的复制和粘贴,不用一个一个修改替换组件的属性了,复制粘贴即可,非常方便.
UnityEditorInternal.ComponentUtility.CopyComponent(logic)
UnityEditorInternal.ComponentUtility.PasteComponentAsNew(spShape);
功能如图:
代码和原理都非常简单,却能数倍提高效率,以后要多多研究编辑器扩展方面了?