見出し画像

Unity初心者が半年でVRChatのクジラワールド制作者になる話

私の所属するラボでは「三宅島クジラ鼻水プロジェクト」という、三宅島にあるカフェ「Cafe691」のオーナーの沖山雄一さんを筆頭に研究者などとともに、三宅島に2018年から突然訪れるようになったクジラの謎を調査をするプロジェクトに関わらせていただいている。

そして当初、別のラボメンバーがClusterで制作中であったクジラワールドを、VRC(VRChat)でも制作しようというところから、私のUnity人生は始まった。(VRC: Whale Research VR

「三宅島クジラ鼻水プロジェクト」のクラウドファンディングページ

Clusterからの移行

Clusterからの移行は同じUnityであるためそこまで大変ではないが、手こずった点がいくつかある。

  • VRC対応のUnityのバージョン以外でVRChat SDKをインポートして動かず(現在はVRChat Creator Companionに移行済み)

  • スポーン地点、Animator付け直し、Colliderなどいくつか直す点があった

  • 音はCluster用のアセットで実装していたようで、再生されず

アイデア出し

この段階で自分の中でアイデアが溢れてきたので、書き起こしてDiscordでメンバーに共有。

手始めに頭に浮かんできたアイデアを書き出した

ワールド・クジラに音を付けたい

1. まず音を鳴らそう

Componentの概念から理解していくレベルだったため、意外と苦戦した。

  1. クジラのGameObjectにAudio Sourceを追加

  2. AudioClipを指定

  3. 3D Sound Settingsから、距離減衰させる

同様に、ワールド内にも距離減衰なしで広範囲に波の音を追加

2. 音を水中とそれ以外で変える

水上はワールドBGM(波の音)、水中はクジラの鳴き声が聞こえるようにしたい。

  1. 水上判定用にBoxColliderをisTriggerにして設置

  2. 1にU# Scriptを追加し、水上にいるか水中にいるかを判別、各種処理

    1. 水中→ クジラの声をMute解除、BGMをMute

    2. 水上→ クジラの声をMute、BGMをMute解除 

public override void OnPlayerTriggerEnter(VRCPlayerApi player)
    {
        for (int i = 0; i < outOfTheWaterAudioSource.Length; i++)
        {
            if (player != Networking.LocalPlayer) return; //LocalPlayer以外はスルー
            outOfTheWaterAudioSource[i].GetComponent<AudioSource>().mute = false;
        }
        for (int i = 0; i < inTheWaterAudioSource.Length; i++)
        {
            if (player != Networking.LocalPlayer) return; //LocalPlayer以外はスルー
            inTheWaterAudioSource[i].GetComponent<AudioSource>().mute = true;
        }
    }

水上判定のColliderに触れた場合のプログラムの一部。Inspectorから指定された要素の数だけMuteを操作する。

Inspectorからオブジェクトを選択可能にした

クジラを泳がせる

ClusterではC#が使えないようで、アニメーションをループさせてクジラを泳がせていた。経路は自動で泳がせたかったため、試行錯誤。

案1. Navigation

最初に思いついたのは、クジラにAIを搭載して経路選択させる方法。Navigationをクジラに追加してできると考えた。

結果: 失敗。泳ぎでは使えず。

Navigationの例
Unity Documentationより)

案2.  特定のオブジェクトを追っかけさせる

では、ある範囲内に生成されたオブジェクトめがけてMoveTowardsでどうだろうか。

結果: 一応成功

1. 追われるオブジェクト側

  1. ランダムに移動させるBoxのGameObjectを作成

  2. Box Colliderを追加、isTriggerに✓

  3. Mesh Rendererを無効化

  4. 1にU# Scriptを追加し、特定の範囲の地点にランダムに移動

  5. 特定のオブジェクトがColliderに入ったら、ランダムに移動(←繰り返し)

追われる担当のBox Colliderくん

2. クジラ側

  1. U# Scriptを追加し、MoveTowardsで上記1のオブジェクトを追いかけさせる

追いかける担当のクジラくん

実際は回転値なども計算して実装しているが、ここでは割愛。

これによって、ランダムに泳ぐクジラが完成したが、問題点はいくつか残った。

  • 直線移動しかしない

  • 調整しているものの次のオブジェクトに向かうときの曲がり方が違和感

将来的にはクジラ側で泳ぎを完結させる必要があると考える。

ブリーチングを実装

クジラのブリーチングはご存知だろうか。

