見出し画像

Unityやってみる07:【長文注意】オブジェクトの動的生成をやってみる

 暑いよう、暑いよう、うーん、うーん。

 あっ! こんにちは、Unity素人おじさんです。ただしプログラミング歴は結構長いです。

 今回数年ぶりに、Unityをゼロから学びなおしているのですけど、想像していた以上に順調で、自分自身驚愕しています。前回は、「モンスター育成ゲーム」のシーンと、モンスターを生成するための、ボタンを設置しましたね。

 で、今回は実際にモンスターを、生成するまでをやってみることにします。

追記:
「オブジェクトの動的生成」、という言葉について、何も説明してなかったことに気づきました、すみません💦

ゲームを起動した時とか、あるシーンを生成した時に、その「世界」の生成と同時に、オブジェクトを生成するのが「静的生成」。

対して、ゲーム起動後に、あるいはシーン生成後に、タイマーによる管理とか、プレイヤーによるアクションとかをきっかけに、その世界に新しい物体を出現させるのが、「動的生成」です。

今回のサンプルは、プレイヤーのボタン押下(おうか)をきっかけに、オブジェクトを生成しますので、動的生成にあたります。

なぜ私が「動的生成」にこだわったのかというと、「動的生成」って結構、メモリの使い方に影響を与えたり、影響されたりするので、作成可能なゲームとかプログラムの内容に、大きな影響を与えてしまうのですよね。なので、真っ先にそれを確認したい、というのがあります。

今回のサンプルでわかったこと。

・前回で報告させていただいたように、Unityでは、UI系と3D系という、2つのインターフェースがあり、それぞれで設計思想が微妙に異なっていたりして、ちょっと癖がある。

・でもその癖さえ理解すれば、「今やりたいこの処理を、どうやれば実現できるか」を、類推するのはそれほど難しいことではない。さらに、BingのチャットAIさんをパートナーにできれば、Unity独特なクセはあまり気にすることなく、正解へのルートをたどっていくことは可能。

・それ(癖の理解と、チャットAIの活用)を前提にして判断するなら、Unityの「動的なオブジェクト生成」の扱いは、さほどトリッキーなものではないし、そのやり方を理解して突破できたら、PCのリソースを有効に利用した性能を享受することが可能(と思われる)。ちょっと癖のあるインターフェースを、理解して「突破」するメリットは十分にある。

 現在のゲーム画面は、こんな感じですね。

 モンスターを降らせるシャワーを、どうしようかなと考えてたのですけど、上空にモンスターの巣を設置して、そこからモンスターを落下させる、という簡単な方法に、変更することにしますね。

 上空に、モンスターの巣を設置してみます。サイズ的には、高さ5メートルくらいの位置に、10メートルx10メートルの、正方形の板を置いてみます。

 やってみました。ちょっと大きすぎですね。5メートルx5メートルに変更します。色も付けてみましょう。

 やってみました。まあまあいい感じですね。

 カメラの位置と角度を調整しました。ゲームビューはこんな感じになりました。影がちょっとわずらわしいですが、慣れれば大丈夫です、このまま続けます。

 で、ここからが今日の最大の難関です。「Generate Monsters」という赤いボタンを押したら、紫の「モンスターの巣」の下に、黒いモンスター(立方体)が出現し、地面まで落下する、という処理を実現します。

 最近私は、Unityエディターを起動すると同時に、Microsoft Edge(ブラウザ)を立ち上げて、BingというサイトのチャットAIとの会話を開始するようにしています。で、チャットAIさんに、こんな質問をしてみました。

