
今回はUnityでVRoidの代3弾として
「UnityでVRoidで作ったモデルを振り向かせる」
を実装した動画を作ってみました。
「UnityでVRoidで作ったモデルを振り向かせる」
を実装した動画を作ってみました。

動画自体は2週間前に上げてたんだけど
土日も作業していたためここへの公開が遅くなってしまったわね……
土日も作業していたためここへの公開が遅くなってしまったわね……

本当はここでこの処理はこうしてるよー
みたいな解説を入れていきたいんだけど、まだそれを作る時間がないから
軽く解説するぐらいしかできないな……
みたいな解説を入れていきたいんだけど、まだそれを作る時間がないから
軽く解説するぐらいしかできないな……

プログラム解説できなくて本当にすみません……
続きを読むで、ざっとコード解説も入れてみましたので
良ければ見ていってください
続きを読むで、ざっとコード解説も入れてみましたので
良ければ見ていってください
もくじ
Youtubeでの解説

解説動画と言ってますが
コンセプト的には出来上がっていくものを眺めて楽しむのを目指しています
(シティーズスカイラインズのにょきにょきを眺める間隔に近いかも)
コンセプト的には出来上がっていくものを眺めて楽しむのを目指しています
(シティーズスカイラインズのにょきにょきを眺める間隔に近いかも)

動画ではこんなことできますよーっていう紹介程度にして
GitHubやこのブログを使って解説できたらいいなって考えてるわ
GitHubやこのブログを使って解説できたらいいなって考えてるわ

動画作るのは結構かんたんにできるんだけどな、
プログラムの様子を録画してそれにゆっくりボイス入れるだけだし
プログラムの様子を録画してそれにゆっくりボイス入れるだけだし

ただ、プログラムを深く解説しようとすると
その時の冬月の感情とかも読み取らないとわからない書き方しているときが有るんですよね……
その時の冬月の感情とかも読み取らないとわからない書き方しているときが有るんですよね……
プログラム解説していくよ
GitHubはこちら
https://github.com/Syungetu/UnitytoVRMProject

解説はいらん!コードを見れ場わかる
といった兄貴姉御は上のリンクで
Unityプロジェクトがダウンロードできますのでご活用ください
といった兄貴姉御は上のリンクで
Unityプロジェクトがダウンロードできますのでご活用ください
振り向き処理解説
変数宣言
[Header("振り向きフラグ(AnimatorのIK Passをオンにしておく)")]
/// <summary> 振り向きをさせるかどうか </summary>
public bool _IsLookAt = false;
/// <summary> 振り向くオブジェクト(ターゲット) </summary>
public GameObject _LookAtObj = null;
/// <summary> 振り向く座標(ターゲット) </summary>
public Vector3? _LookAtPos = null;
/// <summary> 振り向くときの強さ(Animationの強さとの兼ね合い) </summary>
public float _LookAtWeight = 0.5f;

まずは変数宣言の部分から解説していくわね
まあ、コメント書いて有るからしなくても良かったかしらね……
/// <summary> 振り向きをさせるかどうか </summary> public bool _IsLookAt = false;

この部分は振り向き処理をさせるかどうかのフラグだな
ここをTrueにすれば振り向き処理が実行されるようにするよ
ここをTrueにすれば振り向き処理が実行されるようにするよ

ここは外部から操作することを前提と考えてますので
Public変数で宣言しています
Public変数で宣言しています
/// <summary> 振り向くオブジェクト(ターゲット) </summary>
public GameObject _LookAtObj = null;
/// <summary> 振り向く座標(ターゲット) </summary>
public Vector3? _LookAtPos = null;

この2つの変数は、変数型こそ違えど
やっていることは同じなのでまとめて解説します
やっていることは同じなのでまとめて解説します

ここは振り向くターゲットを設定する変数になるわ
_LookAtObjはオブジェクトの座標を取得するためにGameObjectで宣言しているわ
_LookAtObjはオブジェクトの座標を取得するためにGameObjectで宣言しているわ

オブジェクト以外に設定したいときもあると思うので、
_LookAtPosはVector3の3軸を受け取れるようにしてるぞ
_LookAtPosはVector3の3軸を受け取れるようにしてるぞ

Vector3の型の後ろに?がついているのはNull型を許容するためです
これをすると「_LookAtPos = null」にしてもエラーにならなくなります
これをすると「_LookAtPos = null」にしてもエラーにならなくなります