ワールド内のブリーチング

このようにクジラが水上に大きく体を出してバッシャーンのやつだ。これを実装したい。

実際、もともとアニメーション自体は実装されていたが、ランダムに移動しつつ、ランダムなタイミングでブリーチングさせたいと考えた。

そこでAnimatorとU#で実装した。

  1. Animatorでこうする

Animatorの遷移図及び説明

 2. クジラ側で上記青丸用の変数(floatingPara)を作成・一定秒数でランダムな100までの整数を代入

// 前回のフレームからの経過時間を加算
        dt += Time.deltaTime;
        if (dt > reloadTime)
        {
            // 水面よりクジラが下にいた場合
            if (pos.y <= waterSurface)
            {
                dt = 0.0f;
                floatingPara = Random.Range(1, 100);
            }
            // 水面よりクジラが上にいたの場合
            else
            {
                // floatingParaを初期化
                floatingPara = 0;
            }
            //intパラメーターの値を設定する.
            animator.SetInteger("floatingPara", floatingPara);
        }

 3. 特定の条件でFloating 01(これだけ名称が進行形なのは後に修正済み)に分岐

このクジラはfloatingParaが93以上であるとFloatingに移行する

 4. Floatingを繰り返す

 5. 4と同時にクジラ側のU#で水面まで上昇させる(詳細割愛)

// AnimationのタグがJumpingだったら
        if (animator.GetCurrentAnimatorStateInfo(0).IsTag("Jumping") == true)
        {
            // 水面に辿り着いていた場合 
            if (pos.y >= waterSurface)
            {
                animator.SetBool("isJumping", true);
            // 水面に辿り着いていない場合は浮上
            } else
            {
                // Y座標にfloatingDistance加算
                pos.y += FloatingDistanceByOneFrame;
                // 座標設定
                myTransform.position = pos;
            }
        }

 6. 水面に到達したら、クジラ側の変数(isJumpipng)をTrueに

 7. Jump 01を実行した後、Swim 01を実行し通常時に戻る

以上で実装が可能となった。

ボートの変更

この船から↓

初期の船

この遊覧船へ↓

ラボメンバー制作の遊覧船(1)
ラボメンバー制作の遊覧船(2)

Colliderは、「SAColliderBuilder」でBoxCollider群を生成した後に、最終的にはほぼ手作業で調整・作成した。

Colliderたち

クジラマグカップ

持てるモノが欲しかったため、クジラマグカップを実装。

すごくキュートなクジラマグカップ(なにも飲めないけど)

VRC Pickupに少しつまずいた。

  • VRC_PickupはCollider必須

  • OrientationをGripにし、Exact Gripを空のGameObjectで座標指定

VRC Pickupの設定

この2点に気づくのに少し時間がかかった。

アセットで追加した機能

泳ぎ

海の中で泳ぎたい!

SwimSystem 2.0(VRChat水泳ワールドギミック)」で実装。

ミラー

Auto Mirror」で実装。

ボート

[VRCSDK3] ちくわうどんボート」で実装。

デジタル時計

デジタル時計【SDK3のみ対応】」で実装。

ガラス

Fake Glass」で実装。

ペン

QvPen」で実装。

以上、製作者様に感謝申し上げます。

まとめ

Unity?Componentって?というまっさらな状態からでも制作していく内に理解が深まり、加速度的に製作速度が上がっていった。C#(U#)も今まで書いたことがなく初めての言語であったが、Unityと連携がとりやすく比較的書きやすい印象を受けた。

VR自体は黎明期から触れているが、VRCではほぼ遊んできていなかったため(もっぱら単発ゲーム)、新鮮で、流行りまくっているのも納得した。

追加したい機能も、改善の余地も、理解しなければいけない仕様もまだまだあるが、半年でUnityを軽く扱える程度までは成長できたことは嬉しい。

日本語での情報が足りていない部分もあったので、今回このワールドを作るにあたって直面した問題と解決方法の詳細は、今度個別にNote作成して共有したいと思います(いつか)。

最後に

是非みんなワールド、作ってみよう!
そして、今回紹介したワールドにも遊びに来てみてください!

(ワールド投稿は私のラボの担当教員のアカウントをお借りして行っています。)

↑ReadyForのラボメンバー紹介記事

おまけ

三宅島研究調査も同行しました!

↑ 4泊5日で毎日クジラ観察した時に自身が製作した動画


三宅島からの星空


この記事が気に入ったらサポートをしてみませんか?