using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; /// /// 文本组件类型枚举 /// public enum TextComponentType { None = 0, Text = 1, TextEx = 2, GradientText = 3 } /// /// 多语言排版配置项,存储单个语言的布局和适配数据 /// [Serializable] public class LanguageConfigItem { // ============ RectTransform 配置 ============ [Header("RectTransform 配置")] public Vector2 anchoredPosition = Vector2.zero; public Vector2 sizeDelta = new Vector2(100f, 30f); public Vector2 anchorMin = new Vector2(0.5f, 0.5f); public Vector2 anchorMax = new Vector2(0.5f, 0.5f); public Vector2 pivot = new Vector2(0.5f, 0.5f); public Vector3 localScale = Vector3.one; public Vector3 localRotation = Vector3.zero; // ============ Text 排版与适配配置 ============ [Header("Text 排版与适配配置")] public Font font; public FontStyle fontStyle = FontStyle.Normal; public int fontSize = 14; public float lineSpacing = 1f; public HorizontalWrapMode horizontalOverflow = HorizontalWrapMode.Wrap; public VerticalWrapMode verticalOverflow = VerticalWrapMode.Truncate; public bool resizeTextForBestFit = false; public int resizeTextMinSize = 10; public int resizeTextMaxSize = 40; public TextAnchor alignment = TextAnchor.UpperLeft; public bool alignByGeometry = false; /// 将配置应用到 RectTransform public void ApplyToRectTransform(RectTransform rectTransform) { if (rectTransform == null) return; rectTransform.anchorMin = anchorMin; rectTransform.anchorMax = anchorMax; rectTransform.pivot = pivot; rectTransform.anchoredPosition = anchoredPosition; rectTransform.sizeDelta = sizeDelta; rectTransform.localScale = localScale; rectTransform.localRotation = Quaternion.Euler(localRotation); } /// 从 RectTransform 读取配置 public void ReadFromRectTransform(RectTransform rectTransform) { if (rectTransform == null) return; anchorMin = rectTransform.anchorMin; anchorMax = rectTransform.anchorMax; pivot = rectTransform.pivot; anchoredPosition = rectTransform.anchoredPosition; sizeDelta = rectTransform.sizeDelta; localScale = rectTransform.localScale; localRotation = rectTransform.localRotation.eulerAngles; } /// 将配置应用到 Text public void ApplyToText(Text textComponent) { if (textComponent == null) return; if (font != null) textComponent.font = font; textComponent.fontStyle = fontStyle; textComponent.fontSize = fontSize; textComponent.lineSpacing = lineSpacing; textComponent.horizontalOverflow = horizontalOverflow; textComponent.verticalOverflow = verticalOverflow; textComponent.resizeTextForBestFit = resizeTextForBestFit; if (resizeTextForBestFit) { textComponent.resizeTextMinSize = resizeTextMinSize; textComponent.resizeTextMaxSize = resizeTextMaxSize; } textComponent.alignment = alignment; textComponent.alignByGeometry = alignByGeometry; } /// 从 Text 读取配置 public void ReadFromText(Text textComponent) { if (textComponent == null) return; font = textComponent.font; fontStyle = textComponent.fontStyle; fontSize = textComponent.fontSize; lineSpacing = textComponent.lineSpacing; horizontalOverflow = textComponent.horizontalOverflow; verticalOverflow = textComponent.verticalOverflow; resizeTextForBestFit = textComponent.resizeTextForBestFit; resizeTextMinSize = textComponent.resizeTextMinSize; resizeTextMaxSize = textComponent.resizeTextMaxSize; alignment = textComponent.alignment; alignByGeometry = textComponent.alignByGeometry; } /// /// 快速克隆配置 /// 由于内部均为值类型或需浅拷贝的引用(Font),可以直接使用 MemberwiseClone /// public LanguageConfigItem Clone() { return (LanguageConfigItem)this.MemberwiseClone(); } } /// /// 语言配置字典(Unity 内置序列化支持的双 List 结构) /// [Serializable] public class LanguageConfigDictionary { public List keys = new List(); public List values = new List(); public LanguageConfigItem Get(string key) { if (string.IsNullOrEmpty(key)) return null; int index = keys.IndexOf(key); return (index >= 0 && index < values.Count) ? values[index] : null; } public void Set(string key, LanguageConfigItem value) { int index = keys.IndexOf(key); if (index >= 0 && index < values.Count) { values[index] = value; } else { keys.Add(key); values.Add(value); } } public bool ContainsKey(string key) => keys.Contains(key); public void Remove(string key) { int index = keys.IndexOf(key); if (index >= 0) { keys.RemoveAt(index); if (index < values.Count) values.RemoveAt(index); } } } /// /// 文本多语言排版适配组件 /// [RequireComponent(typeof(RectTransform))] public class TextLanguageAdapter : MonoBehaviour { public const string DefaultLangId = "default"; // 提取常量避免硬编码错误 [SerializeField] [Tooltip("每种语言的配置,使用语言ID作为key(如zh、en、ft),default为默认配置")] private LanguageConfigDictionary m_LanguageConfigs = new LanguageConfigDictionary(); [SerializeField] [Tooltip("自动检测到的文本组件类型")] private TextComponentType m_TargetTextType = TextComponentType.None; [SerializeField] [Tooltip("关联的文本组件引用")] private Component m_TargetTextComponent; private bool m_IsApplied = false; #region 公共属性 public TextComponentType TargetTextType => m_TargetTextType; public Component TargetTextComponent => m_TargetTextComponent; // 开放给 Editor 使用,避免反射 public LanguageConfigDictionary LanguageConfigs => m_LanguageConfigs; #endregion #region Unity生命周期 protected void Awake() { DetectTargetComponent(); } protected void OnEnable() { // 保证仅应用一次排版以节省性能,除非在编辑器非运行模式下需要强刷 if (!Application.isPlaying || !m_IsApplied) { string langId = Application.isPlaying ? Language.Id : DefaultLangId; ApplyConfig(langId); } } #if UNITY_EDITOR protected void Reset() { DetectTargetComponent(); if (!HasConfig(DefaultLangId)) { ReadCurrentToConfig(DefaultLangId); } } #endif #endregion #region 公共配置方法 public LanguageConfigItem GetConfig(string languageId) { // 1. 尝试获取目标语言配置 LanguageConfigItem config = m_LanguageConfigs.Get(languageId); if (config != null) return config; // 2. 找不到则回退到默认配置 config = m_LanguageConfigs.Get(DefaultLangId); if (config != null) return config; Debug.LogError($"[TextLanguageAdapter] 找不到语言 {languageId} 的配置,且不存在 default 默认配置!"); return null; } public void SetConfig(string languageId, LanguageConfigItem config) => m_LanguageConfigs.Set(languageId, config); public void RemoveConfig(string languageId) => m_LanguageConfigs.Remove(languageId); public bool HasConfig(string languageId) => m_LanguageConfigs.ContainsKey(languageId); public List GetConfiguredLanguages() => new List(m_LanguageConfigs.keys); public void ApplyConfig(string languageId) { LanguageConfigItem config = GetConfig(languageId); if (config == null) return; if (TryGetComponent(out RectTransform rectTransform)) config.ApplyToRectTransform(rectTransform); if (m_TargetTextComponent is Text textComponent) config.ApplyToText(textComponent); m_IsApplied = true; } public void ReadCurrentToConfig(string languageId) { LanguageConfigItem config = new LanguageConfigItem(); if (TryGetComponent(out RectTransform rectTransform)) config.ReadFromRectTransform(rectTransform); if (m_TargetTextComponent is Text textComponent) config.ReadFromText(textComponent); SetConfig(languageId, config); } public static string GetLanguageShowName(string languageId) { if (Language.languageShowDict != null && Language.languageShowDict.TryGetValue(languageId, out string showName)) { return showName; } return languageId; } #endregion #region 私有组件检测 private void DetectTargetComponent() { if (m_TargetTextComponent != null) { DetermineTextType(m_TargetTextComponent); return; } // 按优先级查找组件 m_TargetTextComponent = FindComponent() ?? FindComponent() ?? (Component)FindComponent(); DetermineTextType(m_TargetTextComponent); } /// 辅助方法:在自身和子物体中查找组件 private T FindComponent() where T : Component { T comp = GetComponent(); return comp != null ? comp : GetComponentInChildren(true); } private void DetermineTextType(Component component) { m_TargetTextType = component switch { GradientText _ => TextComponentType.GradientText, TextEx _ => TextComponentType.TextEx, Text _ => TextComponentType.Text, _ => TextComponentType.None }; } #endregion #if UNITY_EDITOR [ContextMenu("刷新组件检测")] public void Editor_ForceRefreshDetection() { DetectTargetComponent(); UnityEditor.EditorUtility.SetDirty(this); } [ContextMenu("读取当前配置")] public void Editor_ReadCurrentConfig() { ReadCurrentToConfig(DefaultLangId); UnityEditor.EditorUtility.SetDirty(this); } [ContextMenu("应用默认配置")] public void Editor_ApplyDefaultConfig() { ApplyConfig(DefaultLangId); } #endif }