デジタルな日々

ユニティちゃんでモーションとセリフ紐付けメモ


 

凹みさんのユニティちゃんリップシンクを使わせていただいて、リアルタイムにユニティちゃんに好きなセリフを発声させられるようになったので、

モーションと関連付けてセリフを喋らせられるようにしました。

これで、「このモーションのこのタイミングでこのセリフを喋らせる!」ということができるようになります。

 

とりあえずやってる途中で詰まったこととかを忘れないようにメモっておきます…

 

ユニティちゃんでモーションとセリフ紐付け

 

使い方と仕組み

 

AnimatorStateInfoでモーションの再生位置をとってきて下のスクロールバーに反映、スクロールバーを動かすとモーションもその時間に移動。再生位置が特定のタイミングを通過するとTalkを呼び出して発声します。

発声はUnityChanLipSyncのTalkメソッドを使ってリアルタイムに音声合成を行っています。なので長文の場合若干ラグが生じます。

テキストボックスにセリフを入れて登録ボタンを押すと現在の再生位置とともにセリフデータベースに保存されます。

モーションの管理は「MotionManager.cs」セリフと字幕の管理は「CaptionManager.cs」が行います。

 

 

詰まったこと・今後の課題

 

スライダーとモーション

スライダー動かした判定をどうしようか悩みました。

一応Sliderには「OnValueChanged」がありますが、今回の場合常に変化してるので使えません。なのでUpdateでこんな感じにしました。

(ちなみにスライダーの値は0~1になるように設定してあります。)

 

スライダーの値がnormalizedTimeより0.01以上ずれたらスライダーが動かされたと判定。

でなければ普通にモーションの再生位置をスライダーに反映。

なんか自分的にあんまりしっくり来ませんが現状これで…

 

今回はユニティちゃんロックスターのFBXモーションを借りてきていますが、モーションのインポート設定の[animation]タブで「Bake Into Pose」にすべてチェックを入れています。

 

bake into pose

 

こうすることでモーションによってユニティちゃんのpositionやrotationが変化しなくなります。

例えばユニティちゃんの初期位置を[0,0,0]にしておけばその後どんなにモーションで大暴れしてもpositionは[0,0,0]のままになります。

 

こうしておかないと再生位置変更した場合に、変更前の場所を基準にユニティちゃんが動いてしまいます。

 

 

字幕の表示と管理

字幕のリスト表示はテラシュールブログさんの記事を参考にさせていただきました。

UnityのuGUIでスクロールビューを作る – テラシュールブログ

【Unity】各要素の高さが変動するスクロールビューを使ってチャットっぽい何かを作る – テラシュールブログ

現状どこでもanimatorStateInfoのnormalizedTimeを使っていますが、使う分にはちょっとわかりづらいので表示だけ分秒表示に直そうかなーと考えています。

 

Nodeのレイアウトはこんな感じにするといい感じに

Nodeのレイアウト設定親のNodeオブジェクト

Timeのレイアウト設定左側に表示する時間

Sentenceのレイアウト設定右側に表示するセリフ

 

 

 

字幕リストの管理は並べ替えとか色々便利そうだったので、C#の「List<T>」を使いました。

List型の使い方 → List型 – C#プチリファレンス

(使うときは「using System.Collections.Generic;」を忘れずに…)

更に今回は字幕の表示される時間( float )とセリフの内容( string )を管理したいので自作クラス「Caption()」を作ってこれをList型に入れました。

 

 

ちなみにこうやってクラスだけ使いたーいって場合はGameObjectにくっつけてヒエラルキービューに出さなくても大丈夫っぽいです。

アセットにスクリプトが入ってさえいれば、ほかのクラスからそのまま宣言できます。

 

 

foreachも使えてスッキリ

(このへんGetComponentsInChildrenで検索する順番が変わらないこと前提なのであまり良くない気がします…)

 

これを時間でソートしたいって時はこんな感じで

 

 

ラムダ式とデリゲートってなんぞや… って感じでしたが、

要は「メソッドを引数にしたいけどわざわざメソッド書くのダル〜」って思ったから「文字数少なく書けるようにしちゃお!」って感じっぽいです(適当)

 

ラムダ式の方がデリゲートよりも簡略化されちゃってるイメージです。

C# – 【LINQの前に】ラムダ式?デリゲート?Func_T, TResult_?な人へのまとめ【知ってほしい】 – Qiita

 

Listに自作クラスを使うときの参考

C# List_T_ 任意の項目を削除・抽出・変換する

 

 

次の字幕ポイントをListから検索するとき

 

 

色々試行錯誤したダメコメントアウトがたくさんあってすみません、まだ模索中なので残してあります…

 

方法1

「FindIndex()」を使って条件のメソッドに一致するものを返してくれます。ですが単数なので1つ発見したら返しておしまいです。インデックス順に検索してくれるのなら、あらかじめ時間順にソートしているので問題ないのですが…

検索順が不確かなのでちょっと不安です(現状うまく動いてくれていますが)

 

方法2

ボツ、理由:重すぎる でもソートしなくても大丈夫

 

方法3

シンプル イズ ベスト

軽くて確実だがやる前にソートしておく必要あり

 

 

データのセーブ・ロード

字幕データのセーブとロードにはUnityで用意されている「PlayerPref」を利用しました。

Unity – Scripting API_ PlayerPref

「PlayerPref.SetInt()」で数値をセーブ、「PlayerPref.GetInt()」で数値をロードできるスグレモノです。

ですが「flaot」「Int」「string」しか使えません。

なんと!!!

配列使いたいのに!!

 

そこでArrayPref2を利用します。

ArrayPref2はUnify Community wikiにあるスクリプトで無料で使えます。

ArrayPrefs2 – Unify Community Wiki

リンク先下部の「JavaScript – PlayerPrefsX.js」か「C# – PlayerPrefsX.cs」内のコードを丸々コピペしたスクリプトを作ってアセット内の入れておけば使えるようになります。

 

「PlayerPrefX.SetIntArray()」「PlayerPrefX.GetIntArray()」のように使えます。

これコードをちらっと見てみるとデータをうまいことstringに変換して元々のPlayerPrefを使ってるんですね、すげえ

 

なのでデータの保存先も同じ

保存先は先ほどのここ

Unity – Scripting API_ PlayerPref

に載っています。

 

Macで「/Library/Preferences」にねえ!って思ったら

「/Users/”user name”/Library/Preferences」にありました

そりゃそうだよね、ユーザでセーブデータ別にしないとだもんね

ちなみにxmlで書かれているので内容も若干読める

 

ちなみにList型と配列は似ているけれど別物です!!

なのでいったん変換してから保存…

 

 

ロードしたらList型に変換…

 

 

ちょっと手間がかかりますがなんとかこれで正常に動きました。

 

 

 

とりあえず現時点でこんな感じです。

ところで実はまだデータを消去するボタンがありません…

やばい!

ユニティちゃんライセンス

このコンテンツは、『ユニティちゃんライセンス』で提供されています

コメントを残す