Contents

declavatar について

もはや declarative なのかと言われると微妙な気はするけど気にしてはいけない。

手作業で作るのがだるいアセットをテキストファイルでなんとかしましょう。 declavatar / modular-declavatar の力で……

前史

declavatar、の成り立ちを説明するには、まず sk2aac について紹介しなければなりません。これについては以前 VRC-LT で発表した資料があるのでそちらもどうぞ。当時いかに突貫工事でこれが作られたかがわかるぞ!

Animator As Code As JSON As Model

発表資料にあるとおりこれは 「TOML を書いて C# コードを生成する」 というなかなかにロックな構造をしていたわけですが、徐々に以下のような問題が顕在化してきました。

  • TOML で入れ子が深くなる条件やアニメーション定義を書くのがつらい
    • それはそう
    • 元々 TOML はそんな階層が深くなるような構造を書くためにあるわけではない
  • コード生成部の見通しが悪すぎる
    • メンテができない
    • 機能追加するにしてもクォートを入れ忘れたり構文エラーになるコードを吐いたりしがちだった

そして去年末、ついに TOML との別れを決意し新たにデータ定義言語 KDL を迎え入れて精神的後継 declavatar の開発を開始したわけです。

別に sk2aac が使い物にならないわけではなかったので、しばらくは新規アバターのセットアップには sk2aac を使いつつ、ゆるゆると実装を進めていました。 で、 6 月ぐらいに sk2aac の全機能を代替できるところまで実装ができたので、このあたりで sk2aac のリポジトリをアーカイブし declavatar に一本化しました。

ここまではまあ良かったんです。

MA の台頭に伴うある種のガラパゴス化

今や一大インフラとして受け入れられている Modular Avatar (以下 MA) ですが、 declavatar 開発当初はここまで MA が普及するとは思っていなかったので特段存在を意識することなく仕様などを決めていました。 しかし(体感では)今年下半期以降急速に普及が進み、 MA の導入を前提とした衣装やギミックなども販売されはじめました。

さて、これは当時の僕にとってちょっと、いや割と困った事態でした。 declavatar は生成される AnimatorController や Expressions Menu などの正当性を定義ファイル単体かつビルド前に解決するため、 MA によって ビルド時に統合される衣装のメッシュの切り替えなどは原理的・思想的に不可能だった のです。これにより、数カ月間の間は declavatar での作業を行うために手動で衣装を着せたりギミックを移植しなければならなかったのです。

AaC の NDMF 対応により急速に解決

しました。

sk2aac・declavatar いずれも AnimatorController や AnimationClip を生成する部分で Animator As Code (以下 AaC) に強く依存していたので、 これが MA のワークロードに沿うような形で API が更新されたのは非常にインパクトが大きかったです。 また、 MA 側でも非破壊的操作の部分が Non-Destructive Modular Framework (以下 NDMF) として別途整理されたことで、MA 以外のツールを MA と連携してビルド時に動作させることが容易になりました。

ここでようやく重い腰を上げて連携可能なように改修を始めました。そして数日足らずで完了しました。 実際 declavatar はアバターの GameObject に対して何か破壊的な操作を行うわけではなく、あくまで大量のアセットを機械的に生成するだけで最終的な AnimatorController などの割り当てはユーザー(自分しかいねえだろ)が実施する、という形態でした。 よって、 実質的には「ビルド前にアセットのファイルを生成する」かわりに「ビルド中にメモリ上に生成して MA 側にマージしてもらう」変更だけ でほとんどが上手くいきました。よかった~。

…… MA で入れる衣装のアセットが作れない問題が解決してないじゃないかって? そこはですね、 「衣装をアバターとみなして衣装単体で定義ファイルを作ってやはり MA にマージしてもらう」 という僕のかすかな希望がそのまま現実となって現れたのでほぼ何もしなくてすみました。

基本的な使い方

定義ファイルの形式が近々大幅に変わります

このタイミングでこれを解説しておきながらこんなことを書くのは非常に申し訳ないのですが、 現在定義ファイルの形式を KDL から Lisp にする大型アップデートの実装が進行中です。 移行についてはある程度僕が人力でサポートすると思うんで何かあったら言ってください。 意味論はそこまで変化しないとは思いますが……

定義ファイルを KDL ではなく Lisp (Ketos) で書けるようにする

この記事では KDL 版で進行します。

こちらもなんとかして VPM 対応したので、 僕の VPM Package Repository から最新版を選んでインストールするだけで使えます。 ちなみにリポジトリとしての declavatar はコア機能を Rust で実装しているもので、エディタ拡張などは modular-declavatar に含まれています。

