BlenderとUnityで作ったものをVR(Meta Quest/Oculus Link)で動かします

ブログの女の子を作る #34 NavMeshで巡回してモーションする2(外を歩く)【Unity】

 
前回の記事では、「NavMesh」でキャラクターを巡回して、目的地でいろんなモーションをしてみました。
 
今回は「家の外」、池の周りを散歩してみたいと思います。目的地を順番通りに歩いていくのがポイントですね。
 
ついでに、3つほどモーションも追加します。いろんなところでモーションしてるので、だいぶんカオスな感じになってきましたよ。。。



(1) 開発環境

動作環境

・Windows 10
・Unity 2019.4.0f1
・Intel Corei7 3770
・ASUS ROG-STRIX-RTX2060S-O8G-GAMING
・Meta Quest
・Oculus Link
 
前回から変わったのは、Unityのバージョンです。
 

「Unity 2019 LTS」にアップグレードした

今回、「2018 LTS(2018.4.1f1)」から「2019 LTS(2019.4.0f1)」にアップグレードしました。
 
アップグレード直後は、使ってるアセットでスクリプトエラーが出たり、Magica Clothの設定が一部無くなったりで大変でしたが、なんとか動くようになりました。
 
とりあえず良さそうなのは、この辺り。
・EditorのUIが変わり、動作が軽くなった気がする
・「Enter Play Mode Options (Experimental)」オンで「Playボタン」クリック後の起動が爆速に
 
なかなか快適ですね。
 

スクリプトについて

前回作成したスクリプト「AgentMotion.cs」と「Define.cs」を修正しました。「InitCharacter.cs」は修正なしです。
 
この記事では差分だけを掲載してますので、全量は前回の記事をご覧くださいね。
 

(2) 目的地を設定する

まずは、池の周りを歩くための「目的地」を追加します。
 

目的地にオブジェクトを置く

基本的にNavMeshは、障害物などが無ければまっすぐに進みますので、池の四隅に目的地を置くのが良さそうです。
 
今回はこの辺りに置いてみました。
 
前回と同様にCubeを置いてます。
いつもは非表示にしてますよ。
 

目的地の位置情報を設定する

目的地が増えましたので、キャラクターのInspectorにある「Motion Points」で Size の数を増やし、目的地のオブジェクトを設定します。
 
Element 7から9は「モーション用のオブジェクト」、10から19は「散歩用のオブジェクト」です。
 
散歩には2つのルートがあり、池の時計回りは「Sanpo1、Sanpo2、bench(9)、Sanpo3、Sanpo4」、池の反時計回りは「Sanpo4、Sanpo3、bench(8)、Sanpo2、Sanpo1」のオブジェクトを経由するように設定してます。できるだけ前回作ったスクリプトを流用したかったので、こんな感じになりました。
 
 
目的地の数が20になりましたが、この方法で目的地を管理するのは、これくらい限界かも知れませんね。もっとたくさんの目的地がある場合は、スクリプト側で「目的地のオブジェクトを動的に読み込む」などが良い気がします。
 
今回はこれでいきましょう。
 


(3) モーションを追加する

追加する3つのモーションを設定します。
 

Animator Controllerに追加する

前回作成したAnimator Controller「Junkai」に、StateとTransitionを追加していきます。
Stateは「Shagamu2、CanCan、DancingRotations」の3つを追加してます。
 
ここで、Conditionsの「motion」値が重複しないよう、別の値にしておきます。重複してると、正しくモーションしませんので。
 

(4) スクリプトを修正する

キャラクターの目的地やモーションを管理するスクリプト「AgentMotion.cs」と定数を管理する「Define.cs」を修正します。
 

抽選リストの目的地を追加する

ここでは、2つのメソッドを修正します。
 
