📗

【Unity】AddressablesでJson読み込み

2024/08/30に公開

はじめに

初めての方も、そうでない方もこんにちは!
現役ゲームプログラマーのたむぼーです。
自己紹介を載せているので、気になる方は見ていただければ嬉しいです!

今回は
 Addressablesを使ったJsonの読み込み
について紹介します

https://zenn.dev/tmb/articles/1072f8ea010299

準備

・Addressables

https://zenn.dev/tmb/articles/3ccdfb9eeef755

・Newtonsoft.Json

Windows > PackageManager > +ボタン > Add Package from git URLで
下記のURL追加してください

com.unity.nuget.newtonsoft-json

スクリプト

Jsonマスタクラス
JsonMaster.cs
using Cysharp.Threading.Tasks;

namespace Utils
{
    abstract public class JsonMaster<T> : JsonLoader<T> where T : class, new()
    {
        protected T _container;

        private bool _isCompleteLoading = false;

        /// <summary>
        /// マスタの読み込み
        /// </summary>
        protected async UniTask Load(string address)
        {
            if (_isCompleteLoading)
            {
                return;
            }
            _container = await LoadFromJson(address);
            _isCompleteLoading = true;
        }

        /// <summary>
        /// マスタの読み込み
        /// </summary>
        protected async UniTask Load(string path, string name)
        {
            if (_isCompleteLoading)
            {
                return;
            }
            _container = await LoadFromJson(path, name);
            _isCompleteLoading = true;
        }

        /// <summary>
        /// マスタの読み込み
        /// </summary>
        protected async UniTask Load(params string[] paths)
        {
            if (_isCompleteLoading)
            {
                return;
            }
            _container = await LoadFromJson(paths);
            _isCompleteLoading = true;
        }
    }
}
Json読み込みクラス
JsonLoader.cs
using UnityEngine;
using System.IO;
using Newtonsoft.Json;
using Cysharp.Threading.Tasks;

namespace Utils
{
    abstract public class JsonLoader<T> where T : class, new()
    {
        private const string Identifier = ".json";

        /// <summary>
        /// Json読み込み
        /// </summary>
        protected async UniTask<T> LoadFromJson(string address)
        {
            string jsonAddress = CheckIdentifier(address);
            TextAsset jsonText = await AddressableManager.Instance.LoadAssetAsync<TextAsset>(jsonAddress);
            return JsonConvert.DeserializeObject<T>(jsonText.text);
        }

        /// <summary>
        /// Json読み込み
        /// </summary>
        protected async UniTask<T> LoadFromJson(string path, string name)
        {
            string address = AddressableManager.Instance.CreateAddress(path, name);
            return await LoadFromJson(address);
        }

        /// <summary>
        /// Json読み込み
        /// </summary>
        protected async UniTask<T> LoadFromJson(params string[] paths)
        {
            string address = AddressableManager.Instance.CreateAddress(paths);
            return await LoadFromJson(address);
        }

        /// <summary>
        /// 識別子チェック
        /// </summary>
        private string CheckIdentifier(string target)
        {
            string jsonPath = target;
            if (jsonPath.EndsWith(Identifier))
            {
                return jsonPath;
            }
            return string.Concat(jsonPath, Identifier);
        }
    }
}

使い方

json読み込みには、ContainerクラスとMasterクラスの作成が必要になります

jsonの形式
JsonLoader.cs
{
  "Text": [ ---------------------Containerクラス
    {  ---------------------Entryクラス1
      "Id": 100001,
      "Jp": "テスト1!",
      "En": "Test1!",
      "Zh": "測試1!"
    }, ---------------------Entryクラス1
    {  ---------------------Entryクラス2
      "Id": 100002,
      "Jp": "テスト2!",
      "En": "Test2!",
      "Zh": "測試2!"
    }, ---------------------Entryクラス2
    {  ---------------------Entryクラス3
      "Id": 100003,
      "Jp": "テスト3!",
      "En": "Test3!",
      "Zh": "測試3!"
    }, ---------------------Entryクラス3
  ] ---------------------Containerクラス
}
Containerクラス

