しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

Unity の Localization でスクリプトから多言語化表示テキストを切り替える

※ これは 2022/10/13 時点の Unity 2022.1.20f1 Localization v1.3.2 の情報です

最新版では動作が異なる可能性がありますのでご注意ください

前回、文字列とフォントが一緒に切り替えることができたが、あくまで静的な多言語化

今回はスクリプトの中で表示リソースを切り替えてみたい

ボタンが押されたらカフェに

例によってこの「モカ」ボタンが押されたら「カフェ」に表示を切り替えてみたい

まず Localization の Recommended バージョンだと機能が古いので v.1.3.2 にアップデートする

Unity の Package Manager を開き、左上の「+」ボタンから 「Add package by name」を選択し、com.unity.localization のバージョン 1.3.2 を指定して Add ボタン押下

バージョン指定でパッケージインポート

これでバージョンアップ完了

次に String Table に Cafe の文言を追加

Cafe を追加

あとは Test.cs を下記のように変更し、ボタンが押されたら多言語化リソースを切り替えるようにする

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Localization;
using UnityEngine.Localization.Components;
using UnityEngine.Localization.Settings;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.UI;

public class Test : MonoBehaviour
{
    [SerializeField]
    private Image cafeImage = null;

    [SerializeField]
    private Image mochaImage = null;

    [SerializeField]
    private LocalizeStringEvent labelEvent = null;

    private Image thumbnailImage = null;

    private AsyncOperationHandle<Sprite> handle = default;

    private const string RemoteLoadPath = @"http://localhost/StandaloneWindows64/{0}/";
    private static string OriginalRootPath = null;
    private static string CurrentRootPath = null;

    private AssetBundle currentBundle = null;
    private string currentName = null;
    private Hash128 currentHash = default;

    public async void Start()
    {
        // Addresables 初期化
        var locator = await Addressables.InitializeAsync().Task;

        // 初期化時のルートパス取得
        var info = Addressables.GetLocatorInfo(locator.LocatorId);
        OriginalRootPath = info.HashLocation.InternalId.Replace(Path.GetFileName(info.HashLocation.InternalId), "");

        // アドレス書き換えを有効化
        Addressables.InternalIdTransformFunc = MylIdTransform;

        // 日本語指定
        await LocalizationSettings.InitializationOperation.Task;
        LocalizationSettings.SelectedLocale = Locale.CreateLocale("ja");
        
        await SetCatAsset("Cafe");
    }

    private static string MylIdTransform(IResourceLocation location)
    {
        // ルートパスを置き換える
        if (location.InternalId.StartsWith(OriginalRootPath))
        {
            return location.InternalId.Replace(OriginalRootPath, CurrentRootPath);
        }
        return location.InternalId;
    }

    private async Task SetCatAsset(string catName)
    {
        Cat.Name = catName;

        this.thumbnailImage = catName switch
        {
            "Cafe" => this.cafeImage,
            "Mocha" => this.mochaImage,
            _ => null
        };

        // ルートパスの再評価
        CurrentRootPath = string.Format(RemoteLoadPath, Cat.Name);

        // リモートカタログに変更があれば更新する
        var catalogs = await Addressables.CheckForCatalogUpdates(true).Task;
        if (catalogs != null && catalogs.Count > 0)
        {
            var locators = await Addressables.UpdateCatalogs(catalogs, true).Task;

            // リモートアセットの取得先を確認
            if (locators[0].Locate("Assets/Sprites/cat.png", typeof(Sprite), out var locations))
            {
                // リモートアセットの URL は Dependencies に入っている
                foreach(var location in locations[0].Dependencies)
                {
                    UnityEngine.Debug.Log($"--------------> uri={Addressables.InternalIdTransformFunc(location)}");

                    if (location.Data is AssetBundleRequestOptions sizeData)
                    {
#if UNITY_EDITOR
                        EditorUtility.DisplayDialog($"{location.PrimaryKey}", $"{sizeData.BundleSize:#,0} bytes", "OK");
#endif
                    }
                }

                var option =  locations
                        .SelectMany(x => x.Dependencies)
                        .Select(x => x.Data)
                        .OfType<AssetBundleRequestOptions>()
                        .First();
                var newName = option.BundleName;
                var newHash = Hash128.Parse(option.Hash);

                if (this.currentBundle != null)
                {
                    UnityEngine.Debug.Log($"--------------> {this.currentName}{this.currentHash}】 -> {newName}{newHash}】");

                    // 同じ名前で内容(Hash)が異なる AssetBundle がないか調べる
                    if (this.currentName.Equals(newName) && this.currentHash != newHash)
                    {
                        // アセットのインスタンスは残したまま、古い AssetBundle を解放してキャッシュからも破棄する
                        this.currentBundle.Unload(false);
                        if (Caching.IsVersionCached(newName, this.currentHash))
                        {
                            Caching.ClearCachedVersion(newName, this.currentHash);
                        }
                    }
                }

                // BundleName と Hash を保持しておく
                this.currentName = newName;
                this.currentHash = newHash;
            }
        }

        AsyncOperationHandle download = default;
        try
        {
            // AssetBundle を抽出するために Load 前に Download
            download = Addressables.DownloadDependenciesAsync("Assets/Sprites/cat.png");
            var resources = await download.Task as List<IAssetBundleResource>;
            if (resources.Count < 1)
            {
                return;
            }
            this.currentBundle = resources[0].GetAssetBundle();

            // Addressables 経由で Sprite を読み込んで表示
            this.handle = Addressables.LoadAssetAsync<Sprite>("Assets/Sprites/cat.png");
            this.thumbnailImage.sprite = await this.handle.Task;
        }
        finally
        {
            // Load が終わったら Download ハンドルは用済みなので解放
            if (download.IsValid())
            {
                Addressables.Release(download);
            }
        }
    }

    /// <summary>
    /// Mocha ボタン押下イベントハンドラ
    /// </summary>
    public async void OnMochaButtonClicked()
    {
        await SetCatAsset("Mocha");

        // Cafe にラベル変更
        this.labelEvent.SetTable("StringTable");
        this.labelEvent.SetEntry("Cafe");
    }

    public void OnDestroy()
    {
        // いらなくなったら handle を Release する
        if (this.handle.IsValid())
        {
            Addressables.Release(this.handle);
        }
    }
}

重要なのは下記

        // Cafe にラベル変更
        this.labelEvent.SetTable("StringTable");
        this.labelEvent.SetEntry("Cafe");

StringTable が変わらないので LocalizeStringEvent.SetEntry() だけでもいいけど一応 LocalizeStringEvent.SetTable() もして切り替える

最後に Test.cs の labelEvent に忘れずにボタンラベルの LocalizeStringEvent を差しておく

LocalizeStringEvent を差す

これでお試し

ラベル切り替え

ちゃんと日本語でラベルが切り替わった