[AgentMotion.cs]
// 場所の抽選リスト作成
void CreateLotMotionsList()
{
    // 場所の番号
    int[] basho = {
                   (int)PointIndex.Jump,
                   (int)PointIndex.Shagamu,
                   (int)PointIndex.Yoga,
                   (int)PointIndex.Ladder,
                   (int)PointIndex.Breakdance,
                   (int)PointIndex.Bench,
                   (int)PointIndex.Bed,
                   (int)PointIndex.Shagamu2,
                   (int)PointIndex.CanCan,
                   (int)PointIndex.DancingRotations,
                   (int)PointIndex.SanpoL_1,
                   (int)PointIndex.SanpoR_4
    };

    // 場所の重み
    int[] omomi = {
                    10,     // ジャンプ
                    15,     // しゃがむ
                    15,     // ヨガ
                     5,     // はしご
                    10,     // ブレイクダンス
                     5,     // ベンチ
                     3,     // ベッド
                     5,     // しゃがむ2
                     5,     // CanCanダンス
                     5,     // くるくる回る
                     3,     // 散歩(左)
                     3      // 散歩(右)
                };

    for (int i = 0; i < basho.Length; i++)
    {
        for (int j = 0; j < omomi[i]; j++)
        {
            lotPointsList.Add(basho[i]);
        }
    }
}
82から86行目で今回追加した「目的地の場所」、98から102行目で「場所の重み(頻度の高さ)」を設定してます。
 
 
定数値として、PointIndex のenum値 も追加しました。
 
[Define.cs]
// 目的地の値
public enum PointIndex
{
    Jump,
    Shagamu,
    Yoga,
    Ladder,
    Breakdance,
    Bench,
    Bed,
    Shagamu2,
    CanCan,
    DancingRotations,

    SanpoL_1,
    SanpoL_2,
    SanpoL_B,
    SanpoL_3,
    SanpoL_4,

    SanpoR_4,
    SanpoR_3,
    SanpoR_B,
    SanpoR_2,
    SanpoR_1
}
 

目的地で実行するモーションを追加する

3つのメソッドを修正します。
 
[AgentMotion.cs]
// モーション実行と次の目的地を設定
void SetMotionAndNextDest()
{
    switch (destPoint)
    {
        // ジャンプ
        case (int)PointIndex.Jump:
            SetAnimNavOff(MotionIndex.Jump);
            SetDestByLot();
            break;

        // しゃがむ
        case (int)PointIndex.Shagamu:
            SetAnimNavOff(MotionIndex.Shagamu);
            SetDestByLot();
            break;

        // ヨガ
        case (int)PointIndex.Yoga:
            MotionYoga();
            SetDestByLot();
            break;

        // はしご
        case (int)PointIndex.Ladder:
            SetAnimNavOff(MotionIndex.Ladder);
            SetDestByLot();
            break;

        // ブレイクダンス
        case (int)PointIndex.Breakdance:
            SetAnimNavOn(MotionIndex.Breakdance);
            SetDestByLot();
            break;

        // ベンチに座る
        case (int)PointIndex.Bench:
            MotionBench();
            SetDestByLot();
            break;

        // ベッドで寝る
        case (int)PointIndex.Bed:
            // 正面をゆっくり向く
            IEnumerator mukuShomen = MukuShomen();
            StartCoroutine(mukuShomen);

            SetAnimNavOn(MotionIndex.Bed);
            SetDestByLot();
            break;

        // しゃがむ2
        case (int)PointIndex.Shagamu2:
            SetAnimNavOff(MotionIndex.Shagamu2);
            SetDestByLot();
            break;

        // CanCanダンス
        case (int)PointIndex.CanCan:
            SetAnimNavOff(MotionIndex.CanCan);
            SetDestByLot();
            break;

        // くるくる回る
        case (int)PointIndex.DancingRotations:
            SetAnimNavOff(MotionIndex.DancingRotations);
            SetDestByLot();
            break;

        // -------------------------
        // 散歩(左)
        // -------------------------
        case (int)PointIndex.SanpoL_1:
            SetDestByNext(); break;

        case (int)PointIndex.SanpoL_2:
            SetDestByNext(); break;

        case (int)PointIndex.SanpoL_B:
            MotionBench();
            SetDestByNext(); break;

        case (int)PointIndex.SanpoL_3:
            SetDestByNext(); break;

        case (int)PointIndex.SanpoL_4:
            SetDestByLot(); break;

        // -------------------------
        // 散歩(右)
        // -------------------------
        case (int)PointIndex.SanpoR_4:
            SetDestByNext(); break;

        case (int)PointIndex.SanpoR_3:
            SetDestByNext(); break;

        case (int)PointIndex.SanpoR_B:
            MotionBench();
            SetDestByNext(); break;

        case (int)PointIndex.SanpoR_2:
            SetDestByNext(); break;

        case (int)PointIndex.SanpoR_1:
            SetDestByLot(); break;
    }
}
165から181行目で「今回追加したモーション」の設定、183から219行目でそれぞれ「時計回り」と「反時計回り」に池を散歩しつつ、途中でベンチに座るモーションを設定しています。
 
