Steam版 The Room Two 日本語化(自動翻訳化):詳細

こちらは詳細内容の記事です。必要ファイルのダウンロードおよび手順を知りたい方は下記記事をご覧ください。

Steam版 The Room Two 日本語化(自動翻訳化)において、XUnity.AutoTranslatorの導入まではスムーズに進みましたが日本語表示を行うためには時間がかかりました。
日本語表示を行うために調べてわかったことをメモします。

The Room TwoはUnity 5.3.4f1で制作されている

The Room Twoのリリース日は2016年7月5日となっています。そのためか使用されているUnityは結構古めのバージョンです。XUnity.AutoTranslatorで日本語表示できない…と調べるとTextMeshProに関することが良く見つかったので色々と試してみたのですが、このバージョンでは無関係(サポートされる前)だったぽいです。

The Room Twoは文字表示にNGUIを使用している

XUnity.AutoTranslatorが対応しているText FrameworksにはUGUI、NGUI、TextMeshProなどがあるようです。TextMeshProはUnityが古いのであり得ないとして、The Room Twoはいったいどのフレームワークを使用しているのか?調査にはdnSpyを用いました。

dnSpyの"Edit Method (C#)..."でXUnity.AutoTranslator.Plugin.Core.dllへデバック出力を追加してテキストを取得/設定しているオブジェクトのクラス名を表示させてみると、ゲーム起動直後のメインメニューではすべてUILabelでした。 また、UILabelクラスにはfontというUIFontクラスのプロパティがあるということもわかりました。そして、UILabel,UIFontというクラス名からThe Room TwoはNGUIというフレームワークを使用していることがわかりました。

UIFont.dynamicFontを設定すればダイナミックフォントへ変更可能(日本語表示可能)

NGUIで日本語表示をする方法を探してもUnity Editorでの操作手順やビットマップフォントの情報ばかりでした。頑張ってThe Room Twoへ日本語フォントを追加するしか方法がないのかなぁ…とか思いながらdnSpyでUIFontクラスのコードを眺めているとdynamicFontというFontクラスのプロパティがあるのを発見しました。
ダメもとで下記のようなコードを追加してみたところ日本語が表示されました。


UILabel label = (UILabel)ui;
var fonts = Resources.FindObjectsOfTypeAll();    // The Room Twoの場合、見つかるのは"Arial"一つだけ
label.font.dynamicFont = fonts[0];

しかし、dynamicFontにnullを入れても元のフォントには戻ってくれませんでした。(textに英語を入れても何も表示されない)
下記のようにInstantiateでUIFontをコピーして使用してもダメでした。


// 日本語用フォントへ設定
UILabel label = (UILabel)ui;
UIFont font2 = null;
foreach ( var f in Resources.FindObjectsOfTypeAll<UIFont>() )
{
    if ((label.font.name + "2") == f.name) {
        font2 = f;
        break;
    }
}
if (font2 == null) {
    // 元々使用していたフォント名+"2"という名前でフォントを作成
    var fonts = Resources.FindObjectsOfTypeAll<Font>();
    font2 = Instantiate(label.font);
    font2.name = label.font.name + "2";
    font2.dynamicFont = fonts[0];
}
label.font = font2;

…省略…

// 元のフォントへ戻す
UILabel label = (UILabel)ui;
if (label.font.name.Substring(label.font.name.Length-1, 1) == "2") {
    var name = label.font.name.Substring(0,label.font.name.Length-1);
    foreach ( var f in Resources.FindObjectsOfTypeAll<UIFont>() )
    {
        if (name == f.name) {
            label.font = f;     // コレで元のUIFontに戻しているが表示は空欄(表示なし)になってしまう。
            break;
        }
    }
}
UIFont.dynamicFontを設定する前にUIFont.materialをnullにしておく必要があった

日本語が表示出来ても原文に戻せないと使いづらい。正確にはフォントが戻らないだけで文章は原文へ戻せるんだけど…なんかイヤだ…というわけで、引き続きdnSpyで調査しました。 思いついたことを片っ端から試してみたところ、下記のようにmaterialをnullにしてからdynamicFontを設定すれば元に戻せることがわかりました。


