DeepFrostIce / VRChat Avatar
パラメータをアニメーションで動かす
DeepFrostIce の凍結・融解・厚み吸収・ディゾルブ・トップスノー・スパークル・表示切替を、VRChat の Expression Menu から動かすためのガイドです。同梱サンプルをドラッグするだけの最短ルート(STEP A)と、仕組みを理解して自分のメッシュに組む/改変するルート(STEP B)の二段構えでまとめています。
仕組みの全体像
やることは一本道です。メニュー操作が同期パラメータを変え、それが FXレイヤーを通じて AnimationClip を再生し、クリップが Renderer 上の material.<プロパティ> を書き換えます。
「DeepFrostIce」
DFI_Freeze などFloat / Bool
レイヤー
material.
_FreezeAmountこの鎖は同梱サンプルに全部入っています
クリップ・AnimatorController・Expression パラメータ・メニュー・Modular Avatar の設定はすべて Sample/ に生成済みです。まず動かしたいだけなら STEP A だけで完了します。自分のメッシュに適用したい・値を変えたい場合に STEP B へ進んでください。
同梱サンプルをそのまま使う
プレハブをアバターの配下にドラッグしてアップロードするだけで、Expression Menu に「DeepFrostIce」サブメニューが追加され、Radial スライダーとトグルで氷を動かせます。Animator もパラメータもメニューも、手で触る必要はありません。
用意するもの
- VRChat SDK3(Avatars)を導入した Unity プロジェクト
- Modular Avatar(サンプルのマージに使用)
- DeepFrostIce 本体(
Sample/フォルダを含む)
アバターのシーンを開く
いつもアップロードしているアバターのシーンを開き、Hierarchy にアバターが置かれている状態にします。
サンプルをアバター配下にドラッグ
Sample/DeepFrostIceSample.prefabを、Hierarchy 上でアバターの子になるようにドラッグ&ドロップします。これだけで、FX レイヤー・同期パラメータ・メニューがビルド時にマージされる状態になります。ドラッグ後の Hierarchy YourAvatar ├─ Armature ... ├─ Body ... └─ DeepFrostIceSample ← ドラッグしたプレハブ └─ DeepFrostIce Sphere (Renderer + DeepFrostIceSample.mat)
位置とスケールを調整(任意)
既定は
0.15スケールの氷球プロップです。手や腰のボーンの下に入れる、大きさを変えるなど、好きな見せ方に調整してかまいません。いつも通りアップロード
VRChat SDK からいつも通りビルド&アップロードします。ビルド時に Modular Avatar が、FX Controller・Expression Parameters・Expression Menu へ自動でマージします。アバター側の既存メニューやパラメータは壊れません。
VRChatで動かす
Expression Menu を開き、「DeepFrostIce」サブメニューへ入ります。Freeze / Melt / Absorption / Dissolve は Radial Puppet(リングを回して 0 から増やす)、Top Snow / Sparkle / Visible はトグルです。
初期値について
Radial の4つ(Freeze / Melt / Absorption / Dissolve)は既定 0=未適用の状態から始まります。トグルの3つ(Top Snow / Sparkle / Visible)は既定 ON なので、アップロード直後から見た目で効果を確認できます。いずれも saved 指定なので、ワールドをまたいでも最後の状態が維持されます。
サンプルの操作一覧
「DeepFrostIce」サブメニューの7項目が、それぞれどのパラメータ・どのプロパティに繋がっているかの対応表です。STEP B で自作・改変するときの設計図にもなります。
| メニュー項目 | 種類 | 同期パラメータ | 動かす対象 | レンジ / 値 |
|---|---|---|---|---|
| Freeze | Radial Puppet | DFI_FreezeFloat・saved・既定0 | material._FreezeAmount | 0 → 1 |
| Melt | Radial Puppet | DFI_MeltFloat・saved・既定0 | material._MeltAmount | 0 → 1 |
| Absorption | Radial Puppet | DFI_AbsorptionFloat・saved・既定0 | material._AbsorptionDensity | 0 → 5 |
| Dissolve | Radial Puppet | DFI_DissolveFloat・saved・既定0 | material._DissolveAmount | 0 → 1 |
| Top Snow | Toggle | DFI_TopSnowBool・saved・既定ON | material._TopSnowAmount | 0 / 0.6 |
| Sparkle | Toggle | DFI_SparkleBool・saved・既定ON | material._SparkleIntensity | 0 / 3 |
| Visible | Toggle | DFI_VisibleBool・saved・既定ON | 子GameObject の m_IsActive | Off / On |
Absorption だけレンジが 0→5 ですが、パラメータ自体は他と同じ 0〜1 です。これはクリップ側の値レンジを 0→5 にしているだけ、という点が後の STEP B-1 の要点になります。
クリップを作る
Animation ウィンドウで、対象は必ず Renderer が乗っている GameObject(サンプルでは DeepFrostIce Sphere)を選びます。バインドのキーは material.<プロパティ名> です(Unity では「Mesh Renderer → Material._プロパティ」として一覧に出ます)。
連続値のクリップ(線形カーブ1本)
先頭に from、末尾に to のキーを打った線形カーブを1本だけ入れます。クリップの長さ自体は Motion Time で正規化されるため意味を持ちません(1秒で十分)。0→1 以外のレンジは、パラメータではなくクリップの値レンジ側で表現します。
対象 GameObject : DeepFrostIce Sphere (Rendererが乗る子) プロパティ : Mesh Renderer → Material._FreezeAmount キー : 0.00s = 0 / 1.00s = 1 (線形 / Linear) Melt / Dissolve も同じ形(0→1): DeepFrostIceSample_Melt : Material._MeltAmount 0 → 1 DeepFrostIceSample_Dissolve : Material._DissolveAmount 0 → 1 Absorption だけ末尾キーが 5: DeepFrostIceSample_Absorption: Material._AbsorptionDensity 0 → 5
オン/オフのクリップ(定数2本)
Off と On で1本ずつ、それぞれ1フレームの定数クリップを作ります。Write Defaults を off で運用するため、Off クリップでも必ずそのプロパティを off 値で打つのが要点です(打たないと値が戻りません)。
DeepFrostIceSample_TopSnowOff : Material._TopSnowAmount = 0 DeepFrostIceSample_TopSnowOn : Material._TopSnowAmount = 0.6 DeepFrostIceSample_SparkleOff : Material._SparkleIntensity = 0 DeepFrostIceSample_SparkleOn : Material._SparkleIntensity = 3 いずれも1フレームの定数カーブ
表示/非表示のクリップ(m_IsActive)
オブジェクトごと消す場合は、マテリアルではなく 子 GameObject の有効状態(m_IsActive)を動かします。ポイントは、Animator が乗るルート(DeepFrostIceSample)ではなくその子(DeepFrostIce Sphere)を対象にすること。子を対象にすれば、非表示にしても Animator まで止まりません。
対象 GameObject : DeepFrostIce Sphere (子。ルートではない) プロパティ : Is Active (m_IsActive / type = GameObject) DeepFrostIceSample_VisibleOff : 0 (1フレーム定数) DeepFrostIceSample_VisibleOn : 1 (1フレーム定数)
自分のメッシュに適用したいとき
サンプルの氷球ではなく自分のアクセサリや衣装に使いたい場合は、DeepFrostIce のマテリアルを自分の Renderer に割り当て、上記クリップの対象 GameObject を自分の Renderer に差し替えるだけです。プロパティ名とレンジはそのまま使えます。
Animatorレイヤーを組む
FX 用の AnimatorController に、パラメータごとに1レイヤーを足します。サンプルの DeepFrostIceSample.controller は7レイヤー(Motion Time 4本+トグル3本)構成で、マージ先に無駄が残らないよう自動生成される空の Base Layer は削除してあります。各レイヤーは Weight = 1、Write Defaults = off で統一しています。
パラメータを宣言する
Animator の Parameters に、連続値は Float(DFI_Freeze / DFI_Melt / DFI_Absorption / DFI_Dissolve)、オン/オフは Bool(DFI_TopSnow / DFI_Sparkle / DFI_Visible)を追加します。名前は Expression パラメータ名と完全一致させます。
Motion Time レイヤー(Radial Puppet 用・4本)
1ステートだけのレイヤーを作り、その State に 0→1 のクリップを置きます。State のインスペクタで Motion Time を有効化し、対応する Float パラメータを割り当てます。すると 0〜1 のパラメータがクリップの正規化時間へ写像され、結果としてクリップの from→to(Absorption なら 0→5)へ写ります。
Off/On トグルレイヤー(3本)
Off / On の2ステートを作り、既定ステートは Off。Bool パラメータで相互遷移させます。Off→On は「If」、On→Off は「IfNot」。どちらも Has Exit Time = off、遷移時間は 0.1 秒です。
Write Defaults の統一
FX 全体で Write Defaults の on/off を混在させないのが鉄則です。サンプルは off 前提で、各ステートのクリップに「そのステートで欲しい値」を明示的に入れています。Modular Avatar の Match Avatar Write Defaults が、マージ先アバターの流儀に自動で合わせます。
同期パラメータとメニュー
Animator を動かすための 同期パラメータ と、それを触るための Expression Menu を用意します。
Expression Parameters
- 連続値:
Float、Saved 有効、既定値0(DFI_Freezeほか4つ)。 - オン/オフ:
Bool、Saved 有効、既定 ON を明示(DFI_TopSnow/DFI_Sparkle/DFI_Visible)。 - 名前は Animator のパラメータ名と完全一致させます。
Expression Menu
- Radial Puppet:コントロール自身の Parameter は空のまま、Rotation(サブパラメータ)に Float 名を割り当てます。
- Toggle:コントロールの Parameter に Bool 名を割り当て、Value = 1。
- 7項目はルートに直挿しせず、サブメニュー1個にまとめて、ルートにはその入口を1つだけ置きます。サンプルでは
DeepFrostIceSampleMenuControls.assetに7項目、DeepFrostIceSampleMenu.assetに「DeepFrostIce」サブメニュー1件、という2枚構成です。
- アバターのルートメニュー
- (アバター既存の項目…)
- DeepFrostIce SubMenu
- Freeze Radial
- Melt Radial
- Absorption Radial
- Dissolve Radial
- Top Snow Toggle
- Sparkle Toggle
- Visible Toggle
Modular Avatarでマージする
Controller・Parameters・Menu を、プレハブのルート(DeepFrostIceSample)に載せた4つのコンポーネントでアバターへマージします。これが「ドラッグするだけで動く」正体です。
Animator(一時置き)
ルートに
Animatorを付け、Controller にDeepFrostIceSample.controllerを指定します。これはマージ用の受け渡しで、下の Merge Animator がマージ後に自動で外します。MA Merge Animator
Merge Animator To=DeepFrostIceSample.controllerLayer Type= FXDelete Attached Animator= ON(上の一時 Animator を掃除)Path Mode= Relative(クリップのパスをプレハブ内の Renderer に解決)Match Avatar Write Defaults= ON
MA Parameters
7つの同期パラメータをアバターの Expression Parameters へ登録します。Float 4つは Saved・既定 0、Bool 3つは Saved・明示的に既定 ONを指定しています。
MA Menu Installer
Menu to AppendにDeepFrostIceSampleMenu.asset(「DeepFrostIce」SubMenu を1つ持つルートメニュー)を指定します。アバターのメニューへその SubMenu が append され、ルートを埋めません。
MAでマージする利点
Controller・Parameters・Menu をアバター本体へ手作業でコピーしないので、既存設定と衝突しない・複数アバターに使い回せる・更新の差し替えが楽という利点があります。Renderer とマテリアルは同じプレハブ内に置き、Relative パスで解決させます。
MAを使わない手動組み込み
Modular Avatar を使わない場合は、同じ中身をアバター本体へ直接足します。手数が増え、衝突にも注意が要りますが仕組みは同じです。
- FX Controller:STEP B-2 の7レイヤー(Motion Time 4本/トグル3本)をアバターの FX AnimatorController へ複製。クリップのバインドパスが、アバター配下での Renderer のパスと一致するようパスを合わせ直します。
- Expression Parameters:
DFI_*の Float 4つ(Saved・0)/Bool 3つ(Saved・既定ON)を手動登録。 - Expression Menu:SubMenu を作って Radial/Toggle の7コントロールを入れ、アバターのメニューへその SubMenu を追加。
- Write Defaults はアバターの FX 全体の流儀に合わせて統一します(混在させない)。
つまずきやすい注意点
動かないとき・意図せぬ挙動のとき、まずここを疑ってください。
Write Defaults の混在
FX 内で on/off が混ざると値が戻らない・効かないの温床に。全体で統一。off なら各ステートで欲しい値を必ず明示します。
Motion Time のレンジ
Radial は必ず 0〜1。Absorption の 0→5 のような幅はクリップ側の値レンジで表現します。パラメータを 0〜5 にしようとしないこと。
ルートメニューの圧迫
ルートは8枠しかありません。項目はSubMenu にまとめて、ルートにはその入口を1つだけ置きます。
同期パラメータのコスト
同期は 256bit の枠を共有(Float=8bit / Bool=1bit)。サンプルは Float 4+Bool 3 で 35bit 使います。不要な項目は削って構いません。
名前の食い違い
Animator パラメータ/Expression Parameters/メニューの名前は完全一致。DFI_Freeze が1文字違うだけで無反応になります。
表示切替の対象
オブジェクト非表示は 子の DeepFrostIce Sphere の m_IsActive を動かします。Animator が乗るルートを消すと Animator まで止まります。