Unityプログラム講座 モデルの首を任意に動かして振り向かせてみた

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

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

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

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

Youtubeでの解説

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

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

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

ただ、プログラムを深く解説しようとすると
その時の冬月の感情とかも読み取らないとわからない書き方しているときが有るんですよね……

プログラム解説していくよ

GitHubはこちら

https://github.com/Syungetu/UnitytoVRMProject

解説はいらん!コードを見れ場わかる
といった兄貴姉御は上のリンクで
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にすれば振り向き処理が実行されるようにするよ

ここは外部から操作することを前提と考えてますので
Public変数で宣言しています

/// <summary> 振り向くオブジェクト(ターゲット) </summary> 
public GameObject _LookAtObj = null;

/// <summary> 振り向く座標(ターゲット) </summary> 
public Vector3? _LookAtPos = null;

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

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

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

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

なぜこれをするかというと、座標に値を指定しなかったという条件を作りたかったからです。x=0.0f y=0.0f z=0.0f では値を指定してないのか、それともその座標を指定しているのかがわからないからです

/// <summary> 振り向くときの強さ(Animationの強さとの兼ね合い) </summary>
public float _LookAtWeight = 0.5f;

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

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

今回はデフォルトの0.5fを入れていますが
首の可動域が広いキャラなどはここの数値を上げるとソレが表現できます

 

振り向きたいモデルのAnimatorがあるオブジェクトにコードをセットします
 [Header("振り向き処理(Animatorがアタッチされているオブジェクトにしか使えません)")]
/// <summary> モデルにアタッチされているAnimator </summary>
public Animator _ModelAnimator;

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

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

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

言い訳としてはインスペクターで設定したほうが
もしエラーが起きた場合アタッチしてなかったのが、それとも設定してなkったのかが分かりやすくなるからっていうものもありますね

Animatorの処理の後に動かすように
OnAnimatorIK(int layerIndex)を呼び出す

/// <summary>
/// AnimatorIKを使ってボーンを操作する処理
/// </summary>
/// <param name="layerIndex">レイヤー番号</param>
private void OnAnimatorIK(int layerIndex)
{
    SetLookAt();
}

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

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

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

本来ならここはAnimatorのLayerごとに処理を分けて複数のモーションを合成するような使い方をするんでしょうかね?

振り向き処理のリセット
if (_IsLookAt == false)
{
    // 振り向き処理をしないときは処理を抜ける
    // 各ボーンに対しての動く重みを初期化する
    _ModelAnimator.SetLookAtWeight(
        0.0f, // 全体の重み
        0.0f, // 上半身を動かす重み
        0.0f, // 頭を動かす重み
        0.0f, // 目を動かす重み
        0.0f // モーションの制限量(後で数値を入れる)
    );
    return;
}

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

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

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

今回は振り向き処理を解除する目的なので
すべての値を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にオブジェクトが登録されていなかった場合は
_LookAtPosの値を見に行くわ
_LookAtPosに値がない場合は振り向く座標がないと判断して振り向かないわ

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

ここでVector3?にしたときの利点が活かせます
_LookAtPos = null
ならば値無しとして判断できます

実際の振り向き処理
// 各ボーンに対しての動く重みを設定する
_ModelAnimator.SetLookAtWeight(
     1.0f * _LookAtWeight, // 全体の重み
     0.5f * _LookAtWeight, // 上半身を動かす重み
     0.8f * _LookAtWeight, // 頭を動かす重み
     1.0f * _LookAtWeight, // 目を動かす重み
     0.0f // モーションの制限量(後で数値を入れる)
);
// 振り向く位置を設定する
_ModelAnimator.SetLookAtPosition((Vector3)_LookAtPos);

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

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

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

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

まとめ

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

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

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

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

気軽にコメントをどうぞ!

この記事に関すること冬月に聞きたいこと等、小さいことでもコメントしていただける嬉しいです。冬月に直接連絡したい方は下のお問合せフォームをお使いください。(メール送信されます)

内容に問題なければ、下記の「コメントを送信する」ボタンを押してください。※メールアドレスは公開されることは有りません。


The reCAPTCHA verification period has expired. Please reload the page.

Unity | ゲーム製作の関連記事