Unity Companion License をゲームの権利表記に記載する必要はあるのか?

Unity でゲームを作ろうとすると、ほとんどの場合、Unity UI (uGUI) や TextMesh Pro といった Unity 公式のパッケージを何かしら使うことになります。

そしてそれらのパッケージの多くは、Unity Companion License というライセンスの下で配布されています。

こうしたパッケージを使用してゲームをリリースする際、そのライセンスをゲームの権利表記に記載しなければならないのでしょうか?

左は『レスレリアーナのアトリエ』の、右は『FIINAL FANTASY VII EVER CRISIS』の権利表記。「へぇー、このゲームは URP を使ってるんだ!」みたいなのが見て取れて面白くはあるのですが、そもそもこの表記は必要なのでしょうか?

条文を読んでみる

早速、条文を確認してみましょう。なお、この記事を執筆した時点(2023 年 10 月 7 日)の最新版は、v1.3 となっています。

unity.com

この中で、権利表記が必要そうな雰囲気が感じられる条文は 5. に書かれている文章です。以下に引用します。

Notices & Third-Party Rights. This License, including the copyright notice associated with the Work, must be provided in all substantial portions of the Work and derivative works thereof (or, if that is impracticable, in any other location where such notices are customarily placed). Further, if the Work is accompanied by a Unity “third-party notices” or similar file, you acknowledge and agree that software or content identified in that file is governed by those separate license terms.

「このライセンスは、本著作物およびその二次的著作物のすべての重要な部分において(あるいはそれができない場合、そのような表示が慣習的に置かれるその他の場所において)、提供されなければならない」と書いてあります。

なるほど確かに、ゲーム内とかゲーム内から飛べるリンク先のウェブページとかにライセンスの表記をしなければならないような気がしてきます。が、ここで重要なのは「本著作物およびその二次的著作物」という部分です。

日本語訳だと誤解が生じる可能性があるので、ここからは原文どおり Workderivative works で考えていきます。これらは一体何を指しているのでしょうか?

まず Work ですが、わざわざ大文字始まりで表記されていることからも分かるように、これは一般的な「work(作品)」という意味ではなく、この条文内で定義される独自の用語であることが見て取れます。そして、その定義がなされているのは序文です。以下に引用します。

Unity Technologies ApS (“Unity”) grants to you a worldwide, non-exclusive, no-charge, and royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, and distribute the work of authorship that accompanies this License (“Work”), subject to the following terms and conditions:

「the work of authorship that accompanies this License (“Work”)」という部分に着目してください。Work とは、「the work of authorship that accompanies this License」——「このライセンスに付随する著作物」であると明記されています。つまり、Unity UI や TextMesh Pro といった、このライセンス下で配布されているパッケージそのものが Work である、と解釈できます。

次に derivative works です。これは法律用語で「二次的著作物」を意味しますが、これに「Unity UI や TextMesh Pro を用いて作ったゲーム」も含まれるかどうかが焦点になりそうです。そして幸いなことに、この用語に関しても条文内で説明がなされています(丁寧!)。その条文である 3.1 を引用します。

You own your content. In this License, “derivative works” means derivatives of the Work itself--works derived only from the Work by you under this License (for example, modifying the code of the Work itself to improve its efficacy); “derivative works” of the Work do not include, for example, games, apps, or content that you create with the Work. You keep all right, title, and interest in your own content.

前半には「derivative works とは、 Work 自体から派生した(例えば Work の機能を向上させるために Work 自体のコードを修正したといった)著作物を意味する」と書かれています。仮に、TextMesh Pro を拡張した Super TextMesh Pro というパッケージを作った場合、Super TextMesh Pro は derivative works である、ということですね。

そしてさらに重要なのが後半部分です。「derivative works には、例えばあなたが Work を用いて作成したゲーム、アプリ、コンテンツは含まれない」と、はっきり明記されています! derivative works はあくまで Work 自体から派生した著作物のみを指し、Work を使って作った別のコンテンツは derivative works ではないということですね。

結論

ここまでの内容をまとめると、以下のようになります。

  • Workderivative works には、Unity Companion License のライセンス表記が必要である。
  • Work とは、Unity Companion License の下で配布されているコンテンツ自体(Unity UI や TextMesh Pro など)を指す。
  • derivative works とは、 Work 自体から派生した著作物のみを指し、 Work を用いて作成したゲームやアプリ、コンテンツは含まない。

