syoshino

AssetBundleGraphTool を触ってみました


エンジニアの吉野です。

先日アップデートされたばかりの AssetBundleGraphTool v1.3 をこれまた先日リリースされた Unity 2017.1 に入れてみました。

AssetBundleGraphTool の bitbucket ページ
日本語マニュアル

今回はゲームで使用する Asset がどの AssetBundle に入っているかのテーブルデータをカスタムノードで作成したのでかいつまんで紹介しようと思います。

概要

ゲームで Asset を読むときに、開発中はローカルから、実機では AssetBundle を使いたいということが良くあります。
(Unite 2017 で紹介されていた Addressable Assets が入ればいらなくなるはずなので、それまでのつなぎで…)
これを満たすための仕様としては、おおむね以下の図のようになると思います。

asset load flow

この図の AssetBundleTable で使う AssetPath → AssetName 変換用のデータをカスタムノードで作ります。

完成したGraph図はこのようになります。

graph sample

カスタムノードの作り方は日本語のマニュアルの「カスタムノードを追加する」を見てください。
以下、作成したノードの要点を解説していこうと思います。

入力/出力するノードのタイプを指定する

今回は、「AssetBundle 名と AssetPath が設定されたノード」(Grouping)と 「AssetBundle をビルドするノード」(BundleBuilder)の間に入れたいので、以下のようにします。


    public override Model.NodeOutputSemantics NodeInputType
    {
        get
        {
            return Model.NodeOutputSemantics.AssetBundleConfigurations;
        }
    }

    public override Model.NodeOutputSemantics NodeOutputType
    {
        get
        {
            return Model.NodeOutputSemantics.AssetBundleConfigurations;
        }
    }

インスペクター上から AssetBundleTable を指定できるようにする

Inputされた情報を書き込む AssetBundleTable を渡せるようにします。
指定はエディタ拡張でおなじみの OnInspectorGUI(…) 部分に追加します。


public override void OnInspectorGUI(
    NodeGUI node,
    AssetReferenceStreamManager streamManager,
    NodeGUIEditor editor,
    Action onValueChanged)
{
...
    tableAsset = EditorGUILayout.ObjectField("AssetBundleTable Object", tableAsset, typeof(AssetBundleTable), false) as AssetBundleTable;
...
}

ビルド時に AssetBundleTable を更新する

Graph をビルドすると、カスタムノードの Build(…) が呼ばれます。この時に受け取った情報をTableに設定します。


public override void Build(
    BuildTarget target,
    Model.NodeData node,
    IEnumerable<PerformGraph.AssetGroups> incoming,
    IEnumerable<Model.ConnectionData> connectionsToOutput,
    PerformGraph.Output Output,
    Action<Model.NodeData, string, float> progressFunc)
{
...
    tableAsset.Clear();
    foreach (var ag in incoming)
     {
         foreach (var assetGroup in ag.assetGroups)
         {
             foreach (var inputAsset in assetGroup.Value)
             {
                 tableAsset.Add(new AssetBundleLoadPath(assetGroup.Key, inputAsset));
             }
         }
     }
     EditorUtility.SetDirty(tableAsset);
     AssetDatabase.SaveAssets();
...
}

AssetBundleTable 自身も AssetBundle にする

実機で使うので、AssetBundleTable も忘れずに AssetBundle にします。
幸いこの後に AssetBundle をビルドするノードがあるので、Output に追加して流します。


public override void Build(
    BuildTarget target,
    Model.NodeData node,
    IEnumerable<PerformGraph.AssetGroups> incoming,
    IEnumerable<Model.ConnectionData> connectionsToOutput,
    PerformGraph.Output Output,
    Action<Model.NodeData, string, float> progressFunc)
{
...
    EditorUtility.SetDirty(tableAsset);
    AssetDatabase.SaveAssets();

    if (Output != null)
    {
        var dst = (connectionsToOutput == null || !connectionsToOutput.Any()) ?
            null : connectionsToOutput.First();

        // 上流のノードから流れてきたものをそのまま渡します。
        foreach (var ag in incoming)
        {
            Output(dst, ag.assetGroups);
        }

        // AssetBundleTableを追加して渡します。
        var additionalOutput = new Dictionary<string, List<AssetReference>>();
        additionalOutput.Add(settings.tableAssetBundleName,
            new List<AssetReference> { 
                AssetReference.CreateReference(AssetDatabase.GetAssetPath(tableAsset), typeof(AssetBundleTableListData))
            }
        );
        Output(dst, additionalOutput);
    }
}

これで、作成した Graph がビルドされるたびに AssetBundleTable が更新されて、AssetBundle にまでなってくれます。
日本語のわかりやすいマニュアルがあるおかげで、すんなりと実装できてしまいました。

使ってみた感想

ノードの拡張機能があるおかげで Asset を読んで加工してなにかする、という機能をグラフィカルかつ低コストで作成できる素晴らしいツールだと思います。
カスタムノード以外にもいろいろな機能があるのでぜひマニュアルを見てみてください。
いろいろ応用できそうなので今後も活用していこうと思います!