なぜこれをするかというと、座標に値を指定しなかったという条件を作りたかったからです。x=0.0f y=0.0f z=0.0f では値を指定してないのか、それともその座標を指定しているのかがわからないからです
/// <summary> 振り向くときの強さ(Animationの強さとの兼ね合い) </summary>
public float _LookAtWeight = 0.5f;

この変数は振り向く強さを設定するわ
この値が1だと指定された座標に振り向くようになるけど
ボーンを無視した動きに鳴るので、リアルでは無くなるわ
この値が1だと指定された座標に振り向くようになるけど
ボーンを無視した動きに鳴るので、リアルでは無くなるわ

真後ろに目標があった場合に人間は真後ろに頭を向けることができないので
それを表現するために振り向く制限をつけるんだな
それを表現するために振り向く制限をつけるんだな

今回はデフォルトの0.5fを入れていますが
首の可動域が広いキャラなどはここの数値を上げるとソレが表現できます
首の可動域が広いキャラなどはここの数値を上げるとソレが表現できます
振り向きたいモデルのAnimatorがあるオブジェクトにコードをセットします
[Header("振り向き処理(Animatorがアタッチされているオブジェクトにしか使えません)")]
/// <summary> モデルにアタッチされているAnimator </summary>
public Animator _ModelAnimator;

この振り向き処理は歩きアニメーションや待機アニメーションなどと同時に行いたいため、アニメーションをしたあとに振り向きのモーションを組み込みます
そのため、AnimatorのアタッチしたGameObjectで動かす必要があります
そのため、AnimatorのアタッチしたGameObjectで動かす必要があります

上でなんでAnimatorを取得しているかというと、
単純に冬月がGetComponent()を使いたくないだけっていう話だったりするわ
単純に冬月がGetComponent()を使いたくないだけっていう話だったりするわ

GetComponent()が重いっていう話もちらほらあるけど
冬月は単純に使いたくないそれだけみたい、理由は特にないみたいだ
冬月は単純に使いたくないそれだけみたい、理由は特にないみたいだ

言い訳としてはインスペクターで設定したほうが
もしエラーが起きた場合アタッチしてなかったのが、それとも設定してなkったのかが分かりやすくなるからっていうものもありますね
もしエラーが起きた場合アタッチしてなかったのが、それとも設定してなkったのかが分かりやすくなるからっていうものもありますね
Animatorの処理の後に動かすように
OnAnimatorIK(int layerIndex)を呼び出す
/// <summary>
/// AnimatorIKを使ってボーンを操作する処理
/// </summary>
/// <param name="layerIndex">レイヤー番号</param>
private void OnAnimatorIK(int layerIndex)
{
SetLookAt();
}

前に行っていたAnimatorの処理の後に振り向き処理を起こしたいので
OnAnimatorIK(int layerIndex)を使うことにします
OnAnimatorIK(int layerIndex)を使うことにします

AnimatorをアタッチしているGameObjectだとこの
OnAnimatorIK(int layerIndex)がAnimatorの処理の後に呼び出されるわ
OnAnimatorIK(int layerIndex)がAnimatorの処理の後に呼び出されるわ

OnAnimatorIK(int layerIndex)内に呼び出されている
SetLookAt();
が振り向き処理が書かれている関数になるぞ
SetLookAt();
が振り向き処理が書かれている関数になるぞ

本来ならここはAnimatorのLayerごとに処理を分けて複数のモーションを合成するような使い方をするんでしょうかね?
振り向き処理のリセット
if (_IsLookAt == false)
{
// 振り向き処理をしないときは処理を抜ける
// 各ボーンに対しての動く重みを初期化する
_ModelAnimator.SetLookAtWeight(
0.0f, // 全体の重み
0.0f, // 上半身を動かす重み
0.0f, // 頭を動かす重み
0.0f, // 目を動かす重み
0.0f // モーションの制限量(後で数値を入れる)
);
return;
}

まずは振り向き処理をしないときの処理を書きました
最初に使用するかどうかのフラグを書いておけば
使用しないときの処理のパフォーマンスが若干上がると思います
最初に使用するかどうかのフラグを書いておけば
使用しないときの処理のパフォーマンスが若干上がると思います

_ModelAnimator.SetLookAtWeight()
が突然出てきたけど、これが振り向きの重みを調整する処理を行う関数になるわ
が突然出てきたけど、これが振り向きの重みを調整する処理を行う関数になるわ