これらから導き出される結論は、以下のとおりです。

  • Unity Companion License の下で配布されている著作物自体を改変したコンテンツを作成した場合、それは derivative works なので、ライセンス表記が必要。
  • Unity Companion License の下で配布されている著作物を用いて自分のゲームを作成した場合、それは derivative works ではないので、ライセンス表記は不要。

と言うわけで、Unity UI や TextMesh Pro を使ってゲームを作ったとしても、それを権利表記に記載する必要はない、ということが言えそうです。

最後に

この記事の正確性については注意深く書いたつもりですが、筆者は権利関係のプロではないため、もしかしたら不正確な記述があるかもしれません。もし問題のある箇所を見つけられましたら、コメントなどで教えてくださると幸いです!

また、これを読んでいる読者様が Unity Companion License の下で配布されているパッケージをゲームに使用する際は、ご自身でもライセンスの条文を確認することを強くおすすめします! 条文は今後変更される可能性がありますし、もしこの記事の解釈に不正確な箇所があったとしても、筆者は責任を負えませんので……! 🙇

Unity エディターでの Undo 操作の登録を便利するツールを公開しました

はじめに

Unity エディターにおいて、Undo 操作の登録を簡単に行うことができるようになるツールを公開しました! 🚀

github.com

Unity Undo Extensions の使いどころ

Unity での開発では、エディター拡張を書いてメニューにオリジナルの項目を追加したり、Inspector にボタンを置いたりして、特定の操作を効率的に行えるようにするということをよくやると思います。

このとき、これらの拡張から普通に GameObject.AddComponent のようなメソッドを実行すると、Undo 操作が登録されず、Ctrl + Z などで取り消すことができません。また、オブジェクトが Dirty としてマーク*1されることもないため、Ctrl + S などで保存をすることもできません。

Undo や保存ができるようにするには、 Undo.AddComponent のような専用のメソッドを実行する必要がありますが、これらは UnityEditor 名前空間内にあるため、ビルドするとエラーになってしまいます。

これらを加味すると、以下のようなコードにすることが考えられます。

// 自身に SpriteRenderer をアタッチする
// エディター上かつ非再生時のみ、Undo 版のメソッドを使用する

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

public class ExampleMonoBehaviour : MonoBehaviour
{
    public void ExampleButton()
    {
#if UNITY_EDITOR
        if (!EditorApplication.isPlaying)
        {
            Undo.AddComponent<SpriteRenderer>(gameObject);
        }
#endif

        gameObject.AddComponent<SpriteRenderer>();
    }
}

このようにすることで、ビルドエラーを回避しつつ Undo 登録ができるようになります……が、かなりコードが煩雑になってしまい、毎回これを書くのは結構しんどい。

そこで、Unity Undo Extensions の出番です。このパッケージを導入することで、以下のように書くことができるようになります。

using GigaCreation.Tools.UndoExtensions;
using UnityEngine;

public class ExampleMonoBehaviour : MonoBehaviour
{
    public void ExampleButton()
    {
        gameObject.AddComponentAsUndoable<SpriteRenderer>();
    }
}

AddComponentAsUndoable を使用することで、一行で書くことができるようになりました!

使い方

現在のバージョン (2.0.0) では、以下の 3 つの拡張メソッドが含まれています。

  • AddComponentAsUndoable
  • DestroyAsUndoable
  • DoActionAsUndoable

それぞれ、以下のように使用します。

using GigaCreation.Tools.UndoExtensions;
using UnityEngine;

public class ExampleMonoBehaviour : MonoBehaviour
{
    public int Counter;

    public void Example01()
    {
        // GameObject にコンポーネントをアタッチする
        // エディター上かつ非再生時のみ、Undo 操作の登録および Dirty マークを行う
        gameObject.AddComponentAsUndoable<SpriteRenderer>();
    }

    public void Example02()
    {
        if (TryGetComponent(out SpriteRenderer spriteRenderer))
        {
            // オブジェクトを破棄する
            // エディター上かつ非再生時のみ、Undo 操作の登録および Dirty マークを行う
            spriteRenderer.DestroyAsUndoable();
        }
    }

    public void Example03()
    {
        // 任意のアクションを実行する
        // エディター上かつ非再生時のみ、Undo 操作の登録および Dirty マークを行う
        this.DoActionAsUndoable("Increment Counter", x =>
        {
            x.Counter++;
        });
    }
}

DoActionAsUndoable の第 1 引数の文字列は、Undo History ウィンドウで表示されるタイトル名です。お好きな文字列で大丈夫です。

API リファレンス

