当前位置:   article > 正文

Unity编辑器工具开发,批量修改Prefab_unity 替换prefab

unity 替换prefab

【Unity编辑器扩展】UI变量代码自动生成工具(编辑器扩展干货/大幅提高效率)_TopGames的博客-CSDN博客_unity编辑器扩展工具

比如,项目的UI界面Prefab都拼好了,突然要求更换所有文字字体. 再比如,我用Ferr2D Terrain Tool费劲九牛二虎之力拖出了一百多关地形,突然发现Unity自带的SpriteShape2D更好用,又节省资源.如果一个一个手动修改,还不得把人累死,更何况也很难保证不存在漏网之鱼. OK,所以我觉得,懒惰挺好的,因为它是我寻找高效工作方式的动力?

首先,在做批处理之前必须要清楚一个问题,那就是手动修改Prefab的步骤. 批处理无非就是用代码自动化重复这些步骤,强大的Unity编辑器已经留好了这些操作的API, 通过手动可以执行的操作也都可以通过API来完成.

一.批量更换字体, 其实就是修改Text的font属性, 不涉及Prefab删/增节点

0.先做一个界面,方便选择字体文件:

  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using UnityEngine.UI;
  4. using TMPro;
  5. using UnityEditor;
  6. public class PrefabTools : EditorWindow
  7. {
  8. private static Font toFont;
  9. private static TMP_FontAsset tmFont;
  10. [MenuItem("Game Framework/Replace Font")]
  11. public static void ShowWin()
  12. {
  13. EditorWindow.CreateInstance<PrefabTools>().Show();
  14. }
  15. private void OnGUI()
  16. {
  17. GUILayout.Space(10);
  18. toFont = (Font)EditorGUILayout.ObjectField(new GUIContent("Font:"),toFont, typeof(Font), true, GUILayout.MinWidth(100f));
  19. tmFont = (TMP_FontAsset)EditorGUILayout.ObjectField(new GUIContent("TMP_FontAsset:"),tmFont, typeof(TMP_FontAsset), true, GUILayout.MinWidth(100f));
  20. terrTemplate = (GameObject)EditorGUILayout.ObjectField(new GUIContent("SpriteShape Template"), terrTemplate, typeof(GameObject), true, GUILayout.MinWidth(100f));
  21. terrPfb = (GameObject)EditorGUILayout.ObjectField(new GUIContent("Terr Prefab"), terrPfb, typeof(GameObject), true, GUILayout.MinWidth(100f));
  22. GUILayout.Space(10);
  23. if (GUILayout.Button("Replace Font"))
  24. {
  25. //ReplaceFont();
  26. }
  27. if (GUILayout.Button("Replace SpriteShape"))
  28. {
  29. //ReplaceTerr();
  30. //CreateJoints();
  31. //FindAllScripts();
  32. }
  33. }
  34. }

1.找到所有UI界面Prefab:

  1. string[] sdirs = { "Assets/MainGame/Prefabs/UI" };
  2. var asstIds = AssetDatabase.FindAssets("t:Prefab", sdirs);
  3. for (int i = 0; i < asstIds.Length; i++)
  4. {
  5. string path = AssetDatabase.GUIDToAssetPath(asstIds[i]);
  6. var pfb = AssetDatabase.LoadAssetAtPath<GameObject>(path);
  7. }

2.获取Prefab上所有Text组件,修改font

  1. var texts = pfb.GetComponentsInChildren<Text>(true);
  2. foreach (var item in texts)
  3. {
  4. item.font = toFont;
  5. }
  6. var tmTexts = pfb.GetComponentsInChildren<TextMeshProUGUI>(true);
  7. foreach (var item in tmTexts)
  8. {
  9. item.font = tmFont;
  10. }

3.保存修改后的Prefab

PrefabUtility.SavePrefabAsset(pfb, out bool success);

当然还可以显示一个进度条,给用户一些反馈. 

