UGUI 文本多语言方案

UGUI 文本多语言方案

这是侑虎科技第457篇文章,感谢作者林健供稿。欢迎转发分享,未经作者授权请勿转载。如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。QQ群:793972859(原群已满员)。

作者主页:https://zhuanlan.zhihu.com/p/43374662

作者也是U Sparkle活动参与者,UWA欢迎更多开发朋友加入U Sparkle开发者计划,这个舞台有你更精彩!

前言

为了使全世界的玩家都能自由流畅的玩到游戏,游戏支持多国语言是一个常见的需求。游戏的多语言涉及到UI显示,声音,图片等等,而UI中文本的多语言处理是基础。

Unity引擎主流的两套UI框架中,NGUI默认实现了UI文本的多语言功能,而UGUI并没有原生实现。导致我们的项目从NGUI迁移到UGUI的过程中遇到了麻烦,本文介绍了目前UGUI框架下文本多语言的几种方案,以及我们的实现。

我们的需求

1.参考NGUI,使用一套key对应多套文本的方式,来实现多语言。

2.支持游戏内动态切换语言,切换语言后自动刷新UI显示。

3.使用简单、方便,不引入插件。

调研

调研了一些市面上现有的UGUI的多语言方案:

Unity3D UGUI多语言

原理:给Text绑定Key,在程序开始时设置本地语言。 需附加额外脚本,无运行时动态切换语言功能。

Unity3D实现多语言本地化

原理及功能和上面的方案类似。

Unity UGUI 本地化方案

不需要附加新的脚本,基于原生的Text组件进行扩展,但无运行时动态切换语言。

Unity国际化多语言设置

基于NGUI的原理,通过附加脚本形式实现多语言,但无运行时动态切换语言。

AssetStore 上的方案:I2 Localization

功能强大,但是需要集成插件,较为臃肿,会增加使用的复杂度,并且是收费的。至于是否可以运行时动态切换语言暂时未知。

经过调研,现有的方案并不能完全满足我的需求。大多要添加额外的组件脚本,或是无动态切换语言功能,不能切换字体,使用复杂等等。

我的方案主要解决了几个痛点:

1.无需附加额外脚本进行文本的多语言。

2.支持运行时切换语言,并自动刷新多语言文本。

3.脚本上用预制体存储字体引用,多语言时替换不同字体只要替换几个预制体即可。而UGUI原生的方式是要替换项目中所有Text文件的字体属性。

4.增加菜单栏集成,创建多语言Text组件和原生Text是一致的,使用方便。

5.使用习惯和NGUI的多语言较为一致,无需插件,只需几个cs脚本即可。

具体实现

1.添加组件

请输入图片描述

2.设置字体和文本的Key(这两项不能为空)
请输入图片描述

这里的Key_String,是沿用NGUI 里的多语言方案的形式,就是采用一个Key对应多种语言文本的方式。下图中是对应的各个语言的文本及内容。
请输入图片描述

CustomFont 是也是参照NGUI的多语言方案的使用习惯,把实际的字体文件通过引用的方式存储为Prefab,这样将来替换不同语言的字体文件时,只需要替换少量的字体Prefab文件即可,而不是像UGUI官方原生的那样需要替换所有Text组件的Font。Prefab如下图:
请输入图片描述

3.一个API实现切换语言动态刷新
请输入图片描述

以下为原理:

1.总体文本方案,是将NGUI的Localization脚本拿过来,用来加载文本源数据,然后Text基础上扩展了自定义组件,用于指定Key和字体,并用管理器通过注册事件形式,来实现统一的UI切换语言后的自动刷新。

思路总的来说,是借鉴了NGUI的那套多语言方案,经过改造成为UGUI的版本,解决了UGUI 字体替换麻烦,不能指定文本Key,而且默认在切换字体后不能自动刷新的问题,这都是UGUI相比NGUI缺少的机制,这里进行了补全。中间很多东西参考了UGUI的源码,有些地方进行了重载,有些地方直接为了工具化使用,直接扒了少量代码。

2.扩展菜单实现:

 public class LocalizationMenuExtension : MonoBehaviour {

    [MenuItem("GameObject/UI/Text_Local", false, 2000)]
    static public void AddText(MenuCommand menuCommand)
    {
        GameObject go = new GameObject("Text");
        LocalizationText txt = go.AddComponent<LocalizationText>();
        PlaceUIElementRoot(go, menuCommand);
        LocalizationManager.InitValue(txt);
    }
   //... 有省略,上面只放最核心部分
}

3.扩展Inspectors:

 [CustomEditor(typeof(LocalizationText))]
public class LocalizationTextEditor : UnityEditor.UI.TextEditor
{

    public override void OnInspectorGUI()
    {
        LocalizationText component = (LocalizationText)target;
        base.OnInspectorGUI();
        component.KeyString = EditorGUILayout.TextField("Key String", component.KeyString);
        component.CustomFont = (UIFontObj)EditorGUILayout.ObjectField("Custom Font", component.CustomFont, typeof(UIFontObj), true);
    }
}

4.扩展Text组件:

using System;
using UnityEngine.UI;
using UnityEngine;
/// <summary>
/// Custom Text Control used for localization text.
/// </summary>
[AddComponentMenu("UI/Text_Local", 10)]
public class LocalizationText : Text
{
    protected override void Awake()
    {
        base.Awake();
        LocalizationManager.InitValue(this);
        font = CustomFont.UseFont;
    }

    protected override void OnEnable()
    {
        base.OnEnable();
        LocalizationManager.OnLocalize += OnLocalize;  
    }
    protected override void OnDisable()
    {
        base.OnDisable();
        if (LocalizationManager.OnLocalize!=null)
        {
            LocalizationManager.OnLocalize -= OnLocalize;
        }
     
    }

    /// <summary>
    /// 文本的key
    /// </summary>
    public string KeyString;

    /// <summary>
    /// 自定义字体,方便后期替换
    /// </summary>
    public UIFont CustomFont;

    /// <summary>
    /// 重新本地化,用于游戏内切换语言时调用
    /// </summary>
    public void OnLocalize()
    {
        text = Localization.Get(KeyString);
    }

    #region Override Part
    public override string text
    {
        get
        {
            m_Text = Localization.Get(KeyString);
            return m_Text;
        }
        set
        {
            if (String.IsNullOrEmpty(value))
            {
                if (String.IsNullOrEmpty(m_Text))
                    return;
                m_Text = "";
                SetVerticesDirty();
            }
            else if (m_Text != value)
            {
                m_Text = value;
                SetVerticesDirty();
                SetLayoutDirty();
            }
        }
    }

    

    #endregion

}

附上Demo和Package,使用的是Unity2017.4.8f1版本。

文末,再次感谢林健的分享,如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。QQ群:793972859(原群已满员)。

也欢迎大家来积极参与U Sparkle开发者计划,简称"US",代表你和我,代表UWA和开发者在一起!