英語にはなりますが、API リファレンスもございます

インストール方法

Package Manager を使用する方法

導入したい Unity プロジェクトで Package Manager を開き、「+」ボタンを押して「Add package from git URL...」を選択、以下の URL を入力して「Add」ボタンを押すとインストールできます。

https://github.com/gigacreation/UnityUndoExtensions.git?path=Assets/UndoExtensions

手動でインストールする方法

GitHub のリポジトリからソースコードをダウンロードし、その中の Assets/MissingFinder/ ディレクトリを導入したい Unity プロジェクトにコピーします。

*1:シーン名に「*」マークが付いている状態

Unity プロジェクト内の Missing を検索するツールを公開しました

はじめに

Unity のシーンやプレハブの中にある、Missing になってしまっている参照やコンポーネントを検索し、コンソールに表示するツールを公開しました! 🚀

github.com

使い方

このパッケージをインストールすると、メニューに Tools/GIGA CREATION/Missing Finder/ が追加され、以下のコマンドを実行できるようになります。

  • Find Missing in Current Scene
    • 現在開かれているシーンの中にある Missing を検索します。
  • Find Missing in Enabled Scenes
    • 有効なシーンの中にある Missing を検索します。有効なシーンとは、Build Settings ウィンドウの「Scenes In Build」に追加されているシーンのことを指します。
  • Find Missing in All Scenes
    • プロジェクト内のすべてのシーンから Missing を検索します。
  • Find Missing in Current Prefab Stage
    • 現在開かれているプレハブの中にある Missing を検索します(プレハブモードでのみ使用できます)。
  • Find Missing in All Prefab Assets
    • プロジェクト内のすべてのプレハブから Missing を検索します。

インストール方法

Package Manager を使用する方法

導入したい Unity プロジェクトで Package Manager を開き、「+」ボタンを押して「Add package from git URL...」を選択、以下の URL を入力して「Add」ボタンを押すとインストールできます。

https://github.com/gigacreation/UnityMissingFinder.git?path=Assets/MissingFinder

手動でインストールする方法

GitHub のリポジトリからソースコードをダウンロードし、その中の Assets/MissingFinder/ ディレクトリを導入したい Unity プロジェクトにコピーします。

CSV の特定の列を抽出して、リストや辞書を生成できるツールを公開しました

はじめに

CSV の特定の列をリストにしたり、特定の列をキー・値に持つ辞書を生成できる、Unity 向けのツールを公開しました! 🚀

github.com

CSV を辞書にして表示させたもの

使い方

CSV からリストを生成する

1. 抽出したい CSV を準備します。

var csv = @"
name,model_number,release_date
Family Computer,HVC-001,1983-07-15
SUPER Famicom,SHVC-001,1990-11-21
NINTENDO64,NUS-001,1996-06-23
"

上記のように CSV のテキストを直接コードに書いてもいいですし、 Resources.Load<TextAsset>(path).text などでファイルから取得しても大丈夫です。

2. リクエスト用のデータ CsvExtractRequest を作成します。

var request = new CsvExtractRequest(csv, true, "", 1);

3. 最後に、 CsvUtility.ExtractIntoList() でリクエストを投げれば、生成されたリストが取得できます。

List<string> list = CsvUtility.ExtractIntoList(request);

CSV から辞書を生成する

1. 抽出したい CSV を準備します(リストのときと同じ)。

2. リクエスト用のデータ CsvExtractRequest を作成します。 このとき、SetKeyColumnIndexes() で辞書のキーにする列を設定するのを忘れないでください!

var request = new CsvExtractRequest(csv, true, "", 1);
request.SetKeyColumnIndexes(0); // <- キーとして抽出する列のインデックスを設定

3. 最後に、 CsvUtility.ExtractIntoDictionary() でリクエストを投げれば、生成された辞書が取得できます。

Dictionary<string, string> dictionary = CsvUtility.ExtractIntoDictionary(request);

リクエストデータ CsvExtractRequest について

CsvExtractRequest は、以下のメンバーを持っています。

/// <summary>
/// CSV のテキスト。
/// </summary>
public string Csv { get; }

/// <summary>
/// true なら、CSV の 1 行目をヘッダーとして扱います。
/// </summary>
public bool HasHeader { get; }

/// <summary>
/// 複数の列が指定された際、この文字で列同士を繋げます。
/// </summary>
public string Separator { get; }

/// <summary>
/// 値として抽出する列のインデックス。
/// </summary>
public int[] ValueColumnIndexes { get; }

