Files
MinFt/Client/Assets/Editor/SetupTmpTextAndFont.cs
Liubing\LB 3d8d4d18f3 first commit
先修复一下,错误的场景

删除不必要的钓场资产

修复into场景

update:更新meta文件,修复报错

update:修复资源

修复第一章节建造

修复建造第三章

update:删除多余内容

update:更新README.md

update:还原图标

update:更新README

update:更新配置
2026-04-28 02:17:22 +08:00

1160 lines
44 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using UnityEditor;
using TMPro;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine.Localization.PropertyVariants;
using UnityEngine.Localization.Settings;
using UnityEngine.Localization;
using UnityEngine.Localization.PropertyVariants.TrackedProperties;
using UnityEngine.Localization.PropertyVariants.TrackedObjects;
using UnityEngine.Events;
using UnityEngine.Localization.Components;
using UnityEngine.UI;
namespace GameCore.Editor
{
/// <summary>
/// 用于批量设置TMP字体和材质的本地化
/// </summary>
public class SetupTmpTextAndFont : EditorWindow
{
private Vector2 scrollPosition;
private bool processSelectedPrefabs = true;
private bool processFolderPrefabs = false;
private string folderPath = "Assets/";
private bool recursiveSearch = true;
// Unity Localization设置
private string tableReference = "text_AssetTable";
// 处理结果
private List<string> processedPrefabs = new List<string>();
private List<string> errorMessages = new List<string>();
[MenuItem("Tools/Setup TMP Text And Font")]
public static void ShowWindow()
{
SetupTmpTextAndFont window = GetWindow<SetupTmpTextAndFont>("Setup TMP Text And Font");
window.minSize = new Vector2(400, 500);
window.Show();
}
private void OnGUI()
{
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
GUILayout.Label("Setup TMP Text And Font", EditorStyles.boldLabel);
GUILayout.Space(10);
// 处理选项
GUILayout.Label("处理选项", EditorStyles.boldLabel);
processSelectedPrefabs = EditorGUILayout.Toggle("处理选中的Prefab", processSelectedPrefabs);
processFolderPrefabs = EditorGUILayout.Toggle("处理文件夹下的Prefab", processFolderPrefabs);
if (processFolderPrefabs)
{
EditorGUILayout.BeginHorizontal();
GUILayout.Label("文件夹路径:", GUILayout.Width(80));
folderPath = EditorGUILayout.TextField(folderPath);
if (GUILayout.Button("选择", GUILayout.Width(60)))
{
string selectedPath = EditorUtility.OpenFolderPanel("选择文件夹", "Assets", "");
if (!string.IsNullOrEmpty(selectedPath))
{
if (selectedPath.StartsWith(Application.dataPath))
{
folderPath = "Assets" + selectedPath.Substring(Application.dataPath.Length);
}
else
{
folderPath = selectedPath;
}
}
}
EditorGUILayout.EndHorizontal();
recursiveSearch = EditorGUILayout.Toggle("递归搜索子文件夹", recursiveSearch);
}
GUILayout.Space(10);
// Unity Localization设置
GUILayout.Label("Unity Localization 设置", EditorStyles.boldLabel);
tableReference = EditorGUILayout.TextField("表格引用", tableReference);
GUILayout.Space(10);
// 操作按钮
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("预览将要处理的Prefab"))
{
PreviewPrefabsToProcess();
}
if (GUILayout.Button("执行清理设置"))
{
ClearPrefabAll();
}
if (GUILayout.Button("执行批量设置"))
{
ProcessPrefabs();
}
if (GUILayout.Button("执行批量个性化设置"))
{
ProcessIndividuationPrefabs();
}
if (GUILayout.Button("执行批量WFC设置"))
{
ProcessWFCPrefabs();
}
EditorGUILayout.EndHorizontal();
GUILayout.Space(10);
// 显示处理结果
if (processedPrefabs.Count > 0 || errorMessages.Count > 0)
{
GUILayout.Label("处理结果", EditorStyles.boldLabel);
if (processedPrefabs.Count > 0)
{
GUILayout.Label($"成功处理 {processedPrefabs.Count} 个Prefab:", EditorStyles.boldLabel);
foreach (string prefab in processedPrefabs)
{
EditorGUILayout.BeginHorizontal();
GUILayout.Label("✓", GUILayout.Width(20));
if (GUILayout.Button(prefab, EditorStyles.label))
{
Object prefabAsset = AssetDatabase.LoadAssetAtPath<Object>(prefab);
if (prefabAsset != null)
{
Selection.activeObject = prefabAsset;
EditorGUIUtility.PingObject(prefabAsset);
}
}
EditorGUILayout.EndHorizontal();
}
}
if (errorMessages.Count > 0)
{
GUILayout.Label($"处理失败 {errorMessages.Count} 个:", EditorStyles.boldLabel);
foreach (string error in errorMessages)
{
EditorGUILayout.BeginHorizontal();
GUILayout.Label("✗", GUILayout.Width(20));
GUILayout.Label(error, EditorStyles.label);
EditorGUILayout.EndHorizontal();
}
}
}
EditorGUILayout.EndScrollView();
}
private void PreviewPrefabsToProcess()
{
processedPrefabs.Clear();
errorMessages.Clear();
List<string> prefabsToProcess = GetPrefabsToProcess();
if (prefabsToProcess.Count == 0)
{
EditorUtility.DisplayDialog("预览结果", "没有找到要处理的Prefab", "确定");
return;
}
string message = $"将要处理以下 {prefabsToProcess.Count} 个Prefab:\n\n";
foreach (string prefab in prefabsToProcess)
{
message += $"• {prefab}\n";
}
EditorUtility.DisplayDialog("预览结果", message, "确定");
}
private void ProcessPrefabs()
{
processedPrefabs.Clear();
errorMessages.Clear();
List<string> prefabsToProcess = GetPrefabsToProcess();
if (prefabsToProcess.Count == 0)
{
EditorUtility.DisplayDialog("处理结果", "没有找到要处理的Prefab", "确定");
return;
}
int processedCount = 0;
int errorCount = 0;
foreach (string prefabPath in prefabsToProcess)
{
try
{
if (ProcessSinglePrefab(prefabPath))
{
processedPrefabs.Add(prefabPath);
processedCount++;
}
else
{
errorMessages.Add(prefabPath);
errorCount++;
}
}
catch (System.Exception e)
{
errorMessages.Add($"{prefabPath}: {e.Message}");
errorCount++;
}
}
// 刷新资源数据库
AssetDatabase.Refresh();
string resultMessage = $"处理完成!\n\n成功: {processedCount} 个\n失败: {errorCount} 个";
EditorUtility.DisplayDialog("处理结果", resultMessage, "确定");
}
private List<string> GetPrefabsToProcess()
{
List<string> prefabs = new List<string>();
// 处理选中的prefab
if (processSelectedPrefabs)
{
foreach (Object obj in Selection.objects)
{
string path = AssetDatabase.GetAssetPath(obj);
if (IsPrefab(path))
{
prefabs.Add(path);
}
}
}
// 处理文件夹下的prefab
if (processFolderPrefabs && !string.IsNullOrEmpty(folderPath))
{
string fullPath = Path.Combine(Application.dataPath, folderPath.Replace("Assets/", ""));
if (Directory.Exists(fullPath))
{
string searchPattern = "*.prefab";
SearchOption searchOption = recursiveSearch ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
string[] prefabFiles = Directory.GetFiles(fullPath, searchPattern, searchOption);
foreach (string file in prefabFiles)
{
string relativePath = "Assets" + file.Substring(Application.dataPath.Length);
prefabs.Add(relativePath);
}
}
}
return prefabs.Distinct().ToList();
}
private bool IsPrefab(string path)
{
return path.EndsWith(".prefab");
}
private bool ProcessSinglePrefab(string prefabPath)
{
// 加载prefab
GameObject prefabRoot = PrefabUtility.LoadPrefabContents(prefabPath);
if (prefabRoot == null)
{
return false;
}
bool hasChanges = false;
try
{
// 查找所有TMP组件
TextMeshProUGUI[] tmpUGUIComponents = prefabRoot.GetComponentsInChildren<TextMeshProUGUI>(true);
TextMeshPro[] tmpComponents = prefabRoot.GetComponentsInChildren<TextMeshPro>(true);
// 处理TextMeshProUGUI组件
foreach (TextMeshProUGUI tmp in tmpUGUIComponents)
{
if (SetupTmpLocalization(tmp.gameObject, prefabRoot.gameObject.name))
{
hasChanges = true;
}
}
// 处理TextMeshPro组件
foreach (TextMeshPro tmp in tmpComponents)
{
if (SetupTmpLocalization(tmp.gameObject, prefabRoot.gameObject.name))
{
hasChanges = true;
}
}
// 如果有更改保存prefab
if (hasChanges)
{
PrefabUtility.SaveAsPrefabAsset(prefabRoot, prefabPath);
}
}
finally
{
// 清理临时对象
PrefabUtility.UnloadPrefabContents(prefabRoot);
}
return hasChanges;
}
private bool SetupTmpLocalization(GameObject targetObject, string panelName)
{
try
{
if (targetObject == null)
{
Debug.LogError("目标GameObject为空");
return false;
}
// 获取TMP组件
TMP_Text text = targetObject.GetComponent<TextMeshProUGUI>();
if (text == null)
{
text = targetObject.GetComponent<TextMeshPro>();
}
if (text == null)
{
Debug.LogWarning($"未找到TMP组件: {targetObject.name}");
return false;
}
// 检查是否已经有GameObjectLocalizer组件
GameObjectLocalizer existingLocalizer = targetObject.GetComponent<GameObjectLocalizer>();
if (existingLocalizer != null)
{
SetupUnityLocalizationForTMP(text, existingLocalizer);
Debug.Log($"已存在GameObjectLocalizer组件跳过: {targetObject.name}");
return false;
}
// 添加GameObjectLocalizer组件
var localizer = targetObject.AddComponent<GameObjectLocalizer>();
if (localizer == null)
{
Debug.LogError($"无法添加GameObjectLocalizer组件:{panelName} == {targetObject.name}");
return false;
}
// 执行Unity Localization设置
SetupUnityLocalizationForTMP(targetObject, text, localizer, panelName);
return true;
}
catch (System.Exception e)
{
Debug.LogError($"设置TMP本地化时出错 {panelName} == {targetObject?.name}: {e.Message}");
return false;
}
}
private void SetupUnityLocalizationForTMP(GameObject targetObject, TMP_Text text, GameObjectLocalizer localizer, string panelName)
{
try
{
// 检查参数是否为空
if (text == null)
{
Debug.LogError($"TMP_Text组件为空:{panelName} {targetObject.name}");
return;
}
if (localizer == null)
{
Debug.LogError($"GameObjectLocalizer组件为空:{panelName} {targetObject.name}");
return;
}
// 检查Unity Localization系统是否初始化
if (!LocalizationSettings.InitializationOperation.IsDone)
{
Debug.LogWarning($"Unity Localization系统未初始化完成跳过设置: {targetObject.name}");
return;
}
// Gets the Tracked text or creates a new tracker
var trackedText = localizer.GetTrackedObject<TrackedUGuiGraphic>(text);
if (trackedText == null)
{
Debug.LogError($"无法获取Tracked对象:{panelName} {targetObject.name}");
return;
}
// The LocalizedString can be modified directly
var fVariant = trackedText.GetTrackedProperty<LocalizedAssetProperty>("m_fontAsset");
if (fVariant != null)
{
string fontEntryReference = text.font.name;
fVariant.LocalizedObject = new LocalizedTmpFont
{
TableReference = tableReference,
TableEntryReference = fontEntryReference
};
}
else
{
Debug.LogWarning($"无法获取字体属性:{panelName} {targetObject.name}");
}
var mtVariant = trackedText.GetTrackedProperty<LocalizedAssetProperty>("m_sharedMaterial");
if (mtVariant != null)
{
string materialEntryReference = text.fontSharedMaterial.name;
mtVariant.LocalizedObject = new LocalizedMaterial
{
TableReference = tableReference,
TableEntryReference = materialEntryReference
};
}
else
{
Debug.LogWarning($"无法获取材质属性:{panelName} {targetObject.name}");
}
// Force an Update
if (LocalizationSettings.SelectedLocale != null)
{
localizer.ApplyLocaleVariant(LocalizationSettings.SelectedLocale);
}
}
catch (System.Exception e)
{
Debug.LogError($"设置Unity Localization时出错{panelName} == {targetObject.name}: {e.Message}");
}
}
private void SetupUnityLocalizationForTMP(TMP_Text text, GameObjectLocalizer localizer)
{
try
{
// 检查参数是否为空
if (text == null)
{
Debug.LogError($"TMP_Text组件为空:");
return;
}
if (localizer == null)
{
Debug.LogError($"GameObjectLocalizer组件为空:");
return;
}
// 检查Unity Localization系统是否初始化
if (!LocalizationSettings.InitializationOperation.IsDone)
{
Debug.LogWarning($"Unity Localization系统未初始化完成跳过设置:");
return;
}
// Gets the Tracked text or creates a new tracker
var trackedText = localizer.GetTrackedObject<TrackedUGuiGraphic>(text);
if (trackedText == null)
{
Debug.LogError($"无法获取Tracked对象:");
return;
}
// The LocalizedString can be modified directly
var fVariant = trackedText.GetTrackedProperty<LocalizedAssetProperty>("m_fontAsset");
if (fVariant != null)
{
string fontEntryReference = text.font.name;
fVariant.LocalizedObject = new LocalizedTmpFont
{
TableReference = tableReference,
TableEntryReference = fontEntryReference
};
}
else
{
Debug.LogWarning($"无法获取字体属性:");
}
var mtVariant = trackedText.GetTrackedProperty<LocalizedAssetProperty>("m_sharedMaterial");
if (mtVariant != null)
{
string materialEntryReference = text.fontSharedMaterial.name;
mtVariant.LocalizedObject = new LocalizedMaterial
{
TableReference = tableReference,
TableEntryReference = materialEntryReference
};
}
else
{
Debug.LogWarning($"无法获取材质属性:");
}
// Force an Update
if (LocalizationSettings.SelectedLocale != null)
{
localizer.ApplyLocaleVariant(LocalizationSettings.SelectedLocale);
}
}
catch (System.Exception e)
{
Debug.LogError($"设置Unity Localization时出错: {e.Message}");
}
}
void ProcessIndividuationPrefabs()
{
processedPrefabs.Clear();
errorMessages.Clear();
List<string> prefabsToProcess = GetPrefabsToProcess();
if (prefabsToProcess.Count == 0)
{
EditorUtility.DisplayDialog("处理结果", "没有找到要处理的Prefab", "确定");
return;
}
int processedCount = 0;
int errorCount = 0;
foreach (string prefabPath in prefabsToProcess)
{
try
{
if (ProcessIndividuationSinglePrefab(prefabPath))
{
processedPrefabs.Add(prefabPath);
processedCount++;
}
else
{
errorMessages.Add(prefabPath);
errorCount++;
}
}
catch (System.Exception e)
{
errorMessages.Add($"{prefabPath}: {e.Message}");
errorCount++;
}
}
// 刷新资源数据库
AssetDatabase.Refresh();
string resultMessage = $"处理完成!\n\n成功: {processedCount} 个\n失败: {errorCount} 个";
EditorUtility.DisplayDialog("处理结果", resultMessage, "确定");
}
private bool ProcessIndividuationSinglePrefab(string prefabPath)
{
// 加载prefab
GameObject prefabRoot = PrefabUtility.LoadPrefabContents(prefabPath);
if (prefabRoot == null)
{
return false;
}
bool hasChanges = false;
try
{
// 查找所有TMP组件
TextMeshProUGUI[] tmpUGUIComponents = prefabRoot.GetComponentsInChildren<TextMeshProUGUI>(true);
TextMeshPro[] tmpComponents = prefabRoot.GetComponentsInChildren<TextMeshPro>(true);
// 处理TextMeshProUGUI组件
foreach (TextMeshProUGUI tmp in tmpUGUIComponents)
{
if (SetupTmpIndividuationLocalization(tmp.gameObject, prefabRoot.gameObject.name))
{
hasChanges = true;
}
}
// 处理TextMeshPro组件
foreach (TextMeshPro tmp in tmpComponents)
{
if (SetupTmpIndividuationLocalization(tmp.gameObject, prefabRoot.gameObject.name))
{
hasChanges = true;
}
}
// 如果有更改保存prefab
if (hasChanges)
{
PrefabUtility.SaveAsPrefabAsset(prefabRoot, prefabPath);
}
}
finally
{
// 清理临时对象
PrefabUtility.UnloadPrefabContents(prefabRoot);
}
return hasChanges;
}
private bool SetupTmpIndividuationLocalization(GameObject targetObject, string panelName)
{
try
{
if (targetObject == null)
{
Debug.LogError("目标GameObject为空");
return false;
}
// 获取TMP组件
TMP_Text text = targetObject.GetComponent<TextMeshProUGUI>();
if (text == null)
{
text = targetObject.GetComponent<TextMeshPro>();
}
if (text == null)
{
Debug.LogWarning($"未找到TMP组件: {targetObject.name}");
return false;
}
// 检查是否已经有GameObjectLocalizer组件
GameObjectLocalizer existingLocalizer = targetObject.GetComponent<GameObjectLocalizer>();
if (existingLocalizer != null)
{
SetupUnityLocalizationForTMP(text, existingLocalizer);
Debug.Log($"已存在GameObjectLocalizer组件跳过: {targetObject.name}");
return true;
}
return false;
}
catch (System.Exception e)
{
Debug.LogError($"设置TMP本地化时出错 {panelName} == {targetObject?.name}: {e.Message}");
return false;
}
}
void ProcessWFCPrefabs()
{
processedPrefabs.Clear();
errorMessages.Clear();
List<string> prefabsToProcess = GetPrefabsToProcess();
if (prefabsToProcess.Count == 0)
{
EditorUtility.DisplayDialog("处理结果", "没有找到要处理的Prefab", "确定");
return;
}
int processedCount = 0;
int errorCount = 0;
foreach (string prefabPath in prefabsToProcess)
{
try
{
if (ProcessWFCSinglePrefab(prefabPath))
{
processedPrefabs.Add(prefabPath);
processedCount++;
}
else
{
errorMessages.Add(prefabPath);
errorCount++;
}
}
catch (System.Exception e)
{
errorMessages.Add($"{prefabPath}: {e.Message}");
errorCount++;
}
}
// 刷新资源数据库
AssetDatabase.Refresh();
string resultMessage = $"处理完成!\n\n成功: {processedCount} 个\n失败: {errorCount} 个";
EditorUtility.DisplayDialog("处理结果", resultMessage, "确定");
}
private bool ProcessWFCSinglePrefab(string prefabPath)
{
// 加载prefab
GameObject prefabRoot = PrefabUtility.LoadPrefabContents(prefabPath);
if (prefabRoot == null)
{
return false;
}
bool hasChanges = false;
try
{
// 查找所有TMP组件
TextMeshProUGUI[] tmpUGUIComponents = prefabRoot.GetComponentsInChildren<TextMeshProUGUI>(true);
TextMeshPro[] tmpComponents = prefabRoot.GetComponentsInChildren<TextMeshPro>(true);
// 处理TextMeshProUGUI组件
foreach (TextMeshProUGUI tmp in tmpUGUIComponents)
{
if (SetupTmpWFCLocalization(tmp.gameObject, prefabRoot.gameObject.name))
{
hasChanges = true;
}
}
// 处理TextMeshPro组件
foreach (TextMeshPro tmp in tmpComponents)
{
if (SetupTmpWFCLocalization(tmp.gameObject, prefabRoot.gameObject.name))
{
hasChanges = true;
}
}
// 如果有更改保存prefab
if (hasChanges)
{
PrefabUtility.SaveAsPrefabAsset(prefabRoot, prefabPath);
}
}
finally
{
// 清理临时对象
PrefabUtility.UnloadPrefabContents(prefabRoot);
}
return hasChanges;
}
private bool SetupTmpWFCLocalization(GameObject targetObject, string panelName)
{
try
{
if (targetObject == null)
{
Debug.LogError("目标GameObject为空");
return false;
}
// 获取TMP组件
TMP_Text text = targetObject.GetComponent<TextMeshProUGUI>();
if (text == null)
{
text = targetObject.GetComponent<TextMeshPro>();
}
if (text == null)
{
Debug.LogWarning($"未找到TMP组件: {targetObject.name}");
return false;
}
// 检查是否已经有GameObjectLocalizer组件
GameObjectLocalizer existingLocalizer = targetObject.GetComponent<GameObjectLocalizer>();
if (existingLocalizer != null)
{
SetupUnityLocalizationWFCForTMP(text, existingLocalizer);
Debug.Log($"已存在GameObjectLocalizer组件跳过: {targetObject.name}");
return true;
}
return false;
}
catch (System.Exception e)
{
Debug.LogError($"设置TMP本地化时出错 {panelName} == {targetObject?.name}: {e.Message}");
return false;
}
}
private void SetupUnityLocalizationWFCForTMP(TMP_Text text, GameObjectLocalizer localizer)
{
try
{
// 检查参数是否为空
if (text == null)
{
Debug.LogError($"TMP_Text组件为空:");
return;
}
if (localizer == null)
{
Debug.LogError($"GameObjectLocalizer组件为空:");
return;
}
// 检查Unity Localization系统是否初始化
if (!LocalizationSettings.InitializationOperation.IsDone)
{
Debug.LogWarning($"Unity Localization系统未初始化完成跳过设置:");
return;
}
// Gets the Tracked text or creates a new tracker
var trackedText = localizer.GetTrackedObject<TrackedUGuiGraphic>(text);
if (trackedText == null)
{
Debug.LogError($"无法获取Tracked对象:");
return;
}
// The LocalizedString can be modified directly
var fVariant = trackedText.GetTrackedProperty<LocalizedAssetProperty>("m_fontAsset");
if (fVariant != null)
{
string fontEntryReference = text.font.name;
fVariant.LocalizedObject = new LocalizedTmpFont
{
TableReference = tableReference,
TableEntryReference = fontEntryReference
};
fVariant.LocalizedObject.WaitForCompletion = true; // 设置为true以确保WFC处理
}
else
{
Debug.LogWarning($"无法获取字体属性:");
}
var mtVariant = trackedText.GetTrackedProperty<LocalizedAssetProperty>("m_sharedMaterial");
if (mtVariant != null)
{
string materialEntryReference = text.fontSharedMaterial.name;
mtVariant.LocalizedObject = new LocalizedMaterial
{
TableReference = tableReference,
TableEntryReference = materialEntryReference
};
mtVariant.LocalizedObject.WaitForCompletion = true; // 设置为true以确保WFC处理
}
else
{
Debug.LogWarning($"无法获取材质属性:");
}
// Force an Update
if (LocalizationSettings.SelectedLocale != null)
{
localizer.ApplyLocaleVariant(LocalizationSettings.SelectedLocale);
}
}
catch (System.Exception e)
{
Debug.LogError($"设置Unity Localization时出错: {e.Message}");
}
}
void ClearPrefabAll()
{
processedPrefabs.Clear();
errorMessages.Clear();
List<string> prefabsToProcess = GetPrefabsToProcess();
if (prefabsToProcess.Count == 0)
{
EditorUtility.DisplayDialog("处理结果", "没有找到要处理的Prefab", "确定");
return;
}
int processedCount = 0;
int errorCount = 0;
foreach (string prefabPath in prefabsToProcess)
{
try
{
if (ClearPrefab(prefabPath))
{
processedPrefabs.Add(prefabPath);
processedCount++;
}
else
{
errorMessages.Add(prefabPath);
errorCount++;
}
}
catch (System.Exception e)
{
errorMessages.Add($"{prefabPath}: {e.Message}");
errorCount++;
}
}
// 刷新资源数据库
AssetDatabase.Refresh();
string resultMessage = $"处理完成!\n\n成功: {processedCount} 个\n失败: {errorCount} 个";
EditorUtility.DisplayDialog("处理结果", resultMessage, "确定");
}
private bool ClearPrefab(string prefabPath)
{
// 加载prefab
GameObject prefabRoot = PrefabUtility.LoadPrefabContents(prefabPath);
if (prefabRoot == null)
{
return false;
}
bool hasChanges = false;
try
{
// 查找所有TMP组件
GameObjectLocalizer[] tmpUGUIComponents = prefabRoot.GetComponentsInChildren<GameObjectLocalizer>(true);
// 如果有更改保存prefab
if (tmpUGUIComponents.Length > 0)
{
hasChanges = true;
for (int i = 0; i < tmpUGUIComponents.Length; i++)
{
DestroyImmediate(tmpUGUIComponents[i]);
}
PrefabUtility.SaveAsPrefabAsset(prefabRoot, prefabPath);
}
}
finally
{
// 清理临时对象
PrefabUtility.UnloadPrefabContents(prefabRoot);
}
return hasChanges;
}
}
/// <summary>
/// 用于批量设置TMP文本的本地化
/// </summary>
public class LocalizedTmpTextString : EditorWindow
{
/// <summary>
/// 自动配置 UnityEvent 的核心逻辑
/// </summary>
/// <param name="localizeEvent">要配置的 MyLocalizeSpriteEvent 组件实例</param>
private void AutoSetupEvent(LocalizeStringEvent localizeEvent)
{
TMP_Text targetStringComponent = localizeEvent.gameObject.GetComponent<TMP_Text>();
if (targetStringComponent == null)
{
EditorUtility.DisplayDialog("错误", " 对象上没有找到 TMP_Text 组件。", "确定");
return;
}
// 2. 获取 UnityEvent 的序列化属性
// 这是关键!我们不能直接操作 localizeEvent.m_UpdateString
// 因为直接修改不会触发 Unity 的 "脏" 标记和序列化系统。
// 我们必须通过 SerializedObject 和 SerializedProperty 来操作。
SerializedObject serializedObject = new SerializedObject(localizeEvent);
// 使用属性名 "m_UpdateString" 来找到对应的 SerializedProperty
SerializedProperty eventProperty = serializedObject.FindProperty("m_UpdateString");
// 3. 准备添加新的监听器
// 获取 PersistentCalls 列表的属性
SerializedProperty persistentCallsProperty = eventProperty.FindPropertyRelative("m_PersistentCalls.m_Calls");
// --- 选项 A仅添加一个空的监听器相当于点击 + 号)---
// persistentCallsProperty.arraySize++; // 增加数组大小Unity 会自动创建一个新元素
// Debug.Log("已添加一个空的监听器,请在 Inspector 中手动配置。");
// serializedObject.ApplyModifiedProperties(); // 应用修改
// return; // 如果只想实现点击 + 号的效果,到这里就够了
// --- 选项 B添加并完全配置监听器更强大---
// 检查是否已经有监听器了,避免重复添加
bool listenerExists = false;
for (int i = 0; i < persistentCallsProperty.arraySize; i++)
{
SerializedProperty call = persistentCallsProperty.GetArrayElementAtIndex(i);
// 检查目标对象和方法名是否已经匹配
if (call.FindPropertyRelative("m_Target").objectReferenceValue == targetStringComponent &&
call.FindPropertyRelative("m_MethodName").stringValue == "set_text")
{
listenerExists = true;
break;
}
}
if (listenerExists)
{
EditorUtility.DisplayDialog("提示", "事件监听器已经配置过了,无需重复添加。", "确定");
return;
}
// 增加数组大小
int newIndex = persistentCallsProperty.arraySize;
persistentCallsProperty.arraySize++;
serializedObject.ApplyModifiedProperties(); // 应用大小修改
// 获取新添加的 PersistentCall
SerializedProperty newCall = persistentCallsProperty.GetArrayElementAtIndex(newIndex);
// 4. 设置新监听器的各个属性
// 设置目标对象
SerializedProperty targetProperty = newCall.FindPropertyRelative("m_Target");
targetProperty.objectReferenceValue = targetStringComponent;
// --- 关键区别点 1: 设置方法名 ---
// 对于 TextMeshProUGUI 的 text 属性,其 setter 方法名是 "set_text"
SerializedProperty methodNameProperty = newCall.FindPropertyRelative("m_MethodName");
methodNameProperty.stringValue = "set_text";
// 设置模式
SerializedProperty modeProperty = newCall.FindPropertyRelative("m_Mode");
modeProperty.intValue = (int)UnityEventCallState.EditorAndRuntime;
// --- 关键区别点 2: 设置参数 ---
// 参数类型是 string所以这里要设置 string 的完整程序集限定名
SerializedProperty argumentsProperty = newCall.FindPropertyRelative("m_Arguments");
SerializedProperty objectArgumentProperty = argumentsProperty.FindPropertyRelative("m_ObjectArgument");
objectArgumentProperty.objectReferenceValue = null;
SerializedProperty argumentAssemblyTypeProperty = argumentsProperty.FindPropertyRelative("m_ObjectArgumentAssemblyTypeName");
argumentAssemblyTypeProperty.stringValue = "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; // 这是 string 的完整类型名
// 5. 应用所有修改
serializedObject.ApplyModifiedProperties();
// 最后,给用户一个反馈
EditorUtility.DisplayDialog("成功", $"已成功为 '{localizeEvent.name}' 配置事件监听器,连接到 '{localizeEvent.gameObject.name}' 的 text 属性。", "确定");
Debug.Log($"<color=green>自动配置完成:</color> {localizeEvent.name}.OnStringUpdated -> {localizeEvent.gameObject.name}.TextMeshProUGUI.text", localizeEvent);
}
}
/// <summary>
/// 用于批量设置Image的本地化
/// </summary>
public class LocalizedSpriteEditor : EditorWindow
{
/// <summary>
/// 自动配置 UnityEvent 的核心逻辑
/// </summary>
/// <param name="localizeEvent">要配置的 MyLocalizeSpriteEvent 组件实例</param>
private void AutoSetupEvent(LocalizeSpriteEvent localizeEvent)
{
Image targetImageComponent = localizeEvent.gameObject.GetComponent<Image>();
if (targetImageComponent == null)
{
EditorUtility.DisplayDialog("错误", " 对象上没有找到 Image 组件。", "确定");
return;
}
// 2. 获取 UnityEvent 的序列化属性
// 这是关键!我们不能直接操作 localizeEvent.m_UpdateString
// 因为直接修改不会触发 Unity 的 "脏" 标记和序列化系统。
// 我们必须通过 SerializedObject 和 SerializedProperty 来操作。
SerializedObject serializedObject = new SerializedObject(localizeEvent);
// 使用属性名 "m_UpdateString" 来找到对应的 SerializedProperty
SerializedProperty eventProperty = serializedObject.FindProperty("m_UpdateAsset");
// 3. 准备添加新的监听器
// 获取 PersistentCalls 列表的属性
SerializedProperty persistentCallsProperty = eventProperty.FindPropertyRelative("m_PersistentCalls.m_Calls");
// --- 选项 A仅添加一个空的监听器相当于点击 + 号)---
// persistentCallsProperty.arraySize++; // 增加数组大小Unity 会自动创建一个新元素
// Debug.Log("已添加一个空的监听器,请在 Inspector 中手动配置。");
// serializedObject.ApplyModifiedProperties(); // 应用修改
// return; // 如果只想实现点击 + 号的效果,到这里就够了
// --- 选项 B添加并完全配置监听器更强大---
// 检查是否已经有监听器了,避免重复添加
bool listenerExists = false;
for (int i = 0; i < persistentCallsProperty.arraySize; i++)
{
SerializedProperty call = persistentCallsProperty.GetArrayElementAtIndex(i);
// 检查目标对象和方法名是否已经匹配
if (call.FindPropertyRelative("m_Target").objectReferenceValue == targetImageComponent &&
call.FindPropertyRelative("m_MethodName").stringValue == "set_sprite")
{
listenerExists = true;
break;
}
}
if (listenerExists)
{
EditorUtility.DisplayDialog("提示", "事件监听器已经配置过了,无需重复添加。", "确定");
return;
}
// 增加数组大小,为新的监听器腾出位置
int newIndex = persistentCallsProperty.arraySize;
persistentCallsProperty.arraySize++;
serializedObject.ApplyModifiedProperties(); // 应用大小修改,否则 GetArrayElementAtIndex 会越界
// 获取新添加的 PersistentCall 的 SerializedProperty
SerializedProperty newCall = persistentCallsProperty.GetArrayElementAtIndex(newIndex);
// 4. 设置新监听器的各个属性
// 设置目标对象 (m_Target)
SerializedProperty targetProperty = newCall.FindPropertyRelative("m_Target");
targetProperty.objectReferenceValue = targetImageComponent;
// 设置方法名 (m_MethodName)
// 对于属性Unity 的内部调用的是它的 setter 方法,命名为 "set_" + 属性名
SerializedProperty methodNameProperty = newCall.FindPropertyRelative("m_MethodName");
methodNameProperty.stringValue = "set_sprite";
// 设置模式 (m_Mode)
// 0 = EventDefined (使用事件提供的参数), 1 = Void (无参数), 2 = Object (提供一个固定的对象), etc.
// 我们需要的是 EventDefined其值为 0
SerializedProperty modeProperty = newCall.FindPropertyRelative("m_Mode");
modeProperty.intValue = (int)UnityEventCallState.EditorAndRuntime;
// 设置参数 (m_Arguments)
// 因为我们使用的是事件提供的参数 (EventDefined),所以需要告诉它使用哪个参数
// 对于 UnityEvent<Sprite>,我们想将事件的 Sprite 参数传递给方法的 Sprite 参数
SerializedProperty argumentsProperty = newCall.FindPropertyRelative("m_Arguments");
SerializedProperty objectArgumentProperty = argumentsProperty.FindPropertyRelative("m_ObjectArgument");
objectArgumentProperty.objectReferenceValue = null; // 对于 EventDefined这个通常为 null
SerializedProperty argumentAssemblyTypeProperty = argumentsProperty.FindPropertyRelative("m_ObjectArgumentAssemblyTypeName");
argumentAssemblyTypeProperty.stringValue = "UnityEngine.Sprite, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"; // 必须是完整的类型名
// 5. 应用所有修改
// 这一步至关重要!它将我们在代码中做的所有更改写入到实际的组件和场景数据中。
// 同时,它会将对象标记为 "Dirty",提示 Unity 需要保存场景/预制体的更改。
serializedObject.ApplyModifiedProperties();
// 最后,给用户一个反馈
EditorUtility.DisplayDialog("成功", $"已成功为 '{localizeEvent.name}' 配置事件监听器,连接到 '{localizeEvent.gameObject.name}' 的 sprite 属性。", "确定");
Debug.Log($"<color=green>自动配置完成:</color> {localizeEvent.name}.OnAssetUpdated -> {localizeEvent.gameObject.name}.Image.sprite", localizeEvent);
}
}
}