UnityのTextAssetを使った多言語対応(ローカライズ)の方法
UnityのTextAssetを使って多言語対応(ローカライズ)してみた
Unity開発における多言語対応についてあんまり良い感じの記事が見当たらなかったので、とりあえずUnity始めて1週間(本記事執筆時点)の自分が自己流&手抜きで多言語対応の仕組みを作ってみました。
以下、開発環境など前提です。
- Windows 7
- Unity 5.3.1
- C#プログラミング
- 2D用プロジェクト(多分3D用でも変わらないと思うけど、念のため)
- csvのようなテキストファイルで手軽に管理したい
- InspectorのようなGUI上ではなく、スクリプト上から使用したい
という訳で、さくさく紹介して行きます。
Unityで多言語対応する方法
手順としてはこんな感じです。
- 言語ごとの文字列定義ファイルを用意する
- 文字列アクセス用のクラスを作る
- 出来上がったものを使う
1. 言語ごとの文字列定義ファイルを用意する
まず文字列定義ファイルを用意します。
とりあえず日本語と英語の2つを作ってみました。
※1行はキー値、タブ文字(“\t”)、文字列定義という構成になっています。下のソースではタブ文字がスペースに変換されてしまっているので、コピペする際はご注意ください。
- japanese.csv
RETURN 戻る DUNGEON ダンジョン TALK 話しかける
- english.csv
RETURN Return DUNGEON Dungeon TALK Talk
これをResources.Loadメソッドで読み込むために”Assets/Resources”下に配置します。
※今回は”Assets/Resources/Text”に置いてみました。
2. 文字列アクセス用のクラスを作る
次にスクリプト上で使えるクラスを作ります。
TextManagerという名前のCSファイルを”Assets”下のどこか適当なところに作り、以下のソースコードをコピペしてください。(ダブルクリックで全選択できます)
using UnityEngine; using System; using System.Collections.Generic; using System.IO; /// <summary> /// 文字列管理クラス(多言語対応) /// </summary> public static class TextManager { // 文字列格納、検索用ディクショナリー private static Dictionary<string, string> sDictionary = new Dictionary<string, string>(); /// <summary> /// 検索キー /// </summary> public enum KEY { RETURN, DUNGEON, TALK, }; /// <summary> /// 使用言語 /// </summary> public enum LANGUAGE { JAPANESE, ENGLISH, } /// <summary> /// 文字列初期化 /// </summary> /// <param name="lang">使用言語</param> public static void Init(LANGUAGE lang) { // リソースファイルパス決定 string filePath; if (lang == LANGUAGE.JAPANESE) { filePath = "Text/japanese"; } else if (lang == LANGUAGE.ENGLISH) { filePath = "Text/english"; } else { throw new Exception("TextManager Init failed."); } // ディクショナリー初期化 sDictionary.Clear(); TextAsset csv = Resources.Load<TextAsset>(filePath); StringReader reader = new StringReader(csv.text); while (reader.Peek() > -1) { string[] values = reader.ReadLine().Split('\t'); sDictionary.Add(values[0], values[1].Replace("\\n", "\n")); } } /// <summary> /// 文字列取得 /// </summary> /// <param name="key">文字列取得キー</param> /// <returns>キーに該当する文字列</returns> public static string Get(KEY key) { return Get(Enum.GetName(typeof(KEY), key)); } /// <summary> /// 文字列取得 /// </summary> /// <param name="key">文字列取得キー</param> /// <returns>キーに該当する文字列</returns> public static string Get(string key) { return sDictionary[key]; } }
なお、クラス内でApplication.systemLanguageを使っていないのはそうした方が端末の設定に依存せずに使えるからです。(依存したければInitを呼び出す側でそうすれば良い。)
あと、冒頭でも書きましたがInspectorからの使用とかは今回一切考慮していません。
シーンAではInspector上で定義、シーンBでは文字列定義ファイルから取得・・・みたいにとっ散らかるのが嫌なので、文字列は全て定義ファイル上のものを使う想定で組みました。
※それにしても、Java、C、C++、C#とやってると変数やメソッドの命名規則がよく分からなくなってきますね( ゚д゚)
3. 出来上がったものを使う
使い方は簡単。
まずアプリ起動直後などでInitメソッドを1度だけ実行します。
// 言語指定は端末設定に依存しても良いし、アプリ設定に依存しても良い TextManager.Init(TextManager.LANGUAGE.JAPANESE);
その後はGetメソッドを使って文字列を取得、Textコンポーネントにセットしていくだけです。
// buttonはInspector上でアタッチ、もしくは // Prefabをインスタンス化してあると思ってください Transform tf = button.transform; tf.FindChild("Text").gameObject.GetComponent<Text>().text = TextManager.Get(TextManager.KEY.RETURN);
Unityっぽさを求めるならInspectorにガンガン絡めていくべきなのかもしれませんが、GUI上で管理するとGUI上で編集しなきゃいけないので面倒臭いんですよね。(私が方法を知らないだけかもしれませんが・・・)
それに将来的に5ヶ国語とか10ヶ国語とか対応するってなった時に、やっぱりGUIだと拡張しづらいと思うんですよ。
その点、テキストファイルなら編集も拡張も自由自在!やったぜ!ヽ(゚∀゚)ノ パッ☆
トラブルシューティングのヒント
ちなみにですがびっくりするほど何も考えずに作ったので、現時点では性能面だとかそういったものを一切考慮してません。
問題になるとしたら、文字列定義ファイルを読み込んでDictionaryに落とし込む時の「速度」か、アプリを起動し続ける上での「メモリの圧迫」だと思います。
小規模なアプリなら大丈夫だとは思うんですが、一応その辺りのトラブルが起きた時のためにヒントになりそうな手法を記載しておきます。
- 言語だけでなく使用シーンごとに文字列定義ファイルを分けてみる。
→例えば「word_japanese.csv」「serif_japanese.csv」みたいな感じ。もしくは「scene1_japanese.csv」「scene2_japanese.csv」など? - 常に全てをメモリに読み込んでおくのではなく、必要な時に必要な分だけ読み込み、不必要になったら解放する。
→Resources.Loadの回数が増えるがメモリの圧迫は防げる。(しかし単なる文字列データにそこまで神経質にならなくても良いかも?)
要は、大きすぎるなら小分けにしようってだけです。
※小分けの方法や方針については開発者の好みだと思うのでここでは書きません。
他に良い感じだと思ったUnityの多言語対応の方法
自分は今回採用しなかったけれど、なるほどと思ったUnityの多言語対応の方法が書いてある記事を紹介しておきます。
前者はプログラマーなら大体の人が思いつく方法、後者はまさにUnityって感じの方法だと思いました。
他に良い方法があればぜひ教えていただけると嬉しいです。
よろしくお願いしますm(_ _)m
最近のコメント