jsonの内容を格納するコンテナ
基本的には、EntryクラスとContainerクラスに分かれる

using System;
using System.Collections.Generic;

namespace Master
{
    [Serializable]
    sealed public class TextEntry
    {
        public int Id;
        public string Jp;
        public string En;
        public string Zh;
    }

    [Serializable]
    sealed public class TextContainer
    {
        public List<TextEntry> Text;
    }
}
Masterクラス

JsonMaster<Containerクラス>を継承したMasterクラスを作成

using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using Utils;

namespace Master
{
    abstract public class JsonNameTextMaster : JsonMaster<TextContainer>
    {
        /// <summary> アドレス </summary>
        private const string Address = "path/to/jsonname.json";

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public SkitScriptTextMaster()
        {
            // 処理なし
        }

        /// <summary>
        /// マスタ読み込み
        /// </summary>
        public async UniTask LoadMasterAsync()
        {
            await Load(Address);
        }

        /// <summary>
        /// 指定された言語に基づいてテキストを取得
        /// </summary>
        public Dictionary<int, string> GetText(Language language)
        {
            if (_container?.Text == null)
            {
                return new Dictionary<int, string>();
            }
            return _container.Text.ToDictionary(
                entry => entry.Id,
                entry => language switch
                {
                    Language.Jp => entry.Jp,
                    Language.En => entry.En,
                    Language.Zh => entry.Zh,
                    _ => string.Empty
                }
            );
        }
    }
}
// 必要な箇所でJsonNameTextMasterを読み込む
private async void LoadMaster()
{
    JsonNameTextMaster master = new JsonNameTextMaster();
    await master.LoadMasterAsync();
    var data = master.GetText(Language.Jp);
}

おまけ

TextMasterなど基本的に同じ構造で使う場合は、一旦共通処理をまとめる基底クラスを作成して継承することができます。

- sealed public class JsonNameTextMaster : JsonMaster<TextContainer>
+ sealed public class JsonNameTextMaster : LocalizationTextBase
+ abstract public class LocalizationTextBase : JsonMaster<TextContainer>

・LocalizationTextBase

LocalizationTextBase
using System.Collections.Generic;
using System.Linq;
using Utils;

namespace Master
{
    abstract public class LocalizationTextBase : JsonMaster<TextContainer>
    {
        /// <summary>
        /// すべてのテキストエントリを取得
        /// </summary>
        public List<TextEntry> GetAllTextEntries()
        {
            return _container?.Text ?? new List<TextEntry>();
        }

        /// <summary>
        /// 指定された言語に基づいてテキストを取得
        /// </summary>
        public Dictionary<int, string> GetText(Language language)
        {
            if (_container?.Text == null)
            {
                return new Dictionary<int, string>();
            }
            return _container.Text.ToDictionary(
                entry => entry.Id,
                entry => language switch
                {
                    Language.Jp => entry.Jp,
                    Language.En => entry.En,
                    Language.Zh => entry.Zh,
                    _ => string.Empty
                }
            );
        }

        /// <summary>
        /// 指定されたIDと言語に基づいてテキストを取得
        /// </summary>
        public string GetText(int id, Language language)
        {
            TextEntry entry = _container?.Text?.FirstOrDefault(e => e.Id == id);
            if (entry != null)
            {
                return language switch
                {
                    Language.Jp => entry.Jp,
                    Language.En => entry.En,
                    Language.Zh => entry.Zh,
                    _ => string.Empty
                };
            }
            return string.Empty;
        }
    }

    public enum Language
    {
        None,
        Jp,
        En,
        Zh,
    }
}
LocalizationTextBaseを継承したJsonNameTextMasterの例
using Cysharp.Threading.Tasks;

namespace Master
{
    sealed public class JsonNameTextMaster : LocalizationTextBase
    {
        /// <summary> アドレス </summary>
        private const string Address = "path/to/jsonname.json";

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public SkitScriptTextMaster()
        {
            // 処理なし
        }

        /// <summary>
        /// マスタ読み込み
        /// </summary>
        public async UniTask LoadMasterAsync()
        {
            await Load(Address);
        }
    }
}

Discussion