// 日本語用フォントへ設定
UILabel label = (UILabel)ui;
UIFont font2 = null;
foreach ( var f in Resources.FindObjectsOfTypeAll<UIFont>() )
{
    if ((label.font.name + "2") == f.name) {
        font2 = f;
        break;
    }
}
if (font2 == null) {
    // 元々使用していたフォント名+"2"という名前でフォントを作成
    var fonts = Resources.FindObjectsOfTypeAll<Font>();
    font2 = Instantiate(label.font);
    font2.name = label.font.name + "2";
    font2.material = null;		// コレが必要だった!
    font2.dynamicFont = fonts[0];
}
label.font = font2;

…省略…

// 元のフォントへ戻す
UILabel label = (UILabel)ui;
if (label.font.name.Substring(label.font.name.Length-1, 1) == "2") {
    var name = label.font.name.Substring(0,label.font.name.Length-1);
    foreach ( var f in Resources.FindObjectsOfTypeAll<UIFont>() )
    {
        if (name == f.name) {
            label.font = f;
            break;
        }
    }
}

UIFontをInstantiateでコピーしてもmaterialは同じものを参照している。dynamicFontを設定したことで、そちらのUIFontのmaterialへ何らかの変更が入り、元のUIFontにも影響が出てしまった…という事だと思います。

XUnity.AutoTranslator.Plugin.Core.AutoTranslationPlugin.SetText()を変更して完成

UIFontには一時的にフォントを置き換えるためのreplacementというプロパティが用意されていました。せっかくなのでそれを利用し下記コードとなりました。 追加箇所はXUnity.AutoTranslator.Plugin.Core.AutoTranslationPlugin.SetText()のui.SetText()直前としました。

BepInEx\plugins\XUnity.AutoTranslator\XUnity.AutoTranslator.Plugin.Core.dll
XUnity.AutoTranslator.Plugin.Core.AutoTranslationPlugin.SetText

namespace XUnity.AutoTranslator.Plugin.Core
{
                    …省略…
   public class AutoTranslationPlugin :
                    …省略…
   {
                    …省略…
      private void SetText( object ui, string text, bool isTranslated, string originalText, TextTranslationInfo info )
      {
                    …省略…
#ifdef _THE_ROOM_TWO_PATCH_
                if (ui.GetType() == typeof(UILabel)) {
                    UILabel label = (UILabel)ui;
                    if (isTranslated) {
                        if (label.font.replacement == null) {
                            UIFont font2 = null;
                            foreach ( var f in Resources.FindObjectsOfTypeAll<UIFont>() )
                            {
                                if ((label.font.name + "2") == f.name) {
                                    font2 = f;
                                    break;
                                }
                            }
                            if (font2 == null)
                            {
                                var fonts = Resources.FindObjectsOfTypeAll<Font>();
                                font2 = Instantiate(label.font);
                                font2.name = label.font.name + "2";
                                font2.material = null;
                                font2.dynamicFont = fonts[0];   // Arial
                                XuaLogger.AutoTranslator.Info(string.Format("made  UIFont {0}({1})", font2.name, font2.GetInstanceID() ));
                            }
                            else
                            {
                                XuaLogger.AutoTranslator.Info(string.Format("found UIFont {0}({1})", font2.name, font2.GetInstanceID() ));
                            }
                            label.font.replacement = font2;
                        }
                    }
                    else
                    {
                        label.font.replacement = null;
                    }
                }
#endif
                ui.SetText( text, info );
                    …省略…
      }
                    …省略…
   }
}

なお、dnSpyの"Edit Method (C#)..."でXUnity.AutoTranslator.Plugin.Core.AutoTranslationPlugin.SetText()を編集すると下記が原因でCompileエラーとなる場合があります。

読み込んでいるdllが足りない

下記dllの読込は必須です。エラーリスト下にある"Add Assembly Reference (Ctrl+O)"ボタンから足りないdllを追加します。


    BepInEx\plugins\XUnity.AutoTranslator\XUnity.AutoTranslator.Plugin.Core.dll (編集対象)
    BepInEx\core\XUnity.Common.dll
    TheRoomTwo_Data\Managed\Assembly-CSharp.dll
    TheRoomTwo_Data\Managed\UnityEngine.dll

読み込んでいるdllが重複している

dnSpyは足りないdllを自動的に読み込んでくれるようなのですが、System.Core.dllなどが重複してしまいエラーとなる場合があります。その場合はゲーム起動時に読み込まれるであろう方(TheRoomTwo_Data\Managed内のdll)を残し他方を削除します。