特效处理
This commit is contained in:
@@ -0,0 +1,375 @@
|
||||
// Assets/Editor/BulkTextTool.cs
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
#if TMP_PRESENT
|
||||
using TMPro;
|
||||
#endif
|
||||
|
||||
public class BulkTextTool : EditorWindow
|
||||
{
|
||||
// 扫描范围
|
||||
private List<string> targetFolders = new List<string>(); // 空 = 全项目
|
||||
private bool includeScenes = true;
|
||||
private bool includePrefabs = true;
|
||||
|
||||
// UGUI Text 选项
|
||||
private bool applyUiText = true;
|
||||
private bool uiSetFont;
|
||||
private Font uiFont;
|
||||
private bool uiSetFontSize;
|
||||
private int uiFontSize = 24;
|
||||
private bool uiSetColor;
|
||||
private Color uiColor = Color.white;
|
||||
private bool uiSetRichText;
|
||||
private bool uiRichText = true;
|
||||
private bool uiSetRaycast;
|
||||
private bool uiRaycast = false;
|
||||
private bool uiSetAlignment;
|
||||
private TextAnchor uiAlignment = TextAnchor.MiddleCenter;
|
||||
private bool uiSetHorizontalOverflow;
|
||||
private HorizontalWrapMode uiHWrap = HorizontalWrapMode.Wrap;
|
||||
private bool uiSetVerticalOverflow;
|
||||
private VerticalWrapMode uiVWrap = VerticalWrapMode.Truncate;
|
||||
|
||||
// TMP 选项
|
||||
#if TMP_PRESENT
|
||||
private bool applyTmpText = true;
|
||||
private bool tmpSetFontAsset;
|
||||
private TMP_FontAsset tmpFontAsset;
|
||||
private bool tmpSetFontSize;
|
||||
private float tmpFontSize = 24f;
|
||||
private bool tmpSetColor;
|
||||
private Color tmpColor = Color.white;
|
||||
private bool tmpSetRaycast;
|
||||
private bool tmpRaycast = false;
|
||||
private bool tmpSetAlignment;
|
||||
private TextAlignmentOptions tmpAlignment = TextAlignmentOptions.Center;
|
||||
private bool tmpSetWrapping;
|
||||
private bool tmpEnableWordWrapping = true;
|
||||
private bool tmpSetOverflow;
|
||||
private TextOverflowModes tmpOverflow = TextOverflowModes.Overflow;
|
||||
#else
|
||||
private const bool applyTmpText = false; // 未检测到 TMP
|
||||
#endif
|
||||
|
||||
// 执行控制
|
||||
private bool dryRun = false;
|
||||
|
||||
// 报表
|
||||
private int totalAssetsProcessed;
|
||||
private int totalComponentsChanged;
|
||||
private int totalErrors;
|
||||
|
||||
[MenuItem("Tools/Bulk Text Tool")]
|
||||
public static void Open() => GetWindow<BulkTextTool>("Bulk Text Tool");
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.LabelField("扫描范围", EditorStyles.boldLabel);
|
||||
includeScenes = EditorGUILayout.Toggle(new GUIContent("包含场景 (.unity)"), includeScenes);
|
||||
includePrefabs = EditorGUILayout.Toggle(new GUIContent("包含预制体 (.prefab)"), includePrefabs);
|
||||
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
if (GUILayout.Button("Use Selected Folders"))
|
||||
{
|
||||
targetFolders = Selection.GetFiltered<Object>(SelectionMode.Assets)
|
||||
.Select(AssetDatabase.GetAssetPath)
|
||||
.Select(path => AssetDatabase.IsValidFolder(path) ? path : Path.GetDirectoryName(path).Replace("\\", "/"))
|
||||
.Where(p => !string.IsNullOrEmpty(p))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
}
|
||||
if (GUILayout.Button("Clear Folders")) targetFolders.Clear();
|
||||
}
|
||||
EditorGUILayout.HelpBox(
|
||||
targetFolders.Count == 0 ? "范围:全项目" : $"范围:{string.Join(", ", targetFolders)}",
|
||||
MessageType.Info);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
DrawUiTextSection();
|
||||
EditorGUILayout.Space();
|
||||
DrawTmpTextSection();
|
||||
EditorGUILayout.Space();
|
||||
|
||||
dryRun = EditorGUILayout.Toggle(new GUIContent("Dry run(仅预览,不保存)"), dryRun);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
GUI.enabled = includeScenes || includePrefabs;
|
||||
if (GUILayout.Button("Run(场景+预制体)")) RunBatch(includeScenes, includePrefabs);
|
||||
GUI.enabled = includeScenes;
|
||||
if (GUILayout.Button("Run(仅场景)")) RunBatch(true, false);
|
||||
GUI.enabled = includePrefabs;
|
||||
if (GUILayout.Button("Run(仅预制体)")) RunBatch(false, true);
|
||||
GUI.enabled = true;
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("报表", EditorStyles.boldLabel);
|
||||
EditorGUILayout.LabelField($"处理资产数:{totalAssetsProcessed}");
|
||||
EditorGUILayout.LabelField($"改动组件数:{totalComponentsChanged}");
|
||||
EditorGUILayout.LabelField($"错误数:{totalErrors}");
|
||||
}
|
||||
|
||||
private void DrawUiTextSection()
|
||||
{
|
||||
EditorGUILayout.LabelField("UGUI Text", EditorStyles.boldLabel);
|
||||
applyUiText = EditorGUILayout.Toggle(new GUIContent("应用到 UnityEngine.UI.Text"), applyUiText);
|
||||
using (new EditorGUI.DisabledScope(!applyUiText))
|
||||
{
|
||||
uiSetFont = EditorGUILayout.Toggle("修改 Font", uiSetFont);
|
||||
if (uiSetFont) uiFont = (Font)EditorGUILayout.ObjectField("Font", uiFont, typeof(Font), false);
|
||||
|
||||
uiSetFontSize = EditorGUILayout.Toggle("修改 Font Size", uiSetFontSize);
|
||||
if (uiSetFontSize) uiFontSize = EditorGUILayout.IntField("Font Size", uiFontSize);
|
||||
|
||||
uiSetColor = EditorGUILayout.Toggle("修改 Color", uiSetColor);
|
||||
if (uiSetColor) uiColor = EditorGUILayout.ColorField("Color", uiColor);
|
||||
|
||||
uiSetRichText = EditorGUILayout.Toggle("修改 Rich Text", uiSetRichText);
|
||||
if (uiSetRichText) uiRichText = EditorGUILayout.Toggle("Rich Text", uiRichText);
|
||||
|
||||
uiSetRaycast = EditorGUILayout.Toggle("修改 Raycast Target", uiSetRaycast);
|
||||
if (uiSetRaycast) uiRaycast = EditorGUILayout.Toggle("Raycast Target", uiRaycast);
|
||||
|
||||
uiSetAlignment = EditorGUILayout.Toggle("修改 Alignment", uiSetAlignment);
|
||||
if (uiSetAlignment) uiAlignment = (TextAnchor)EditorGUILayout.EnumPopup("Alignment", uiAlignment);
|
||||
|
||||
uiSetHorizontalOverflow = EditorGUILayout.Toggle("修改 Horizontal Wrap", uiSetHorizontalOverflow);
|
||||
if (uiSetHorizontalOverflow) uiHWrap = (HorizontalWrapMode)EditorGUILayout.EnumPopup("Horizontal Wrap", uiHWrap);
|
||||
|
||||
uiSetVerticalOverflow = EditorGUILayout.Toggle("修改 Vertical Overflow", uiSetVerticalOverflow);
|
||||
if (uiSetVerticalOverflow) uiVWrap = (VerticalWrapMode)EditorGUILayout.EnumPopup("Vertical Overflow", uiVWrap);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTmpTextSection()
|
||||
{
|
||||
#if TMP_PRESENT
|
||||
EditorGUILayout.LabelField("TextMeshPro (TMP_Text)", EditorStyles.boldLabel);
|
||||
applyTmpText = EditorGUILayout.Toggle(new GUIContent("应用到 TMPro.TMP_Text"), applyTmpText);
|
||||
using (new EditorGUI.DisabledScope(!applyTmpText))
|
||||
{
|
||||
tmpSetFontAsset = EditorGUILayout.Toggle("修改 TMP Font Asset", tmpSetFontAsset);
|
||||
if (tmpSetFontAsset) tmpFontAsset = (TMP_FontAsset)EditorGUILayout.ObjectField("TMP Font Asset", tmpFontAsset, typeof(TMP_FontAsset), false);
|
||||
|
||||
tmpSetFontSize = EditorGUILayout.Toggle("修改 Font Size", tmpSetFontSize);
|
||||
if (tmpSetFontSize) tmpFontSize = EditorGUILayout.FloatField("Font Size", tmpFontSize);
|
||||
|
||||
tmpSetColor = EditorGUILayout.Toggle("修改 Color", tmpSetColor);
|
||||
if (tmpSetColor) tmpColor = EditorGUILayout.ColorField("Color", tmpColor);
|
||||
|
||||
tmpSetRaycast = EditorGUILayout.Toggle("修改 Raycast Target", tmpSetRaycast);
|
||||
if (tmpSetRaycast) tmpRaycast = EditorGUILayout.Toggle("Raycast Target", tmpRaycast);
|
||||
|
||||
tmpSetAlignment = EditorGUILayout.Toggle("修改 Alignment", tmpSetAlignment);
|
||||
if (tmpSetAlignment) tmpAlignment = (TextAlignmentOptions)EditorGUILayout.EnumPopup("Alignment", tmpAlignment);
|
||||
|
||||
tmpSetWrapping = EditorGUILayout.Toggle("修改 Word Wrapping", tmpSetWrapping);
|
||||
if (tmpSetWrapping) tmpEnableWordWrapping = EditorGUILayout.Toggle("Enable Word Wrapping", tmpEnableWordWrapping);
|
||||
|
||||
tmpSetOverflow = EditorGUILayout.Toggle("修改 Overflow Mode", tmpSetOverflow);
|
||||
if (tmpSetOverflow) tmpOverflow = (TextOverflowModes)EditorGUILayout.EnumPopup("Overflow Mode", tmpOverflow);
|
||||
}
|
||||
#else
|
||||
EditorGUILayout.LabelField("TextMeshPro (TMP_Text)", EditorStyles.boldLabel);
|
||||
EditorGUILayout.HelpBox("未检测到 TMP。导入 TextMeshPro 后,在 Scripting Define Symbols 定义 TMP_PRESENT 以启用 TMP 设置。", MessageType.Info);
|
||||
#endif
|
||||
}
|
||||
|
||||
private void RunBatch(bool processScenes, bool processPrefabs)
|
||||
{
|
||||
totalAssetsProcessed = 0;
|
||||
totalComponentsChanged = 0;
|
||||
totalErrors = 0;
|
||||
|
||||
var guids = CollectAssetGuids(processScenes, processPrefabs);
|
||||
try
|
||||
{
|
||||
AssetDatabase.StartAssetEditing();
|
||||
|
||||
foreach (var guid in guids)
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
bool changed = false;
|
||||
|
||||
if (path.EndsWith(".unity"))
|
||||
{
|
||||
changed = ProcessScene(path);
|
||||
}
|
||||
else if (path.EndsWith(".prefab"))
|
||||
{
|
||||
changed = ProcessPrefab(path);
|
||||
}
|
||||
|
||||
totalAssetsProcessed++;
|
||||
if (changed && !dryRun)
|
||||
{
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
AssetDatabase.StopAssetEditing();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
EditorUtility.DisplayDialog("Bulk Text Tool",
|
||||
$"完成。\n处理资产数:{totalAssetsProcessed}\n改动组件数:{totalComponentsChanged}\n错误数:{totalErrors}",
|
||||
"OK");
|
||||
}
|
||||
|
||||
private IEnumerable<string> CollectAssetGuids(bool scenes, bool prefabs)
|
||||
{
|
||||
var filters = new List<string>();
|
||||
if (scenes) filters.Add("t:scene");
|
||||
if (prefabs) filters.Add("t:prefab");
|
||||
|
||||
var scope = targetFolders.Count == 0 ? null : targetFolders.ToArray();
|
||||
var set = new HashSet<string>();
|
||||
|
||||
foreach (var f in filters)
|
||||
{
|
||||
var guids = scope == null ? AssetDatabase.FindAssets(f) : AssetDatabase.FindAssets(f, scope);
|
||||
foreach (var g in guids) set.Add(g);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
private bool ProcessScene(string path)
|
||||
{
|
||||
bool changedAny = false;
|
||||
var scene = EditorSceneManager.OpenScene(path, OpenSceneMode.Single);
|
||||
try
|
||||
{
|
||||
foreach (var root in scene.GetRootGameObjects())
|
||||
{
|
||||
changedAny |= ProcessHierarchy(root);
|
||||
}
|
||||
|
||||
if (changedAny && !dryRun)
|
||||
{
|
||||
EditorSceneManager.MarkSceneDirty(scene);
|
||||
EditorSceneManager.SaveScene(scene);
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
totalErrors++;
|
||||
Debug.LogError($"[BulkTextTool] 场景处理错误:{path}\n{ex}");
|
||||
}
|
||||
return changedAny;
|
||||
}
|
||||
|
||||
private bool ProcessPrefab(string path)
|
||||
{
|
||||
bool changedAny = false;
|
||||
|
||||
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
||||
if (prefab == null) return false;
|
||||
|
||||
var instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab);
|
||||
try
|
||||
{
|
||||
changedAny = ProcessHierarchy(instance);
|
||||
|
||||
if (changedAny && !dryRun)
|
||||
{
|
||||
PrefabUtility.ApplyPrefabInstance(instance, InteractionMode.AutomatedAction);
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
totalErrors++;
|
||||
Debug.LogError($"[BulkTextTool] 预制体处理错误:{path}\n{ex}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (instance != null) DestroyImmediate(instance);
|
||||
}
|
||||
|
||||
return changedAny;
|
||||
}
|
||||
|
||||
private bool ProcessHierarchy(GameObject root)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
// UGUI Text
|
||||
if (applyUiText)
|
||||
{
|
||||
var texts = root.GetComponentsInChildren<Text>(true);
|
||||
foreach (var t in texts)
|
||||
{
|
||||
if (ApplyUiTextChanges(t))
|
||||
{
|
||||
totalComponentsChanged++;
|
||||
changed = true;
|
||||
if (!dryRun) EditorUtility.SetDirty(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TMP Text
|
||||
#if TMP_PRESENT
|
||||
if (applyTmpText)
|
||||
{
|
||||
var tmps = root.GetComponentsInChildren<TMP_Text>(true);
|
||||
foreach (var tt in tmps)
|
||||
{
|
||||
if (ApplyTmpTextChanges(tt))
|
||||
{
|
||||
totalComponentsChanged++;
|
||||
changed = true;
|
||||
if (!dryRun) EditorUtility.SetDirty(tt);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private bool ApplyUiTextChanges(Text t)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
if (uiSetFont && t.font != uiFont) { t.font = uiFont; changed = true; }
|
||||
if (uiSetFontSize && t.fontSize != uiFontSize) { t.fontSize = uiFontSize; changed = true; }
|
||||
if (uiSetColor && t.color != uiColor) { t.color = uiColor; changed = true; }
|
||||
if (uiSetRichText && t.supportRichText != uiRichText) { t.supportRichText = uiRichText; changed = true; }
|
||||
if (uiSetRaycast && t.raycastTarget != uiRaycast) { t.raycastTarget = uiRaycast; changed = true; }
|
||||
if (uiSetAlignment && t.alignment != uiAlignment) { t.alignment = uiAlignment; changed = true; }
|
||||
if (uiSetHorizontalOverflow && t.horizontalOverflow != uiHWrap) { t.horizontalOverflow = uiHWrap; changed = true; }
|
||||
if (uiSetVerticalOverflow && t.verticalOverflow != uiVWrap) { t.verticalOverflow = uiVWrap; changed = true; }
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
#if TMP_PRESENT
|
||||
private bool ApplyTmpTextChanges(TMP_Text tt)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
if (tmpSetFontAsset && tt.font != tmpFontAsset) { tt.font = tmpFontAsset; changed = true; }
|
||||
if (tmpSetFontSize && Mathf.Abs(tt.fontSize - tmpFontSize) > 0.001f) { tt.fontSize = tmpFontSize; changed = true; }
|
||||
if (tmpSetColor && tt.color != tmpColor) { tt.color = tmpColor; changed = true; }
|
||||
if (tmpSetRaycast && tt.raycastTarget != tmpRaycast) { tt.raycastTarget = tmpRaycast; changed = true; }
|
||||
if (tmpSetAlignment && tt.alignment != tmpAlignment) { tt.alignment = tmpAlignment; changed = true; }
|
||||
if (tmpSetWrapping && tt.enableWordWrapping != tmpEnableWordWrapping) { tt.enableWordWrapping = tmpEnableWordWrapping; changed = true; }
|
||||
if (tmpSetOverflow && tt.overflowMode != tmpOverflow) { tt.overflowMode = tmpOverflow; changed = true; }
|
||||
|
||||
return changed;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user