まずはアバターを通常通りセットアップしてたら、アバタールートの GameObject に GenerateByDeclavatar を追加します。ちなみにこのコンポーネントは MA や AAO などのコンポーネントと同様ビルド後に削除されるので、 VRCSDK にアップロード時に怒られることはありません。 いくつかフィールドがありますが、最低限 Definition (TextAsset) のところだけいじれば動きます。

/posts/about-declavatar/gbd-component.png
このようなコンポーネントが生成されます

拡張子 .kdl で定義ファイルを作成します。

version "1.0.0"

avatar "ここは適宜変えてください" {

}

これを Unity にインポートすると TextAsset として処理されます。そのアセットを先程の Definition フィールドに指定してあげればセットアップは完了です。 あとはビルド時に全部やってくれるので、定義ファイルを編集してビルドして確認を繰り返しましょう。

サンプルの使い方
ここから先のサンプルは、全て定義ファイルの avatar “XXXXXX” { … } の … ブロック内に記述する内容を示しています。 リポジトリに含まれるサンプルも参照してください。

定義サンプル: メニューで一部の衣装を切り替える

アバター直下にある Hat というオブジェクトをメニューで切り替えます。

parameters {
    // ExParams で Bool で定義されるパラメーター
    // declavatar はデフォルトでパラメーターを保存しないので、保存したい場合は save=true を追加する
    bool "hat-enabled" save=true
}

animations {
    // Bool 型パラメーターで操作される AnimatorController のレイヤーを定義する
    // switch の後の名前は、他の animations の要素と被らなければなんでもよい
    switch "帽子切り替え" {
        // 上で定義した hat-enabled を使用する。名前や型が間違っているとエラーとして検出される
        parameter "hat-enabled"

        // GameObject "Hat" を操作する。パラメーターが false (disabled) のときに true、つまり表示し、
        // true (enabled) のときに非表示にする
        object "Hat" disabled=true enabled=false
    }
}

menu {
    // Toggle タイプのメニュー項目を定義する
    // 1 つ目の名前はメニューのラベルとして使われ、 switch="..." の名前は上で定義した switch の名前と合わせる必要がある
    toggle "帽子" switch="帽子切り替え"
}

定義サンプル: 靴を脱いだときに絞りシェイプキーを解除する

Shoes と靴下 Socks は分かれていて、靴を履いていないときはハイヒール化 Body.Foot_Heel を解除し、裸足のときは絞るシェイプキーを解除する Body.Shrink_Foot という例です。

parameters {
    int "shoes-mode"
}

animations {
    // Int 型パラメーターで操作される AnimatorController のレイヤーを定義する
    // 主に 3 つ以上の状態を切り替えたいときに使う
    group "足" {
        parameter "shoes-mode"
        
        // 操作に使うパラメーターが 0 のときに適用するデフォルト設定
        // ここに書かれた設定は、他の option で被らない限りデフォルト値を書き続けるようにコピーされる
        // (Write Default OFF 対応)
        default {
            object "Shoes" value=true
            object "Socks" value=true

            // シェイプキーの設定範囲は Unity とは異なり 0.0-1.0 なので注意
            shape "Foot_Heel" mesh="Body" value=1.0
            shape "Shrink_Foot" mesh="Body" value=1.0
        }

        // option で 1 つのパターンを定義する。
        // 操作に使うパラメーターの値は自動で割り振られる。
        option "靴下" {
            object "Socks" value=false
            shape "Foot_Heel" mesh="Body" value=0.0
        }

        option "裸足" {
            object "Shoes" value=false
            object "Socks" value=false
            shape "Foot_Heel" mesh="Body" value=0.0
            shape "Shrink_Foot" mesh="Body" value=0.0
        }
    }
}

menu {
    // サブメニューも定義可能かつ入れ子にできる
    submenu "足" {
        // toggle は group も制御することができる
        // group の名前とその中の option の名前を両方指定する。
        toggle "靴下" group="足" option="靴下"
        toggle "裸足" group="足" option="裸足"
    }
}

その他の機能

サンプルにないけど使える機能としては次のようなものがあります。

  • Renderer の指定番号マテリアルの差し替え
  • Float 型によってなめらかに動くアニメーションの定義
  • ほぼそのまま AnimatorController に変換されるレイヤー
  • Radial/Two/Four Puppet の定義
  • etc…

逆に現在は Gesture Layer や Action Layer を生成することはできませんが、今後需要が高ければ実装予定です。