/// <summary>
/// キーとして抽出する列のインデックス。辞書を生成するときのみ使用します。
/// </summary>
public int[] KeyColumnIndexes { get; private set; }

public CsvExtractRequest(string csv, bool hasHeader = false, string separator = null, params int[] valueIndexes)
{
    Csv = csv;
    HasHeader = hasHeader;
    Separator = separator;
    ValueColumnIndexes = valueIndexes;
}

public void SetKeyColumnIndexes(params int[] indexes)
{
    KeyColumnIndexes = indexes;
}

インストール方法

Package Manager を使用する方法

導入したい Unity プロジェクトで Package Manager を開き、「+」ボタンを押して「Add package from git URL...」を選択、以下の URL を入力して「Add」ボタンを押すとインストールできます。

https://github.com/gigacreation/Csv2CollectionsForUnity.git?path=Assets/Csv2Collections

手動でインストールする方法

GitHub のリポジトリからソースコードをダウンロードし、その中の Assets/Csv2Collections/ ディレクトリを導入したい Unity プロジェクトにコピーします。

Unity 向けのサービスロケーターを公開しました

はじめに

このたび、Unity でサービスロケーターを使用できるようにするパッケージを公開いたしました! 🚀

github.com

今回はこのパッケージの紹介を、サービスロケーター自体の紹介も合わせつつ、させていただければと思います。

概要

【サービスロケーター】とは、

  • どこからでもアクセスできる格納場所(サービスコンテナ)を一つだけ用意し、
  • その格納場所に任意のクラス(サービス)を登録することで、
  • 他のクラスから格納場所に登録されたクラスへどこからでもアクセスできるようにする

というデザインパターンです。

例えば、ゲームデータのセーブ・ロードを司るクラスの SaveLoadService があったとして、それをあらかじめサービスコンテナに登録しておけば、他の様々なクラスからサービスコンテナを介し SaveLoadService へアクセスしてセーブやロードが行えるようになります。

これと似たようなことを実現できるものとして、【シングルトン】【DI (Dependency Injection)】パターンというのもありますが、

  • シングルトンだと SaveLoadService などの各サービスに他の各クラスが直接アクセスする形になり、サービスの数が増えていくと依存関係がスパゲッティ化してしまう
  • DI はサービスロケーターよりも依存関係を整理できるが、仕組みがやや難解で学習コストが高い

といった特徴があり、サービスロケーターはその中間的な立ち位置で使いやすさと実用性を兼ね揃えたデザインパターンと言えます。

インストール

それでは、パッケージのインストール方法から説明いたします。

Package Manager を使用する方法

導入したい Unity プロジェクトで Package Manager を開き、「+」ボタンを押して「Add package from git URL...」を選択、以下の URL を入力して「Add」ボタンを押すとインストールできます。

https://github.com/gigacreation/ServiceLocatorForUnity.git?path=Assets/Service

手動でインストールする方法

GitHub のリポジトリからソースコードをダウンロードし、その中の Assets/Service/ ディレクトリを導入したい Unity プロジェクトにコピーします。

使い方

基本の使い方

1. サービスコンテナへ登録したいクラスに IService を実装します。

using GigaCreation.Tools.Service;
using UnityEngine;

public class SampleService : IService
{
    public void Bark()
    {
        Debug.Log("Bowwow!");
    }
}

2. 次に、そのクラスをサービスコンテナに登録します。*1 *2

using GigaCreation.Tools.Service;
using UnityEngine;

public class RegisterServiceSample : MonoBehaviour
{
    private SampleService _sampleService;

    private void Awake()
    {
        // サービスを生成し……
        _sampleService = new SampleService();

        // サービスコンテナに登録する
        ServiceLocator.Register(_sampleService);
    }

    private void OnDestroy()
    {
        // GameObject が破棄されたら、サービスコンテナへの登録を解除する
        ServiceLocator.Unregister(_sampleService);
    }
}

3. これで、どこからでもアクセスできるようになりました!

using GigaCreation.Tools.Service;
using UnityEngine;

public class UseServiceSample : MonoBehaviour
{
    private void Start()
    {
        // 型を指定してサービスを取得できる
        var sampleService = ServiceLocator.Get<SampleService>();

        sampleService.Bark();
    }
}

IDisposable とセットで使う

サービスコンテナに登録されたクラスが IDisposable を実装していた場合、登録解除時に Dispose() が呼ばれます。

using System;
using GigaCreation.Tools.Service;
using UnityEngine;