完整方法代码:

  1. public static void ReplaceFont()
  2. {
  3. EditorUtility.DisplayProgressBar("Progress", "Replace Font...", 0);
  4. var asstIds = AssetDatabase.FindAssets("t:Prefab", sdirs);
  5. int count = 0;
  6. for (int i = 0; i < asstIds.Length; i++)
  7. {
  8. string path = AssetDatabase.GUIDToAssetPath(asstIds[i]);
  9. var pfb = AssetDatabase.LoadAssetAtPath<GameObject>(path);
  10. //var pfb = PrefabUtility.InstantiatePrefab(pfbFile) as GameObject;//不涉及增删节点,不用实例化
  11. var texts = pfb.GetComponentsInChildren<Text>(true);
  12. foreach (var item in texts)
  13. {
  14. item.font = toFont;
  15. }
  16. var tmTexts = pfb.GetComponentsInChildren<TextMeshProUGUI>(true);
  17. foreach (var item in tmTexts)
  18. {
  19. item.font = tmFont;
  20. }
  21. PrefabUtility.SavePrefabAsset(pfb, out bool success);
  22. if (success)
  23. {
  24. count++;
  25. }
  26. EditorUtility.DisplayProgressBar("Replace Font Progress", pfb.name, count / (float)asstIds.Length);
  27. }
  28. EditorUtility.ClearProgressBar();
  29. }

二.批量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结构了.

完整方法代码:

  1. public static void ReplaceTerr()
  2. {
  3. EditorUtility.DisplayProgressBar("Progress", "Replace SpriteShape...", 0);
  4. string[] dirs = { "Assets/MainGame/Prefabs/Entity/Levels" };
  5. var asstIds = AssetDatabase.FindAssets("t:Prefab", dirs);
  6. int count = 0;
  7. for (int i = 0; i < asstIds.Length; i++)
  8. {
  9. string path = AssetDatabase.GUIDToAssetPath(asstIds[i]);
  10. var lvRoot = PrefabUtility.LoadPrefabContents(path);
  11. var terrs = lvRoot.GetComponentsInChildren<Ferr2DT_PathTerrain>();
  12. bool changed = terrs.Length > 0;
  13. for (int j = 0; j < terrs.Length; j++)
  14. {
  15. var terr = terrs[j];
  16. var spShape = PrefabUtility.InstantiatePrefab(terrTemplate, terr.transform.parent) as GameObject;
  17. spShape.name = string.Format("SpriteShape_{0}", j);
  18. spShape.transform.position = terr.transform.position;
  19. spShape.transform.rotation = terr.transform.rotation;
  20. spShape.transform.localScale = Vector3.one;
  21. var spline = spShape.GetComponent<UnityEngine.U2D.SpriteShapeController>().spline;
  22. spline.Clear();
  23. var pathDt = terr.PathData;
  24. var points = pathDt.GetPathRaw();
  25. int rawIndex = spline.GetPointCount() - 1;
  26. for (int pindex = 0; pindex < points.Count; pindex++)
  27. {
  28. var p = points[pindex] * terr.transform.localScale;
  29. spline.InsertPointAt(pindex, p);
  30. }
  31. spShape.GetComponent<UnityEngine.U2D.SpriteShapeController>().BakeCollider();
  32. var logic = terr.GetComponent<PhysicsTarget>();
  33. if (null != logic)
  34. {
  35. if (UnityEditorInternal.ComponentUtility.CopyComponent(logic))
  36. {
  37. UnityEditorInternal.ComponentUtility.PasteComponentAsNew(spShape);
  38. }
  39. }
  40. }
  41. for (int index = terrs.Length - 1; index >= 0; index--)
  42. {
  43. DestroyImmediate(terrs[index].gameObject);
  44. }
  45. if (changed)
  46. {
  47. PrefabUtility.SaveAsPrefabAsset(lvRoot, path);
  48. }
  49. PrefabUtility.UnloadPrefabContents(lvRoot);
  50. count++;
  51. EditorUtility.DisplayProgressBar("Replace SpriteShape Progress", path, count / (float)asstIds.Length);
  52. }
  53. EditorUtility.ClearProgressBar();
  54. }

代码中还有两个比较常用的API, 就是组件的复制和粘贴,不用一个一个修改替换组件的属性了,复制粘贴即可,非常方便.

  1. UnityEditorInternal.ComponentUtility.CopyComponent(logic)
  2. UnityEditorInternal.ComponentUtility.PasteComponentAsNew(spShape);

功能如图:

代码和原理都非常简单,却能数倍提高效率,以后要多多研究编辑器扩展方面了?

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/127575
推荐阅读
相关标签
  

闽ICP备14008679号