結論
NDMFで自作プラグインの順序制御をするにはQualifiedNameを定義してAfter/BeforePluginする。
NDMFとは?
Unityで非破壊的なビルドを実現するためのもの
下のページをよく読みましょう1
雑に言うと、NDMFを使うと「AddComponent」したものに対応してビルド時に「いい感じの処理」をすることができます2
PluginとPassとは?
-
Pluginは、ざっくりいうとNDMFツールのことです。例えば「ModularAvatar」とか「AAO」とかはPluginです
-
Passは、Pluginを構成するものです。多くの場合、1種類のコンポーネントに対応します。たとえば「MergeArmature」とか「BoneProxy」は「ModularAvatar」というPluginを構成するPassです
-
自分が作ったのはどっち?
- 小規模なものを開発するときは(公式のサンプルがPluginのものであることもあり、)Pluginを作成することがおおいでしょう
- Passは、NDMFツールを公開するにあたって複数のコンポーネントを内包する事になってから考えるものです
順序制御とは?
NDMFは「AddComponent」したものに対応してビルド時に処理するものです
コンポーネントは色々あります。
ではそれぞれをどういう順番でやればいいか?ということを決めるのが順序制御です。
フェーズ
大雑把な順序制御です。悩んだらTransformingにしておきましょう
- Resolving(解決): アバターが変形する前に、保存された状態を元にコンポーネントを復元するためのフェーズ(例: オブジェクトの移動前に文字列で保存されたパスをオブジェクトに変換するなど)
- Generating(生成): 新しいオブジェクトやコンポーネントを生成し、他のシステムで使用する前に実行されるフェーズ
- Transforming(変形): 一般的な処理を行うフェーズで、多くの拡張機能がアバターを変形させます
- Optimizing(最適化): アバターに意味的な変更を加えずに、最適化処理を行うフェーズ
細かい順序制御
例えば「MergeBlendTree」というPassは「MergeAnimator」というコンポーネントを作る機能を持っています。ですので、MergeBlendTreeはMergeAnimatorよりも先に実行しないと、うまく動きません。これを制御するにはフェーズ制御だけではうまくいきません。ではこういうときにどうやって順番を制御するか?3ということを定義するのが「(After/Before)(Plugin/Pass)」です。
具体的な方法:「ModularAvatarより前」
例えば「MA Menu Item」を作成するNDMFツールを作ったとします。これはModularAvatarより先に動作する必要があります。このときはこう書きます
[assembly: ExportsPlugin(typeof(作るプラグインの名前))]
namespace 逆ドメイン形式の自分プロジェクト名.editor
{
public class 作るプラグインの名前 : Plugin<作るプラグインの名前>
{
protected override void Configure()
{
InPhase(BuildPhase.Transforming)
.BeforePlugin("nadena.dev.modular-avatar")
.Run("実行時のメッセージ", ctx =>
{
// 実際の処理
});
}
}
この記事として大事なのはここです。
Transformingのフェーズで実行する
InPhase(BuildPhase.Transforming)
ModularAvatarより前に実行する
.BeforePlugin("nadena.dev.modular-avatar")
このようにして、順番を指定したいものを表す文字列を(After/Before)Pluginで指定すればよいです。
ではこの文字列ってそもそもどうやって定義するの?というところがこの記事の本題です
具体的な方法:「自作のhogehogeより前」
後に実行するプラグインは次のように定義しましょう
[assembly: ExportsPlugin(typeof(後に実行するプラグインの名前))]
namespace 逆ドメイン形式の自分プロジェクト名.editor
{
public class 作るプラグインの名前 : Plugin<後に実行するプラグインの名前>
{
public override string DisplayName => "後に実行するプラグインの名前";
public override string QualifiedName => "後に実行するプラグインの逆ドメイン形式の正式名称";
protected override void Configure()
{
InPhase(BuildPhase.Transforming)
.Run("実行時のメッセージ", ctx =>
{
// 実際の処理
});
}
}
重要なのはQualifiedNameです。これがAfter/Beforeで指定する文字列の正体です
先に実行するプラグインは次のようになります
[assembly: ExportsPlugin(typeof(先に実行するプラグインの名前))]
namespace 逆ドメイン形式の自分プロジェクト名.editor
{
public class 先に実行するプラグインの名前 : Plugin<先に実行するプラグインの名前>
{
protected override void Configure()
{
InPhase(BuildPhase.Transforming)
.BeforePlugin("後に実行するプラグインの逆ドメイン形式の正式名称")
.Run("実行時のメッセージ", ctx =>
{
// 実際の処理
});
}
}
Beforeで作った後Afterに変えたりしてみて、コンソール上で順番がうまく変わることを確認してみましょう!
補足1:逆ドメイン形式の正式名称って何?
- VPM作成をすると出てくる概念で、「hogehoge.名前.プロジェクト名」のようなものです
- 基本的にPackageManagerは自分のドメインを持っている人が作ることを想定されているらしく、本来hogehogeの中には自分のドメイン名が入ります
- 一番重要なのは他人と被らないことです。ドメインをもっていなければgithubのユーザーサイトのものを借りたりしてもいいのじゃないかな4と思います
補足2:Passを定義したくなってきた
- ModularAvatarを読んで勉強しましょう
- ModularAvatarは全体で1つのPluginなので、Plugin定義も1回だけです。PluginDefinition.csで行っています
- QualifiedNameを定義してあります。よくAfter/Before Pluginするやつです
- LogoとかThemeColorが定義できるんですね・・? 何に使うんだろう
- Configreがあります。Plugin定義のお約束
- InPhase句を一度seq変数に入れて、そこから複数のRunを呼ぶことでそれぞれのPassを呼んでいます
- Passはクラス名.Instanceとすると呼べそうです
- Passの定義は色々な場所・方法でやっていますがScaleAdjusterPass.csがシンプルでわかりやすい
- PassはPluginと違い[Export]みたいなものはないようです
- クラス名・ファイル名の末尾を「Pass」にしておくと解りやすそうです
- Pass<クラス名>を継承しています
- protected override void Execute(ndmf.BuildContext context)の宣言が強制されているのでそれを強制して、あとは普段通りの実装です。引数名はいつもどおりctxにしたほうが分かりやすいかも
- ModularAvatarは全体で1つのPluginなので、Plugin定義も1回だけです。PluginDefinition.csで行っています