文章目录
前言
开发超休闲小游戏时,由于大部分游戏都是出海发布的,所以需要支持多语言。
目前运用比较广泛的本地化插件为I2 Localization。不过该插件功能繁杂,于是编写了一个简易版的语言本地化功能。
一、基本功能介绍
项目开发过程中,将数据存放在Excel中方便查看也方便更改。然后通过读取Excel的内容,将其表中内容读取到项目中对应的class实例(LocalizationData)中,然后将该实例生成一个Asset,如图所示:
生成好之后,我们就可以在游戏中通过这个Asset来获取我们需要的数据啦~
二、功能详解
1.创建Excel表格,确定所需数据
- 首先我们需要知道自己需要哪些数据。这里以我开发的俄罗斯方块案例来说明吧,游戏界面如下:
- 创建Excel表格,填写数据信息,如图所示:
- 根据Excel表格,我们需要定义两个枚举。
第一个枚举:语言类型。
//这里保证与Excel表顺序一致
public enum LanguageType
{
English = 0,//默认语言类型为英文
Chinese = 1
}
第二个枚举:需要进行本地化的文本类型Key。
public enum TextKey
{
Racing_Bricks,
Score,
High_Score,
Speed,
Quick,
Down,
Left,
Right,
Rotate_Direction
}
- 根据Excel表格,定义三个基础类。
第一个类:用来存放每行的单元格表数据。
using System;
using UnityEngine;
namespace SmipleLocalization
{
[Serializable]
public class ItemData
{
[SerializeField]
private string itemName;//语言名称,主要是方便编辑器上查看
public LanguageType languageType;//语言类型
public string content;//对应的内容
//构造函数:初始化数据成员
public ItemData(LanguageType _languageType, string _content)
{
this.languageType = _languageType;
this.itemName = _languageType.ToString();
this.content = _content;
}
}
}
第二个类:用来存放前者的数组即每行的表数据。
using System;
using System.Collections.Generic;
using UnityEngine;
namespace SmipleLocalization
{
[Serializable]
public class TextLocalization
{
[SerializeField]
private string keyName;//当前文本类型名称,主要是方便编辑器上查看
public TextKey curTextKey;//该文本类型
public List<ItemData> itemDatas = new List<ItemData>();//当前文本类型对应的语言本地化文本信息
//构造函数:初始化
public TextLocalization(string _textKey, params string[] language)
{
this.keyName = _textKey;
this.curTextKey = (TextKey)Enum.Parse(typeof(TextKey), _textKey);
for (int i = 0; i < language.Length; i++)
{
ItemData cnData = new ItemData((LanguageType)i, language[i]);
itemDatas.Add(cnData);
}
}
//根据语言类型查找对应语言文本信息
public ItemData GetType(LanguageType t)
{
for (int i = 0; i < this.itemDatas.Count; i++)
{
if (this.itemDatas[i].languageType == t)
{
return this.itemDatas[i];
}
}
throw new KeyNotFoundException("Language not found: " + t);
}
}
}
第三个类:用来存放第二个类的数组即所有的表数据。
using UnityEngine;
using System;
using System.Collections.Generic;
namespace SmipleLocalization
{
public class LocalizationData : ScriptableObject
{
public List<TextLocalization> localizationTxts = new List<TextLocalization>();
/// <summary>
/// 根据TextKey获取对应的所有语言类型信息
/// </summary>
/// <param name="k">需要本地化的文本</param>
public TextLocalization GetTextInfo(TextKey k)
{
for (int i = 0; i < this.localizationTxts.Count; i++)
{
if (this.localizationTxts[i].curTextKey == k)
{
return this.localizationTxts[i];
}
}
//return null;
throw new KeyNotFoundException("TextKey not found: " + k);
}
}
}
2.读取Excel表格
- 需要引入Excel.dll和ICSharpCode.SharpZipLib库文件,放到Plugins文件夹下。
下载地址:Unity读取和写入Excel表格所需dll-Unity3D文档类资源-CSDN文库
- 在Editor文件夹下创建cs文件去实现读取Excel操作。
1.定义一个类,里面存放我们需要的字段:
using UnityEngine;
namespace SmipleLocalization
{
public class LocalizationExcelConfig : MonoBehaviour
{
/// <summary>
/// 存放将Excel表格数据转化CS文件的文件夹路径
/// </summary>
public static readonly string assetFolderPath = "Assets/Resources/Datas/";
/// <summary>
/// 存放将Excel表格数据转化CS文件的路径
/// </summary>
public static readonly string assetPath = string.Format("{0}{1}.asset", assetFolderPath, "LanguageLocalizationData");
/// <summary>
/// 加载文件的路径
/// </summary>
public static readonly string loadAssetPath = "Datas/LanguageLocalizationData";
}
}
2.定义一个类,用来读取Excel表格的数据,并解析数据存放到Asset中
using System.Collections.Generic;
using System.Data;
using System.IO;
using Excel;
namespace SmipleLocalization
{
public class LocalizationExcelTool
{
/// <summary>
/// 读取表格数据,生成对应的数据
/// </summary>
public static List<TextLocalization> CreateDataWithExcel(string filePath)
{
List<TextLocalization> localizationTxts = new List<TextLocalization>();
//获得表格数据
int row = 0, column = 0;
DataRowCollection collection = ReadExcel(filePath, ref row, ref column);
//根据Excel表的定义,第二行开始才是数据
for (int i = 1; i < row; i++)
{
string[] language = new string[column - 1];
for (int j = 1; j < column; j++)
{
language[j - 1] = collection[i][j].ToString();
}
TextLocalization tl = new TextLocalization(collection[i][0].ToString(), language);
localizationTxts.Add(tl);
}
return localizationTxts;
}
/// <summary>
/// 读取Excel文件内容
/// </summary>
/// <param name="filePath">文件路径</param>
/// <param name="row">行数</param>
/// <param name="column">列数</param>
private static DataRowCollection ReadExcel(string filePath, ref int row, ref int column)
{
FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
IExcelDataReader edReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
DataSet result = edReader.AsDataSet();
//Tables[0]下标0表示Excel文件中第一张表的数据
row = result.Tables[0].Rows.Count;
column = result.Tables[0].Columns.Count;
return result.Tables[0].Rows;
}
}
}
3.将拿到的数据存放在LocalizationData中,并生成一个Asset文件:
using System.IO;
using UnityEditor;
using UnityEngine;
namespace SmipleLocalization
{
public class BuildExcelDataEditor : Editor
{
[MenuItem("CustomEditor/CreateLocalizationData")]
public static void CreateLocalizationData()
{
//打开Excel
string filePath = EditorUtility.OpenFilePanel("Open Localization Excel", "", "xlsx");
//获取Excel数据
LocalizationData data = ScriptableObject.CreateInstance<LocalizationData>();
data.localizationTxts = LocalizationExcelTool.CreateDataWithExcel(filePath);
//判断文件夹是否存在
if (!Directory.Exists(LocalizationExcelConfig.assetFolderPath))
{
Directory.CreateDirectory(LocalizationExcelConfig.assetFolderPath);
}
string assetPath = LocalizationExcelConfig.assetPath;
//判断文件是否已经存在
//若已经存在,则删除该文件
if (File.Exists(assetPath))
{
AssetDatabase.DeleteAsset(assetPath);
}
//生成一个Asset文件
AssetDatabase.CreateAsset(data, assetPath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}
}
这样我们就可以在Unity中选择我们自定义的菜单CustromEditor->CreateLocalizationData,然后选择我们需要的Excel表格,即可生成Asset了。
3.处理Unity中需要本地化的Text
- 创建一个类,在游戏运行时,加载数据,为目标提供本地化方法
using UnityEngine;
using System;
namespace SmipleLocalization
{
public static class LocalizationSys
{
private static LocalizationData localizationDatas;
public static Action ChangeCurLanguageTypeEvent;//监听语言类型变化事件,在事件触发时设置内容,主要是方便用来游戏运行中测试本地化
private static LanguageType mCurLanguageType;
static LocalizationSys()
{
InitDatas();
InitLanguageType();
}
/// <summary>
/// 加载数据
/// </summary>
private static void InitDatas()
{
localizationDatas = Resources.Load<LocalizationData>(LocalizationExcelConfig.loadAssetPath);
}
/// <summary>
/// 根据系统语言设置默认语言
/// </summary>
private static void InitLanguageType()
{
SystemLanguage languageStr = Application.systemLanguage;
if (languageStr == SystemLanguage.Chinese ||
languageStr == SystemLanguage.ChineseSimplified)
{
CurLanguageType = LanguageType.Chinese;
}
else
{
CurLanguageType = LanguageType.English;
}
}
/// <summary>
/// 设置当前语言类型
/// </summary>
public static LanguageType CurLanguageType
{
set
{
if (mCurLanguageType != value)
{
mCurLanguageType = value;
ChangeCurLanguageTypeEvent?.Invoke();
}
}
}
/// <summary>
/// 对应的语言本地化
/// </summary>
/// <param name="textKey">需要本地化的文本</param>
public static string Localize(TextKey textKey)
{
return Localize(textKey, mCurLanguageType);
}
/// <summary>
/// 获取对应的语言本地化
/// </summary>
/// <param name="textKey">需要本地化的文本</param>
/// <param name="languageType">语言类型</param>
/// <returns></returns>
private static string Localize(TextKey textKey, LanguageType languageType)
{
string value = string.Empty;
TextLocalization textInfo = localizationDatas.GetTextInfo(textKey);
if (textInfo != null)
{
ItemData typeInfo = textInfo.GetType(languageType);
//如果没找到对应语言本地化信息,则设置默认英文信息
if (typeInfo == null)
{
typeInfo = textInfo.GetType(LanguageType.English);
}
value = typeInfo.content;
}
return value;
}
}
//这里保证与Excel表顺序一致
public enum LanguageType
{
English = 0,//默认语言类型为英文
Chinese = 1
}
public enum TextKey
{
Racing_Bricks,
Score,
High_Score,
Speed,
Quick,
Down,
Left,
Right,
Rotate_Direction
}
}
- 创建一个类,用来设置文本文字内容
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace SmipleLocalization
{
public class UITextLocalize : MonoBehaviour
{
private Text mText;
[SerializeField]
private TextKey textKey;
private void Awake()
{
this.mText = base.GetComponent<Text>();
}
private void Start()
{
LocalizationSys.ChangeCurLanguageTypeEvent += OnChangeLanguage;
this.OnChangeLanguage();
}
private void OnDestroy()
{
LocalizationSys.ChangeCurLanguageTypeEvent -= OnChangeLanguage;
}
private void OnChangeLanguage()
{
this.StaticSet();
}
private void StaticSet()
{
this.mText.text = LocalizationSys.Localize(textKey);
}
}
}
- 在需要本地化的文本上挂上UITextLocalize脚本,并配置对应的信息,如下所示:
- 如果我们需要在游戏中测试多语言功能,可以在画布下创建一个DropDown,设置Options,添加OnValueChanged事件,如下所示:
public void OnChangeLanguage(int value)
{
LocalizationSys.CurLanguageType = (LanguageType)value;
}
这样我们就可以在游戏运行中查看我们的多语言本地化是否正确了。
三、扩展功能
这款游戏中我只用到了静态文本,但是有时候我们还需要简单的动态修改文本内容。这个时候我们修改一下UITextLocalize脚本即可,如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace SmipleLocalization
{
public class UITextLocalize : MonoBehaviour
{
private Text mText;
[SerializeField]
private TextKey textKey;
[SerializeField]
private LocalizeType localizeType = LocalizeType.Static;
private object[] args;
private void Awake()
{
this.mText = base.GetComponent<Text>();
}
private void Start()
{
LocalizationSys.ChangeCurLanguageTypeEvent += OnChangeLanguage;
this.OnChangeLanguage();
}
private void OnDestroy()
{
LocalizationSys.ChangeCurLanguageTypeEvent -= OnChangeLanguage;
}
private void OnChangeLanguage()
{
if (this.localizeType == LocalizeType.Static)
{
this.StaticSet();
}
else if(this.args != null)
{
this.DynamicSet(this.args);
}
}
private void StaticSet()
{
this.mText.text = LocalizationSys.Localize(textKey);
}
/// <summary>
/// 动态设置文本内容
/// </summary>
public void DynamicSet(params object[] _args)
{
string format = LocalizationSys.Localize(textKey);
this.mText.text = string.Format(format, _args);
this.args = _args;
}
}
}
然后在Inspector面板上设置UITextLocalize的LocalizeType为Dynamic。
接着在需要动态修改Text内容时,不是调用GetComponent<Text>().text去设置,而是调用GetComponent<UITextLocalize>().DynamicSet(),传入需要修改的数据即可。参考如下:
总结
大家也可以试试导入EPPlus.dll,用来向Excel写入数据。后续会给大家整理一下。今天就到这里啦~
以上就是今天要讲的内容,如果有哪里不懂的可以留言交流一下。