案例来源:Localization Tools
资源:Localization Tools资源
界面完全可以自己弄只是没那么靓靓而已..
Localization Tools (本地化工具),当一个游戏需要支持多语言时,使用Localization Tools可以更高效的完成多语言编辑工作。
Hierarchy如下:
1、布置UI界面(如果使用资源的话无需自行布置)
LoadingScreen初始布局如下:
MenuScreen布局如下:
English版本
German版本
流程:
1. 在LoadingScreen中选择语言;
2. MenuScreen显示对应语言。
2、创建JSON文件(英语和德语版)
选择存储数据的格式,这里选择JSON用于保存不同语言下各UI对应的文本。
例如:key为game_title的Text的文本为Quiz Spiel;
localizedText_de.json:
{
"items":
[
{
"key":"game_title",
"value":"Quiz Spiel"
},
{
"key":"start_button",
"value":"Spiel Beginnen"
}
]
}
localizedText_en:
{
"items":
[
{
"key":"game_title",
"value":"Quiz Game"
},
{
"key":"start_button",
"value":"Start Game"
}
]
}
3、构建类结构来存储JSON数据:
LocalizationData:
[System.Serializable]
public class LocalizationData
{
public LocalizationItem[] items; // store a localizationItem list.
}
[System.Serializable]
public class LocalizationItem // store a item
{
public string key; // similar to json's key
public string value; // similar to json's value
}
4、构建LocalizationManager
实现根据文件名读出对应的json数据,并将数据存入LocalizationData实例中。
using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LocalizationManager : MonoBehaviour {
public static LocalizationManager instance; // Single Instance
private Dictionary localizedText; // data similar to json files, the first string is key, second string is value
private bool isReady = false; // mark if load json data process has finished.
private string missingTextString = "Localized text not found."; // default result to return
void Awake()
{
if (instance == null) {
instance = this;
} else if (instance != this) {
Destroy (gameObject);
}
DontDestroyOnLoad (gameObject); // when load another scene, not destroy this manager instance.
}
public void LoadLocalizedText(string fileName) // filename: json file name, remember to add ".json"
{
localizedText = new Dictionary ();
string filePath = Path.Combine (Application.streamingAssetsPath, fileName); // get absolutly path of json file. json file store in Assets/StreamingAssets
if (File.Exists (filePath)) {
string dataAsJson = File.ReadAllText (filePath); // read all text from json file.
LocalizationData loadedData = JsonUtility.FromJson (dataAsJson); // transform the data from tpye string to LocalizationData
for (int i = 0; i < loadedData.items.Length; i++) {
localizedText.Add (loadedData.items [i].key, loadedData.items [i].value); // ergodic all data an store it into localizedText
}
Debug.Log ("Data loaded, dictionary contains: " + localizedText.Count + " entries."); // show how many item loaded success.
} else {
Debug.LogError ("Cannot find file!"); // show error.
}
isReady = true; // After finished json loaded, set the mark to true,so the MenuScreen scene can be loaded.
}
public string GetLocalizedValue(string key) // return value according the key.
{
string result = missingTextString;
if (localizedText.ContainsKey (key)) {
result = localizedText [key];
}
return result;
}
public bool GetIsReady() // return the status of json data loaded protcss.
{
return isReady;
}
}
5、设置不同Button下通过LocalizationManager加载不同语言的JSON文件。
打开LoadingScreen,在LoadingScreen下创建一个名为LocalizationManager的空物体,将LocalizationManager脚本附加到该物体上。
选择EnBtn,在Button Component的OnClick()事件中添加一项,将刚刚创建的LocalizationManager物体拖拽到上面,设置LoadLocalizedText方法并设置文件名为localizedText_en.json(记得加后缀)。
GeBtn也执行同样的操作,只是它的文件名为localizedText_de.json。
然后我们就可以Ctrl+P来进行观察:
点击不同button,查看Console Panel下是否有提示信息输出,如果没有,请检查你的代码、json文件的存放位置。
6、构建StartUpManger类
为了防止点击按钮后,json文件还未加载完毕就加载场景,导致出现一些问题,我们应该对json文件的加载过程进行检测,只有加载完毕json文件后,才能进入MenuScreen。因此,在LocalizationManager类中我们使用了bool类型的isReady来标识当前json文件的加载是否完毕,并实现了读取isReady的接口,所以在StartUpManager中我们只需要检测isReady的状态便可决定是否Load到MenuScreen中。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class StartUpManager : MonoBehaviour {
IEnumerator Start()
{
while (!LocalizationManager.instance.GetIsReady ()) {
// Wait a frame.
yield return null;
}
SceneManager.LoadScene ("MenuScreen");
}
}
使用协程,如果JSON文件加载完毕,则让Start()等待一帧。
在LoadingScreen中创建一个名为StartUpManager的空物体,并将StartUpManager脚本拖拽到上面,然后Ctrl+P,点击Button,看看是否成功加载到MenuScreen中。
注意,请把LoadingScreen和MenuScreen都添加到BuildSettings中。
7、构建LocalizedText实现不同UI对应不同文本
在LocalizationManager中实现了GetLocalizedValue方法,设置不同的key来读取对应的值。
LocalizedText:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class LocalizedText : MonoBehaviour {
public string key; // the key of UI.
void Start()
{
Text text = GetComponent ();
text.text = LocalizationManager.instance.GetLocalizedValue (key); // set the ui's text
}
}
将LocalizedText脚本附加到MenuScreen的Title Text和button的text中。
设置Title Text的LocalizedText 对应的key为game_titile;
Text的LocalizedText 对应的key为start_button;
8、完成所有逻辑!
打开LoadingScreen Scene,Ctrl+P执行并进行调试,观察点击不同语言按钮后MenuScreen内UI的Text是否有转换为相对应的语言。
注意,有时候Text的value太长,会导致只显示一部分的text,因此需要根据实际情况来对UI进行范围的调整。
9、自定义编辑器
如果开发的游戏比较大型,并且该游戏需要多语言支持的时候,我们需要翻译,但如果队伍里没有相应的翻译人员或者翻译人员不懂Unity的使用方法的话,翻译工作还是会显得很蛋疼。我们希望,翻译人员不需要懂得编程便可以对游戏进行翻译工作。通过自定义编辑器,我们可以实现新建、保存、和打开指定的JSON文件并进行方便直观的翻译工作。
在Scripts文件夹下创建Editor文件夹,在Editor文件夹下新建C#文件LocalizedTextEditor。
LocalizedTextEditor:
using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class LocalizedTextEditor : EditorWindow {
public LocalizationData localizationData; // The LocalizationData we current editing.
[MenuItem("Window/Localized Text Editor")] // we can show the localized text editor by select Window/Localized Text Editor
static void Init()
{
EditorWindow.GetWindow (typeof(LocalizedTextEditor)).Show (); // show the editor window.
}
void OnGUI() // determine what the localized text editor window show. after open the editor, it will update editor per frame.
{
if (localizationData != null) {
SerializedObject serializedObject = new SerializedObject (this); // the window connect to this.
SerializedProperty serializedProperty = serializedObject.FindProperty ("localizationData"); // get localizationData from this.
EditorGUILayout.PropertyField (serializedProperty, true); // set the localizationData can be fold
serializedObject.ApplyModifiedProperties (); // Apply these settings.
if (GUILayout.Button ("Save data")) { // Draw Save data Button to execute SaveGameData() logic.
SaveGameData ();
}
}
if (GUILayout.Button ("Load data")) { // Draw Load data Button to execute LoadGameData() logic.
LoadGameData ();
}
if (GUILayout.Button ("Create new data")) { // Draw Create new data to execute CreateNewData() logic.
CreateNewData ();
}
}
void LoadGameData()
{
string filePath = EditorUtility.OpenFilePanel ("Select localization data file", Application.streamingAssetsPath, "json"); // get the filePath of the json file selected by user.
if (!string.IsNullOrEmpty (filePath)) {
string dataAsJson = File.ReadAllText (filePath); // read all data from json.
localizationData = JsonUtility.FromJson (dataAsJson); // store json data into localizationData.
}
}
void SaveGameData()
{
string filePath = EditorUtility.SaveFilePanel ("Save localization data file", Application.streamingAssetsPath, "", "json"); // set the save path of json file we just edited.
if (!string.IsNullOrEmpty (filePath)) {
string dataAsJson = JsonUtility.ToJson (localizationData); // Transform the localizationData to string
File.WriteAllText (filePath, dataAsJson); // store the data into json file.
}
}
void CreateNewData()
{
localizationData = new LocalizationData (); // create a new localizationData instance.
}
}
Editor Window的更多详情可查看Documentation - Editor Window
脚本编写完毕便可打开Localized Text Editor来体验它的方便之处了~
很多时候,编写不同的Unity工具可以提高团队开发效率,这个小技巧不仅仅是适用于当前的案例,就像最近Unity3d官方发布的2D Game Kits,让用户可以根据自己的喜好制作一款横版2d闯关游戏,而不需要关注太多的编码细节。
以上,完毕!