凹みTips

C++、JavaScript、Unity、ガジェット等の Tips について雑多に書いています。

uLipSync で Animator を使ったリップシンクができるように更新してみた

はじめに

uLipSync v2.5.1 をリリースしました。

github.com

以下の 2 点のアップデートが含まれています。

それぞれ PR を頂きました、ありがとうございます。

github.com

github.com

本記事では、これらのうち、ちょっと解説が必要な Animator 利用についてセットアップの方法やユースケースについての解説を行おうと思います。uLipSync そのものの使い方については以下の記事をご参照ください。

tips.hecomi.com

概要

BlendTree を組んであげると Animator へ音素に応じたパラメタが渡り、次のようにリップシンクさせることが出来ます。

Animator を作ることの利点としては次のような点が挙げられます。

  • ブレンドシェイプがセットアップされていないモデルでアニメーションベースで動作させられる
  • リップシンクのステートとアニメーションのステートの切り分けができる
    • 普段はアニメーションに応じた表情をしつつ、必要になったタイミングで口パクのリアルタイム解析ステートへと遷移が可能

これらの設定例は 09. Animator に含まれています。

設定

Animator の設定

まずは Animator を作成します。Animator 自体についての詳細については省きます*1が、だいたい次のような感じになるかと思います。

  • AnimatorController を準備
  • 顔(または口のみ)の Avatar Mask を設定したレイヤを作成
  • リップシンクブレンドステートとその他のステートを作成
  • 音素に対応する Float パラメタを追加してブレンドステートをセットアップ
    • 後述の uLipSync 側からはこの Float に 0 ~ 1 の値が渡される形になります

uLipSyncAnimator の設定

uLipSync コンポーネントをセットアップした上で、uLipSyncAnimator というコンポーネントをアタッチします。

Animator セクションでは、Find From Children をチェックしている場合はプルダウンリストからコンポーネント以下自身を含む子供にアタッチされた Animator が表示されるので、口パクさせたい GameObject を選択してください。直接指定したい場合は Find From Children のチェックを外して表示される Animator 指定フィールドに対象の Animator をセットしてください。

Parameters セクションは他のコンポーネントと共通です。マイクが反応してほしいボリューム帯(Volume Min/Max)と、口パクの応答速度(Smoothness)を指定します。

そして Animator Contorller Parameters セクションで、先程セットアップした AnimatorController と認識した音素の紐付けを行います。必要な音素分のエントリを作成し、それぞれ Phoneme に uLipSync の Profile で登録した音素を、Parameter には AnimatorController の Float パラメタを指定します。

最後に uLipSync コンポーネントOn Lip Sync Update イベントにこの uLipSyncAnimator.OnLipSyncUpdate を指定すれば完了です。

余談: 開発中に詰まったバグ修正

Animator is not playing an AnimatorController

uLipSyncAnimator では Editor スクリプトAnimator から色々情報を取得しています(プルダウンリストに表示するパラメタ名など)。しかしながら、この情報取得箇所でシーンをセーブしたときなどに Animator is not playing an AnimatorController と表示されます。これは(おそらく)Animator がリセットされることに起因するようなのですが、ワークアラウンドとしては Animator.Rebind() 呼ぶことで切り抜けることができるようです。私は、OnInspectorGUI() 中で次のように Animator が初期化されていなければ Rebind() するようにコードを付け加えました。

var animator = ...;
if (!animator.isInitialized)
{
    animator.Rebind();
}

もし他に良い方法をご存知でしたら教えていただけると助かります。

おわりに

次は動的に uLipSync をセットアップしたいというお話を見かけたのでそちらを色々対応しようと思います。VRM をランタイムロードした場合にどうアタッチするか、またランタイムキャリブレーションをどうするか、もう少し色々な方に使ってもらえるようにそのあたりもまとめたいですね。