[AgentMotion.cs]
// 一つ次の場所を目的地に設定
void SetDestByNext()
{
    destPoint++;
    agent.destination = motionPoints[destPoint].position;
}
池の周りを移動する時は、抽選ではなく「今の目的地 +1」の場所に進めば良いですね。「destPoint++」で変数値に 1 を加算して、次の目的地を設定するだけのメソッドを作成しました。
 
 
定数値として、MotionIndex のenum値 も追加しました。
 
[Define.cs]
// animation切り替え用のパラメータ値
public enum MotionIndex
{
    Walk = 0,
    Jump = 10,
    Shagamu = 20,
    Shagamu2 = 21,
    Yoga1 = 30,
    Yoga2 = 31,
    Yoga3 = 32,
    Yoga4 = 33,
    Yoga5 = 34,
    Yoga6 = 35,
    Ladder = 40,
    Breakdance = 50,
    CanCan = 51,
    DancingRotations = 52,
    Bench = 60,
    Bed = 70
}
 

目的地付近で位置をずらす

最後に、目的地付近で位置をずらすロジックも追加しておきます。
 
[AgentMotion.cs]
// 目的地をランダムに設定
void SetDestByLot()
{
    destPoint = lotPointsList[Random.Range(0, lotPointsList.Count)];
    Vector3 basho = motionPoints[destPoint].position;

    // 目的地付近で位置をずらす
    switch (destPoint)
    {
        // ジャンプ
        case (int)PointIndex.Jump:
            basho += OffsetPosition(2.0f); break;
        // しゃがむ
        case (int)PointIndex.Shagamu:
            basho += OffsetPosition(2.0f); break;
        // ヨガ
        case (int)PointIndex.Yoga:
            basho += OffsetPosition(2.0f); break;
        // ブレイクダンス
        case (int)PointIndex.Breakdance:
            basho += OffsetPosition(2.0f); break;
        // CanCanダンス
        case (int)PointIndex.CanCan:
            basho += OffsetPosition(1.5f); break;
        // くるくる回る
        case (int)PointIndex.DancingRotations:
            basho += OffsetPosition(1.5f); break;
    }
    agent.destination = basho;
}
あまり左右に動かないタイプのモーションの場合、目的地をずらしておくとキャラクターの重なり軽減になります。モーション中もキャラ同士の当たり判定があると良いんですけどね。
 
switch文が長くなったりで、ベタなロジックではありますが、修正量が少なかったので良しとしましょう。
 
これですべての修正が完了しました!
 


(5) 動画で確認してみる

今回作成したスクリプトを動かした時の動画です。
 
・目的地ごとの動画3本(モーションの確率は変えてます)
・池を時計回りに散歩する動画1本
 

しゃがむモーション2

モーション名:Centroid-Sitting On The Floor
たそがれつつ体育座りしてる感じです。
 

池の周りを散歩する

キャラを追いかけるのが面倒なので、キャラクタ自体にカメラを設定してみました。自撮り棒で撮ったような動画になりましたよ。
 

まとめ 

キャラクターにモーションを追加して、「NavMesh」で池の周りを散歩してみました。
 
これでやっと、エリア内を動き回っているキャラを眺めることができるようになりました!
NavMeshは便利ですね。
 
スポンサーリンク

コメント

  • トラックバックは利用できません。

  • コメント (0)

  1. この記事へのコメントはありません。

ブログの女の子を作る #33 NavMeshで巡回してモーションする【Unity】

ブログの女の子を作る #35 パーティクルとコライダーの風魔法を当てる【前編】【Unity】


最近のコメント

だーしゅ
IT関係のお仕事してます。
3Dモデルの女の子は「ブログノ・スージー」。VRは楽しいですね。

[当ブログについて]