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()直前としました。
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)を残し他方を削除します。