この関数はかなり有能な関数で
各部位に振り向く強さを調整することができるぞ
各部位に振り向く強さを調整することができるぞ

今回は振り向き処理を解除する目的なので
すべての値を0にして設定しています
すべての値を0にして設定しています
振り向き強度の設定
if(_LookAtObj != null)
{
// 指定されたオブジェクトが入っていたらそこから座標を取得する
_LookAtPos = (Vector3)_LookAtObj.transform.position;
}
if(_LookAtPos == null)
{
// 座標が入力されていないので、目標地点がないので処理しない
// 各ボーンに対しての動く重みを初期化する
_ModelAnimator.SetLookAtWeight(
0.0f, // 全体の重み
0.0f, // 上半身を動かす重み
0.0f, // 頭を動かす重み
0.0f, // 目を動かす重み
0.0f // モーションの制限量(後で数値を入れる)
);
return;
}

この処理では振り向き座標を設定します
_LookAtObjにオブジェクトが登録されていればtransformから座標を取得して行きます
_LookAtObjにオブジェクトが登録されていればtransformから座標を取得して行きます

で_LookAtObjにオブジェクトが登録されていなかった場合は
_LookAtPosの値を見に行くわ
_LookAtPosに値がない場合は振り向く座標がないと判断して振り向かないわ
_LookAtPosの値を見に行くわ
_LookAtPosに値がない場合は振り向く座標がないと判断して振り向かないわ

振り向かないときの処理は、さっきの処理の
_IsLookAt = false だった場合と同じ処理で振り向かないようにしているぞ
_IsLookAt = false だった場合と同じ処理で振り向かないようにしているぞ

ここでVector3?にしたときの利点が活かせます
_LookAtPos = null
ならば値無しとして判断できます
_LookAtPos = null
ならば値無しとして判断できます
実際の振り向き処理
// 各ボーンに対しての動く重みを設定する
_ModelAnimator.SetLookAtWeight(
1.0f * _LookAtWeight, // 全体の重み
0.5f * _LookAtWeight, // 上半身を動かす重み
0.8f * _LookAtWeight, // 頭を動かす重み
1.0f * _LookAtWeight, // 目を動かす重み
0.0f // モーションの制限量(後で数値を入れる)
);
// 振り向く位置を設定する
_ModelAnimator.SetLookAtPosition((Vector3)_LookAtPos);

はい、ようやく振り向きの処理に入ります!
まずは各部位を曲げられるように重みを付けます
頭から体にかけて重みを下げていくと自然な振り向きになります
まずは各部位を曲げられるように重みを付けます
頭から体にかけて重みを下げていくと自然な振り向きになります

上のプログラムのように重み自体は固定な値にして
変数(_LookAtWeight)で全体的な重みを掛けて計算するようにすると
振り向きの度合いの設定がやりやすくなるわ
変数(_LookAtWeight)で全体的な重みを掛けて計算するようにすると
振り向きの度合いの設定がやりやすくなるわ

振り向きが鈍いキャラを表現にするときは
_LookAtWeightの値を0に近い数字にすると表現できるぞ
逆にフクロウのように振り向き度合いが大きくするば場合は1に近い値にすれば表現できるぞ
_LookAtWeightの値を0に近い数字にすると表現できるぞ
逆にフクロウのように振り向き度合いが大きくするば場合は1に近い値にすれば表現できるぞ

そして、先程の項目で取り出した振り向き座標を
_ModelAnimator.SetLookAtPosition((Vector3)_LookAtPos);
に入れればその方向を振り向くようになります
_ModelAnimator.SetLookAtPosition((Vector3)_LookAtPos);
に入れればその方向を振り向くようになります
まとめ

ということで、これでアドベンチャーパートで
キャラクター同士で向き合ったり、首振りを行うことができるようになりました
キャラクター同士で向き合ったり、首振りを行うことができるようになりました

対象に振り向くだけじゃなくて
イヤイヤ時の首振りなんかもこれでできそうだね
イヤイヤ時の首振りなんかもこれでできそうだね

前回ご紹介した目パチ、口パクを組み合わせて
一歩進んだ会話パートの実装もできるな
一歩進んだ会話パートの実装もできるな

キャラクター周りの演出アップですが
こういう演出ができるとよりゲームがクオリティアップするかと思いますよ!
こういう演出ができるとよりゲームがクオリティアップするかと思いますよ!