VCI(Virtual Cast Interactive)でUI表示に言語パックを実装してみる


# はじめに
このVCIのスクリプトはバーチャルキャスト上で複数言語対応を見据えた実装を目指して行きます。

今回は実装を抜粋しVCI連携用の同期リクエスト周りのボタンを例に書いていきます。
# 題材
プラネタリウム用のUserコンソールVCI

![2023120501012060.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679847/5b2d154b-0ce6-43bc-33e1-09139f03a029.png)

このVCIの言語切替機能を使用します。

# 基本設計の案だし

- 対象の文字を数値にし言語パックJson形式で構成し外注などをしやすい物にまとめる

|メリット| デメリット |
|:---|:-----------------------------------------
|言語対応を他人に依頼しやすく複数人での実装に向いている| Luaファイルが増えるとバーチャルキャストで<br>読み込む速度が、落ちてしまう特性上複数ファイルは好ましくない|


 - csv形式でシリアライズしJson同様に行う

|メリット| デメリット |
|:---|:-----------------------------------------
|言語対応を他人に依頼しやすく複数人での実装に向いている| Json同様にLuaファイルが増えると<br>バーチャルキャストで読み込む速度が、<br>落ちてしまう特性上複数ファイルは好ましくない|

- Table形にキーを割り当てて各言語実装していく

|メリット| デメリット |
|:---|:-----------------------------------------
|今まで道理に気軽にTableに書くことができる| 他人に言語対応の依頼がしにくくなる|


# 実装方針
- 一人で実装をしていくためTable形にキーを割り当てて各言語実装していくことにする。
下記で言語の実装済みの言語選択用の初期化を行っている
```lua:UserConsoleModule.lua
----------------------------------------
--言語設定
----------------------------------------
UserConsole.Country = {}
UserConsole.Country = function()
--言語設定

    return{

        jp = "jp",
        en = "en",
    
    }

end--END_言語設定
```
# 言語パック用管理Table宣言関数
Tableのキーと役割
- `Name`:TextMeshProオブジェクトの名前
- `jp`:日本語
- `en`:英語
- `colorNo`:色ステータスナンバー(1:赤, 2:緑, 3オレンジ)

下記のコードをプログラムスタート全員が時に行う
```lua:UserConsoleModule.lua
----------------------------------------
--テキスト設定
----------------------------------------
UserConsole.TextBox = {}
----------------------------------------
--同期リクエスト
----------------------------------------
UserConsole.TextBox.Sync = function()
--同期リクエスト

    local TextBox = {}
    TextBox[1] = {Name = "DisConnect_Text", jp = "切断", en = "DisConnect", colorNo = 1}
    TextBox[2] = {Name = "Connect_Text", jp = "接続", en = "Connect", colorNo = 1}
    TextBox[3] = {Name = "Send_Text", jp = "送信", en = "Send", colorNo = 1}

    return {
        
        ListAll = TextBox, 
        DisConnect = TextBox[1],
        Connect = TextBox[2],
        Send = TextBox[3],

    }

end--END_同期リクエスト


```

# プログラムの全体

- 考え方
1.Moduleに言語関係の物を集めてグローバル変数もModule内に
保存し使っていくため「main.lua」で1度だけ初期化でグローバル変数にします。
2.グローバル変数に保存したtableに対してセレクト表示を行います。

※接触判定やスイッチ動作用も「セレクト色変更」を共通化し使用しているため、
ボタンの接触時にも言語選択が必須になっています。

<font color="Red">Point：一部言語パックに不十分なものが有り完全な状態でない場合に用意Tableに存在しない言語選択してしまった場合はデフォルトで設定を行う。</font> 


- 言語初期化までのシーケンス図を書いていきます。

![シーケンス図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679847/f01c22bb-2797-52d2-448b-b27a23ccd249.png)


- 初期化し表示するとこまでの「main.lua」と「UserConsoleModule.lua」を書いていきます
```lua:main.lua
----------------------------------------
--定義
----------------------------------------
local UserConsoleModule = require("UserConsoleModule")
local MyCountry = UserConsoleModule.Country()

--初期化待ち用
local ini_Flag = false
----------------------------------------
-- 同期変数
----------------------------------------
local UserConsole = {}
UserConsole.Storage = {}
--言語設定
UserConsole.Country = MyCountry.jp

----------------------------------------
--言語初期化
----------------------------------------
function Country_ini()
--言語初期化

    UserConsoleModule.TextCountry_ini(UserConsole.Country)

end--END_言語初期化

----------------------------------------
--表示の初期化
----------------------------------------
function Reset_ini()
--初期化

    --言語初期化
    Country_ini()

    --初期化完了
    ini_Flag = true

end --END_値の初期化

----------------------------------------
--実行シーケンス
----------------------------------------
--初期化
Reset_ini()

```