public class SampleService : IService, IDisposable
{
    public void Bark()
    {
        Debug.Log("Bowwow!");
    }

    public void Dispose()
    {
        // 登録解除時に呼ばれる
        Debug.Log("SampleService disposed.");
    }
}

自作のインターフェイスとセットで使う

クラスをサービスコンテナへ登録する際、クラスを直接登録するのではなく、インターフェイスを介して登録することもできます。これにより、他から呼ばれるメソッドを限定することが可能です。

using GigaCreation.Tools.Service;
using UnityEngine;

public class SampleService : ISampleService
{
    public void Bark()
    {
        Debug.Log("Meow!");
    }

    public void Scratch()
    {
        // このメソッドは public であるが、ISampleService を介してアクセスすることはできない
        Debug.Log("Ouch!");
    }
}

public interface ISampleService : IService
{
    void Bark();
}

こんな形でサービスに自作のインターフェイスを実装し、

// 登録
// 左辺は ISampleService であることに注意
ISampleService sampleService = new SampleService();
ServiceLocator.Register(sampleService);

// アクセス
// ISampleService を指定して取得する
var sampleService = ServiceLocator.Get<ISampleService>();
sampleService.Bark();
sampleService.Scratch(); // <- これはエラー!

という感じでインターフェイスを介して登録やアクセスができます。*3

インターフェイスをさらに細分化して、各クラスで使用できるメソッドを個別に限定することなども可能です(SaveLoadService で、あるクラスはセーブだけ、あるクラスはロードだけできるようにする、みたいな)。

API リファレンス

一応リファレンスも用意していて、英語にはなりますが こちら で読むことができます。

補遺

シングルトンとの使い分け

「あるサービスへどこからでもアクセスしたい」という用途に関して言えば、シングルトンとサービスロケーターは使用感が結構似ていて、特にサービスの数が 1 つだけの場合、サービスコンテナを介すか介さないかの違いくらいしかありません。

サービスが 1 つの場合
あまりサービスロケーターのメリットが無さそうに見える

ごく小規模なミニゲームを作るときなどで、サービスの数が 1 つ程度で済むことが確定している場合は、シングルトンを使ってもそれほど大きな問題にはならないかと思います。

ただまぁ、ゲーム開発は得てして「あれも追加しよう、これも追加しよう」となりがちなので、後々の拡張性を考えれば、最初からサービスロケーターを使うのをオススメしたいですね。サービスの数が増えていけばいくほど、サービスロケーターで依存関係を整理できるのが活きてくるでしょう。

サービスが 3 つの場合
サービスロケーターならスッキリ!

先述の「自作のインターフェイスとセットで使う」をやれば、例えば同じインターフェイスを実装したデバッグ用のサービスを別で作り、デバッグモードの時はそちらを登録するようにすれば、使用者側のほうは何もせずともデバッグ用のコードを実行できる、といったことも実現可能です。

登録や破棄のタイミングも、サービスロケーターなら自由に変更できますしね(シングルトンだと基底クラスを変更したりしないといけないのでちょっと面倒)。

サービスロケーターはアンチパターンなのか?

DI パターンを説明する文脈では、サービスロケーターはアンチパターンとして紹介されることが多いようです。

実際これは一理あって、サービスロケーターは依存関係をスッキリさせられるとはいえ、結局サービスコンテナというグローバルな存在に依存することになりますし、コンテナにアクセスできるクラスは自動的に、コンテナに登録されているすべてのサービスにアクセスできてしまいます。例えば前項の図だと、Class B は SaveLoadService にだけアクセスできれば良いのに、すべてのサービスへのアクセス権を得られてしまう……ちょっと微妙ですよね。

ですので、必要なクラスに必要な分だけ依存性を注入する DI パターンのほうが、よりスマートだと言えそうです。Unity 開発であれば、 VContainer という素晴らしい DI フレームワークが存在していますので、もし DI を使いたいという場合はぜひ一度目を通すことをオススメします。DI とは何か? というところから、日本語で詳細に説明してくれています。

一方、DI の難点としては、やはり学習コストが高いことが挙げられると思います。VContainer のマニュアルを見ていただければ分かりますが、結構覚えないといけないことが多く、サービスロケーターやシングルトンに比べて導入はかなり難しめです。

サービスロケーターはグローバルなコンテナに依存するという点がマイナスポイントですが、各サービスはローカルな存在のまま保たれるので、十分実用に耐えられるデザインパターンだと個人的には考えています。シングルトンだとサービス自体がグローバルな存在になってしまうので、個人的にはデメリットのほうが大きいかなという印象です。