「3Dオブジェクト(モンスター生成ボタン)がクリックされたら、あるオブジェクト(モンスターの巣)の1メートル下に、モンスターがランダムな位置に生成されるようにしたいのですが、その実現のためには、モンスター生成ボタンのIsTriggerをOnにして、スクリプトを実行させて、プレハブで用意しておいた、モンスターのオブジェクトをインスタンス化すればいいですか?」

 知らんけど(笑。

 いや、これまでのUnity学習の経験から、「たぶんこういうことなんだろうなあ」、と、適当にでっちあげて、質問としてぶっこんで見ました。

 Unityがそのやり方を教えてくれるとしたら、すごく楽なのですけど、どうなるでしょう。質問してみました。

 結果は……。

「はい、可能です。そのコードは次のようになります」
うおおおおお、できるんかーーーい、やったああああ(笑。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ButtonClick : MonoBehaviour
{
    public GameObject monsterPrefab;
    public Transform monsterNest;

    void OnMouseDown()
    {
        float range = (monsterNest.localScale.x - monsterPrefab.transform.localScale.x) / 2f;
        Vector3 randomPosition = new Vector3(Random.Range(-range, range), -1f, Random.Range(-range, range));

        Instantiate(monsterPrefab, monsterNest.position + randomPosition, Quaternion.identity);

    }

}

※実際は何回かやり取りして、その最終的なコードが上記のものです。また実際は「void Generate Monster()」という関数として、提示してくれているので、上記と実際のAIの提示したコードは、少し異なります。

 で、私が予想した通り、monsterPrefab(モンスターのプレハブ:出現させたいオブジェクト)への参照と、monsterNest(モンスターの巣:どこにモンスターを出現させるかを指示するための、オブジェクトへの参照)を、定数みたいな感じで内部に持つようになっています。で、その内容は、ここでは記述する必要はなくて、Unityエディターの画面上で、ドラッグ&ドロップで設定可能ですね。ここがUnityの、ぶっとんだ所です。ボタンとして配置したCubeに、上記のスクリプトをアタッチして保存し、Unityエディタ上のInspectorの表示がどうなるか、確認してみましょう。

 表示されました! 「Monster Prefab」と「Monster Nest」! すげええええ!(笑。

 もしかしたらなぜ私がこんなに驚愕しているか、不思議に思ってる人も多いかも知れませんね。ソフトウェアの「設計思想」的な話になるので、その説明は別途することにして、先に進みましょう。

 

 まずMonsterNest(モンスターの巣)を、左のヒエラルキーパネルからドラッグして、ドロップしてやりましょう。

 設定できました! 簡単です! ちょっとスクリプトの説明を、省略してしまってますが、スクリプトでは、ここで設定されたオブジェクト(モンスターの巣)の1メートル下に、モンスターを生成してくれるようになっています。

 で、「MonsterPrefab」という、モンスターを生成するための設計図も、設定してやらないといけないのですけれど、まずヒエラルキーパネル上に「Cube」を追加して設定し、これで動くか、動作確認してみましょう。

 まずCubeを追加して「MonsterPrefab」に変名します。大きさ(scale)は変更せず一辺1メートルのままとしておきます。右の原点の位置に、四角が配置されてますね。

 で、これを、さっきと同様にヒエラルキーパネルから、スクリプトの参照設定に、ドラッグ&ドロップで設定してやります。できましたね。

 設定が終わったら実行してみます。ゲームビューに切り替わりました。原点にモンスターのプレハブ(モンスターの設計図)が見えてしまっていますが、ひとまず気にしないことにしましょう。「Generate Monsters」ボタンを押してみます。どうなるでしょうか!

 やりました! 紫色の、モンスターの巣の下に、白いモンスターが出現しました! これでオブジェクトの、動的生成にも成功ですね。
 と思ったのですけど、あれれ?

 ボタンが落下せずに、巣にくっついたままで、そのまま赤いボタンを連打すると、巣の下にモンスターが次々と生成されていきます。チョイキモですね!

 なぜこうなるのかというと、モンスターには重力を働かせて落下させないといけないので、RigidBody(剛体)、というコンポーネントを、モンスターのプレハブに、アタッチ(くっつける)してやらないと、ダメなんですね。

※と、ここで、「重力を制御させるのに、なぜRigidBody剛体なんていう名前のコンポーネントが必要なの?」、「モンスターのプレハブって、なんなの?」、という疑問が浮かびましたので、チャットAIに質問してみましたので、興味がおありの方はご覧ください。

チャットAIの答えから理解できたこと:

「RigidBody(剛体)というのは、物理用語に由来していて、この名前のコンポーネントで、衝突演算などの物理演算を行わせることが出来ます。そのコンポーネントに、重力演算も含まれているので、重力演算が必要となった場合には、RigidBody(剛体)というコンポーネントが、使われるのです」

「プレハブというのは、英語のprefabricated(プリファブリケイテッド)に由来しています。これは現場で部品を組み立てるのではなく、あらかじめ工場で完成させておいて現場に持ち込み設置する、という方法のことです。Unityにおけるプレハブは、オブジェクトを再利用するための、設計図のようなものです。プレハブとして利用することで、同じオブジェクトを、複数使いまわすことができるのです」

 うーん、なるほどねー(笑。

 まあそれは置いといて、さきほど作成したMonsterPrefabに、RigidBodyのコンポーネントを、アタッチしてみます。

 ヒエラルキーパネルで「MonsterPrefab」を選び、表示されたInspectorパネルの一番下の、「Add Component」をクリックし、

表示されたメニューから「Physics」を押します。

 するとメニューが切り替わるので、RigidBodyをクリックします。

 Inspectorパネル上に、「Rigidbody」関連の設定項目が、追加されました! Use Gravityっていうのにチェックが入ってます。「重力を適用させる」っていうことですね。

 では実行してみましょう。と、実行したら何故か、プレハブ用に置いておいたCubeがジャンプしました! 一体これは……、と思いましたが気を取り直して、赤いボタンを押してみます。

やりました! ボタンを押すたびに、白いモンスターが次々と生成され、地面に落下していきます。どんどん押してみましょう!

 どんどん生成されてます! このまま押し続けるとどうなるのでしょうか! やってみましょう!

 うわあああああ(笑。

 ここで私は気づきました。これ、モンスター育成ゲーとして遊ぶんじゃなくて、Cubeを無数に生成させてあふれさせる、「エアキャップゲー」として作った方が、楽しいんじゃね?(笑。

 どうせならカラフルな方がいいので、黒い立方体ではなく、色をランダムに変更できるようにしてみます。チャットAIに質問した所、さっきのスクリプトを少し変更するだけで、できるそうです。やりますなあ、AIくん(笑。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ButtonClick : MonoBehaviour
{
    public GameObject monsterPrefab;
    public Transform monsterNest;

    void OnMouseDown()
    {

        float range = (monsterNest.localScale.x - monsterPrefab.transform.localScale.x) / 2f;
        Vector3 randomPosition = new Vector3(Random.Range(-range, range), -1f, Random.Range(-range, range));

//      変更前
//      Instantiate(monsterPrefab, monsterNest.position + randomPosition, Quaternion.identity);

//      変更後
        GameObject monster = Instantiate(monsterPrefab, monsterNest.position + randomPosition, Quaternion.identity);
        Renderer renderer = monster.GetComponent<Renderer>();
        renderer.material.color = new Color(Random.value, Random.value, Random.value);

    }

}

 見比べられるように、変更前のをコメントにして残しておきました。

 変更前の1行では、モンスターのプレハブを実体化(Instantiate)して、その後放置だったのですけど、変更後は、作成したモンスターの実体を、「monster」という変数に、入れてますね。で、2行目でそのモンスターの実体のうち、「レンダラー」という部分の参照を取得して、3行目でそのレンダラーの中の、マテリアルの色を、ランダムで作った色に、変更する、というものですね。

 いやあ、AIくんが考えてくれて、一発で動いたからこうやって解説もできますけど、ゼロから書いてみなさいって言われたら、絶対無理ですって答えます(笑。でも、みんなそのような状況で、勉強してUnity使いになっていくのですよね。すごいです。

 色をランダムに変更する処理が出来たので、実行してみましょう。実行直後に、プレハブ用に設置した立方体が、ぽよん、と飛び跳ねるのはさっきと同じ(笑。これは後でなんとかします。

 赤いボタンを連打すると、カラフルなモンスターが出現! やったー。綺麗ですねえ!

 うわああああ(笑。

 でね。今回の「作品」は、なんかこれで満足しちゃったので、「モンスターを歩かせる処理」はまたいつかやることにして、次回はデコボコの地面とか、森とか、海とかにこの「モンスター(?)生成機」を設置して、モンスター(?)を生成させてみる、というのをやってみたいと思っています。
 森とか海とかの地形は、Unityのアセットストアというサイトで無料のものが入手可能ですので、それを使いたいと思います。※ENDOさん、アセットストアの情報ありがとうござました!

 で、最後にモンスターのプレハブをなんとかする方法、ですけど、正解がどうなのかはわからないですけど、
①ヒエラルキーパネル上の「MonsterPrefab」を、プロジェクトパネルの「Assets」に、ドラッグ&ドロップ(MonsterPrefabが、Assetsというフォルダにコピーされます)
②ヒエラルキーパネルから、「MonsterPrefab」を削除
③ボタンのスクリプトに設定しておいた、MonsterPrefabの参照が切れてしまい、Missingとなっているはずなので、ここにさっきAssetsにコピーしたMonsterPrefabを、ドラッグ&ドロップ

以前は「プレハブとかアセットとか、一体何なの?」、と思っていた私ですけど、ようやくその使い方が、おぼろげながらわかってきた気がします。もしここをご覧の方で、これまでの内容などでご質問とかあれば、お気軽にコメント欄でご質問ください。ばっちり答えちゃいますよチャットAIくんが(笑。

次回、いろんな所でモンスター生成! こうご期待(笑。

次回はこちら:

いいなと思ったら応援しよう!