- 言語制御用の「UserConsoleModule.Lua」を下記に書きます
```lua:UserConsoleModule.lua
UserConsole = {}

----------------------------------------
--保存用
----------------------------------------
UserConsole.Storage = {}

----------------------------------------
--言語設定
----------------------------------------
UserConsole.Country = {}
UserConsole.Country = function()
--言語設定

    return{

        jp = "jp",
        en = "en",
    
    }

end--END_言語設定

----------------------------------------
--テキスト設定
----------------------------------------
UserConsole.TextBox = {}
----------------------------------------
--文字色
----------------------------------------
UserConsole.TextBox.lampColor = function()
--文字色

    --赤
    local RedColor = "<color=#900000>"
    --緑
    local GreenColor = "<color=#009000>"
    --オレンジ
    local OrangeColor = "<color=#FF3300>"

    local TextColor = {}
    TextColor[1] = RedColor
    TextColor[2] = GreenColor
    TextColor[3] = OrangeColor

    return{

        ListAll = TextColor,
        Red = RedColor,
        Green = GreenColor,
        Orange = OrangeColor
    
    }

end--END_文字色

----------------------------------------
--同期リクエスト
----------------------------------------
UserConsole.TextBox.Sync = function()
--同期リクエスト

    local TextBox = {}
    TextBox[1] = {Name = "DisConnect_Text", jp = "切断", en = "DisConnect", colorNo = 1}
    TextBox[2] = {Name = "Connect_Text", jp = "接続", en = "Connect", colorNo = 1}
    TextBox[3] = {Name = "Send_Text", jp = "送信", en = "Send", colorNo = 1}

    return {
        
        ListAll = TextBox, 
        DisConnect = TextBox[1],
        Connect = TextBox[2],
        Send = TextBox[3],

    }

end--END_同期リクエスト

----------------------------------------
--Storage確認
----------------------------------------
UserConsole.CheckStorage = function()
--Storage確認

    --同期リクエスト
    if UserConsole.Storage.TextSync == nil then
        UserConsole.Storage.TextSync = UserConsole.TextBox.Sync()
    end

end--END_Storage確認

----------------------------------------
--言語初期化
----------------------------------------
UserConsole.TextCountry_ini = function(Country)
--言語初期化

    --Storage確認
    UserConsole.CheckStorage()
    
    --カラーセット用
    local ChangeColor = UserConsole.Storage.lampColor.Red

    for i in ipairs(UserConsole.Storage.TextSync.ListAll) do
    --Sync
        UserConsole.TextChangeColor(Country, UserConsole.Storage.TextSync.ListAll[i],ChangeColor)
    end--END_Sync

end--END_言語初期化


----------------------------------------
--セレクト色変更
----------------------------------------
UserConsole.TextChangeColor = function(Country, TextBox, lampColor)
--セレクト色変更

    if (Country == nil) or (TextBox[Country] == nil) then
        vci.assets._ALL_SetText(TextBox.Name, lampColor .. TextBox.jp .. "</color>")
    else
        vci.assets._ALL_SetText(TextBox.Name, lampColor .. TextBox[Country] .. "</color>")
    end

    vci.assets.HapticPulseOnGrabbingController("Pen2", 1, 0.1)

end--END_セレクト色変更
return UserConsole
```

- 言語用Luaの一覧
上記の関数と役割を表にしてみます

|   関数            |       役割                                | 引数       |        戻り値       |
|:------------------|:-----------------------------------------|:----------|:-------------------- |
| UserConsole.Country()| 設定可能な言語パックを定義 | 無し | 対応言語の頭文字　|
| UserConsole.TextBox.lampColor() | 文字色制御用のTable定義  | 無し | 全Table一覧、各色 　|
| UserConsole.TextBox.Sync() | 同期リクエストに使うTable定義  | 無し | 全Table一覧、各ボタンに対するTable 　|
| UserConsole.CheckStorage() | Tableの定義が無ければ関数をTable化  | 無し | Table言語パックのTable化 　|
| UserConsole.TextCountry_ini(Country) | 言語初期化時に呼ぶ関数  | Country：言語指定の頭文字 | 無し|
| UserConsole.TextChangeColor(Country, TextBox, lampColor) | セレクト色変更  | Country：言語指定の頭文字<br>TextBox：ボタンに対するTable<br>lampColor:文字色のカラーコード| 無し|

# 実際の表示のされ方
- 日本語表示
![2023120501015039.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679847/87b70612-34da-e7fb-f08b-181d53260b0f.png)


- 英語表示
![2023120501020765.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679847/3d44147d-2cce-0f61-f55a-ff95a950c1a7.png)


# まとめ
バーチャルキャストでは、基本的に日本人で一部が国外の人が利用しています。
基本的に日本の設定でバーチャルキャストを遊んでいる人が多いため、
言語パックの実装はそんなに意味をなしてないと思いますが、言語パックの対応事態は別プラットフォームや色んな所に応用が効きます。

今回は「main.lua」を極力文字やボタン制御で埋め尽くして可読性を損なわないように、
極力シンプルで「Module」に集めた実装になっています。

言語対応を考えた時に参考になれば幸いです。