まとめると、サービスロケーターは手軽さと実用性を兼ね揃えた良いデザインパターンですので、シングルトンを使うくらいならぜひこちらを使っていってほしいと思います。より堅牢な設計を求めるのであれば、DI の導入を検討したい、といった感じですね。

*1:ここでは MonoBehaviour の Awake() で登録していますが、別に他のタイミングで登録しても良いですし、MonoBehaviour ではない Pure C# クラスで登録してももちろん大丈夫です

*2:OnDestroy() で破棄することで、そのシーン内でのみサービスが有効になります。これを無くすとシーンを切り替えたりしてもサービスが生き続けるので、シーン間のデータのやり取りが可能になります

*3: (sampleService as SampleService).Scratch() って書いたら無理やりクラスへアクセスできたりもしますが……

Rainbow Folders と一緒に使える、Unity 用のフォルダアイコンを公開しました

概要

Unity プロジェクトに Rainbow Folders を導入すると、Project View のフォルダアイコンがフォルダ名に応じたものになり、視認性が向上します。

このたび、その Rainbow Folders のフォルダアイコンのデザインに沿って作成した、自作のアイコンをいくつか公開しました。

github.com

Rainbow Folders と一緒に使うのがオススメですが、エディター拡張を書けばこれ単体でも使用できます。

ダークテーマ

https://user-images.githubusercontent.com/5264444/219367565-2cdbfc53-aeaa-4d91-9966-de6e5877494a.png

ライトテーマ

https://user-images.githubusercontent.com/5264444/219367571-7f0158f9-cdca-4e82-90f5-e450c82c2c2c.png

Addressables.DownloadDependenciesAsync() によるダウンロードを、アセットバンドルごとに分けて行う

はじめに

Addressables のダウンロードを行う場合、普通にコードを書くと以下のようになります。

// ※ラベル名を指定してダウンロードする場合

// Addressable アセットのラベル名
string labelName = "LABEL_NAME";

// 指定したラベルを持つすべての Addressable アセットの IResourceLocation を取得する
IList<IResourceLocation> locations = await Addressables.LoadResourceLocationsAsync(labelName);

// 取得した IResourceLocation が含まれるアセットバンドルをすべてダウンロードする
await Addressables.DownloadDependenciesAsync(locations);

この場合、ダウンロードするアセットバンドルが複数あっても一度にダウンロードを行うため、以下のような問題が起こります。

  • 現在ダウンロード中のアセットバンドルの情報が取得できない。
    • 例えば、「全体のうち何 % のダウンロードが完了したか」は取得できるが、「今ダウンロードしているアセットバンドルのうち何 % のダウンロードが完了したか」は分からない。
  • サーバーの設定によっては、ダウンロードに時間がかかりすぎるとサーバー側から通信を打ち切られる場合がある。

アセットバンドルごとにダウンロードする

こうした問題を防ぐため、各アセットバンドルごとに別々にダウンロードを行うようにします。

これは、以下のようなコードで実現できます。

// Addressable アセットのラベル名
string labelName = "LABEL_NAME";

// 指定したラベルを持つすべての Addressable アセットの IResourceLocation を取得する
IList<IResourceLocation> locations = await Addressables.LoadResourceLocationsAsync(labelName);

// DependencyHashCode でグルーピングする
foreach (IGrouping<int, IResourceLocation> groupedLocations in locations.GroupBy(x => x.DependencyHashCode))
{
    // グループごとに IResourceLocation のダウンロードを行う
    await Addressables.DownloadDependenciesAsync(groupedLocations.ToList());
}

解説

IResourceLocation は、 DependencyHashCode というパラメータを持ちます。

これは他の IResourceLocation との依存関係を示すハッシュコードなのですが、依存関係が同じ=所属しているアセットバンドルが同じなので、この値をもとにグループ分けすれば、アセットバンドルごとに IResourceLocation を分けることができます。

あとは、分けたグループごとに Addressables.DownloadDependenciesAsync() を実行してやれば OK です。

補遺

これを応用すると、以下のようなダウンロード画面を実装できます。

f:id:Gigacee:20211228195551g:plain
プログレスバー 2 本のダウンロード画面。上:全体の進行度、下:アセットバンドルごとの進行度

The coloring of this site is Dracula PRO🧛🏻‍♂️
This website uses the FontAwesome icons licensed under CC BY 4.0.

2020 GIGA CREATION