【Unity】PrefabをInstantiateして楽にオブジェクトを配置
UnityでGameObjectを作成する場合は、Hierarchy(ヒエラルキー)ウィンドウから作成していきます。ゲームを実行していないとき、事前に必要なオブジェクトを作成する分にはこれでいいのですが、ゲームの実行中にもGameObjectを作りたい場面は多々あります。
例えばシューティングゲームでは、主人公機の撃った弾のオブジェクトは攻撃ボタンが押されたときにシーンに登場させたいですよね。その瞬間に私たちがユーザーのもとに行って、ヒエラルキーウィンドウからGameObjectを作る……なんてことはできないので、スクリプトから動的に生成したいところ。
そんな機能なんて……あるんです!
Unityでは、シーン内で作成したGameObjectをPrefab(プレハブ)という形でアセットファイルとして保存しておくことができます。このPrefabはヒエラルキーウィンドウにドラッグすればGameObjectとして使えて、なんと別のシーンでもGameObjectを実体化させられちゃうんです。すごい。
画面で手作業で行う以外にも、スクリプトから自動的にGameObjectを作ることもできて便利なのがこのPrefabなんです。
このページではPrefabを実体化(Instantiate)する方法の解説や、関連する用語の解説をしていきます。
環境
macOS Catalina 10.15
Unity2019.4.4f1
参考文献
UnityのマニュアルでPrefab(プレハブ)について解説されているのでこちらもご覧ください。
こちらはUnityのスクリプトリファレンスです。Instantiateメソッドの使い方が記載されています。
PrefabをInstantiateする
さぁ! スクリプトからPrefabをInstantiateしたまえ!(世界樹)
と言われても何が何やらわからないと思うので、まずはそれぞれの言葉の意味を確認してみましょ。
Prefab(プレハブ)について
Prefab(プレハブ)とは、ゲームオブジェクトの設定をそのままアセットファイルとして保存しておくことができる機能です。このPrefabのファイルは再利用できる、つまり他で使いまわせるので、同じシーンでコピーを作成することもできますし、別のシーンでもコピーを作成することができます。
Prefabの良いところは、設定も一緒に保持してくれていることです。例えばシーンの中で『Rigidbody』や『Box Colloder』の設定を行ったとしましょうか。もし新しいオブジェクトを作成した場合は、再びその設定を行う必要があります。これってめんどくさいですよね。楽してゲーム作りたいのに、めんどくさい作業が発生するのは辛いことです。
Prefabの場合は、アタッチされているコンポーネントの設定も保持してくれます。上の例だったら『Rigidbody』や『Box Colloder』の設定も一緒に保持してくれているんですね。
こうすることで、別のシーンにコピーするときもその設定をそのまま使えるんです。超便利。
さらに、Prefabはコピーしたオブジェクトとも繋がっていて、Prefabのアセットファイル側で設定を変更した場合は、コピーした先のGameObjectでも反映されるんです。これによって手間が削減されます。Prefabを作りたいオブジェクトって、結構数多く作ることが多いんですよね。これらの設定をひとつひとつ変えていくのは地獄めいた作業量になるので、一括で変更できる機能があるのはありがたいことです。
設定がリンクしている点は、単純にGameObjectをコピペするより優れている点です。
ちなみにPrefab(プレハブ)というのはプレハブ工法から来ています。プレハブ工法とは、例えば住宅などを現地で1から作成していくのではなく、事前に組み立ててあるパーツを用意しておいて、それを現地で組み上げていく工法のこと。GameObjectをパーツとして用意しておいて、実際に使うときにはそのパーツを組み合わせてシーンを作っていくことからこの名前が使われているんだと思います。名付け親じゃないので想像ですが(笑)
Prefab(プレハブ)の作り方
おさらいとしてPrefabの作り方も確認しておきましょうか。まずは3Dの『Cube』オブジェクトを作成します。
『Game』ウィンドウではこんな感じ。このオブジェクトをPrefabにしてみましょ。
Prefabにする方法は簡単で、ヒエラルキーウィンドウからPrefabにしたいオブジェクト(ここでは『Cube』)を選択して、Project(プロジェクト)ウィンドウにドラッグ&ドロップするだけです。ね? 簡単でしょう?
Prefabが作成されると、以下のようにProject(プロジェクト)ウィンドウでファイルが表示されます。この時のファイル名はGameObjectの時の名前が使われています。
作成したPrefabを使って手動でシーンに作成することもできます。作成したいPrefabを選択してからヒエラルキーウィンドウにドラッグ&ドロップしましょう。
Prefabから作成したオブジェクトはヒエラルキーウィンドウでは名前が青色で表示されます。Prefabを作成した元のオブジェクトである『Cube』も青くなっていますね。
これらのオブジェクトの設定値はプロジェクトウィンドウにあるPrefabと繋がっていて、Prefabの設定を変更するとこれらのオブジェクトの設定値も一緒に変更されます。ただし、作成したオブジェクト側で個別の設定を入れた場合は、そのオブジェクトの設定値が優先されます。
例えば上の例で『Cube (4)』の『Rigidbody』コンポーネントで質量を変えたとしたら、大元のPrefabで『Rigidbody』コンポーネントの質量を変えたとしても、個別の値が入った『Cube (4)』ではそのままになります。
また、作成した直後のPrefabは『Transform』コンポーネントの値が元のオブジェクトと同じになっているため、自分で位置を調整します。この点はスクリプトからオブジェクトを作成する場合も同様です。
Instantiate(インスタンシエイト)について
続いて『スクリプトからPrefabをInstantiateしたまえ!』のInstantiate(インスタンシエイト: 実体化)について。
これはスクリプトから呼ぶメソッドで、その名の通りオブジェクトをインスタンス化(実体化)する機能を持っています。
インスタンスとは実体のことで、Prefabのデータを金型にして、インスタンスという鯛焼きを作っていくイメージです。鯛焼きは金型があるので同じ形になっていますよね。あれと同じイメージです。鯛焼きも、言ってしまえば形のコピーを行っているわけです。(前この説明をしたら「あんこの量が違うかもしれないじゃないか!」と難癖をつけられたことがあるので、あんこの量も機械で同一量注入していると考えてください)
オブジェクト指向のプログラミングに慣れている場合はインスタンス化のイメージもつきやすいかもしれませんね。
Instansiateのメソッドではコピー元のオブジェクトを指定して、そのオブジェクトをシーンに作成してくれます。戻り値として生成したGameObjectへの参照を返してくれるので、この参照を使ってオブジェクトの名前を変えたり、スケールを変更したりすることができます。
私はよく親オブジェクトの変更をすることがあります。というのも、何も考えずにInstansiateメソッドを使ってオブジェクトを生成するとヒエラルキーウィンドウの最上位にオブジェクトが作成されるため、とっ散らかってしまうんですよね。
サンプルコード
実際にスクリプトから Prefabの作成をしてみます。
まずはシンプルにオブジェクトを生成するコードから。
Start()のタイミングで1回CrateObject()のメソッドを呼ぶようにしています。このメソッドの中では、Instantiateメソッドを使用してオブジェクトを作っています。
Instantiateの引数は3つ指定していて、左からコピーするPrefab、生成する位置、回転を指定しています。ここではコピーするPrefabとして上で作成したCubeのPrefabを指定し、位置はゼロを意味するVector3.zero、回転も回転していないことを意味するQuaternion.identityを指定しています。
このスクリプトを任意のオブジェクトにアタッチして実行すると、ゲーム開始時に『Cube』のオブジェクトを生成してくれます。(ここでは『PrefabMaker』というオブジェクトを作成してスクリプトをアタッチしました)
オブジェクトの名前はCloneがつくので、気になる場合はInstantiate後に名前を変更すると良いでしょう。
Quaternion(クォータニオン)って何だ!?
ところでしれっと説明を流しましたが、Instantiateのメソッドで回転の指定方法として『Quaternion.identity』を使いました。
この『Quaternion』は初心者キラーとして有名な要素で、ゲームで回転を表現するための方法です。
3次元空間ではX方向、Y方向、Z方向それぞれに回転するから3つの値で表現できる……と思いきや、実はこれだと回転の向きが決められないんです。意図した向きにならないんですね。
そこで、四元数(しげんすう: クォータニオン)を使って表現するようになりました。1/4を表すクォーターを思い出すと言葉も覚えやすいかも?
四元数は直感的に理解できる考え方ではないので、今日は「ゲームの回転を表すためには四元数を使う」とだけ覚えていれば大丈夫です。数学や物理に慣れている人はノルムがどうのこうのとか、ハミルトン積がどうのこうのまで突っ込んで調べてみるのも面白いと思います。
四元数で大切なのは、あるベクトルを、原点を通る単位ベクトルの周りに任意の角度だけ回転させる、というのを簡単に表現できることです。四元数を計算するためのメソッドはUnityさんによって作られているので、私たちが意識するのはどの軸の周りに何度回転させたい、という部分だけで済みます。ありがとうUnityさん。
TransformコンポーネントでX, Y, Zの回転を設定することができますが、実は内部的には与えられた角度を使ってQuaternionで計算していたりします。ユーザーがダイレクトにいじるのは辛いですからね(笑)ありがとうUnityさん(2回目)
ここで『Quaternion.identity』の話に戻ると、この指定は「ワールドまたは親オブジェクトの軸に合わせます」という指定で、自分独自の回転はしませんという宣言です。Prefabを生成する場合、生成時に直接回転を指定するよりも、生成後に親オブジェクトを設定して必要ならそこで回転を指定する方がわかりやすくて簡単なので、Instantiateのメソッドで回転を指定する場合は『Quaternion.identity』を指定しておくと良いでしょう。
オブジェクトの生成に使おう
基本の部分をお伝えしたので、これであなたもスクリプトを使ってPrefabからGameObjectを自動的に作れるようになりました。ゲームの実行中に生成するオブジェクトはこの方法で作成するのがベストです。
たくさん生成する時にも便利
サンプルとしてPrefabに『Rigidbody』コンポーネントをアタッチした状態で10フレームおきにInstantiateを呼んでみました。生成したオブジェクトの位置をスクリプトから設定しており、空からCubeが降ってくるようにしています。
マテリアルは3種類用意して、乱数を使ってランダムなマテリアルを設定するようにしています。
これだけの数を自力で作るのは骨が折れますから、スクリプトを使って本領発揮です。
このサンプルについては以下の記事でインスペクターウィンドウの設定やコードを公開しているので、よかったらご覧くださいな。
動かないオブジェクトでも
動かないオブジェクトを作る時にも便利です。例えばブロック崩しのブロックなどはスクリプトを使って配置すると簡単です。
Unityを使い始めて間もないころは、インスペクターウィンドウから順番に値を変えていった思い出があります。単純作業なので音楽を聴きながらやっていたのですが、スクリプトを書いてしまえば1発で並べられることを知った後のショックと言ったら……orz
例えば以下の画像では200個のオブジェクトを並べています。これを手作業でやっていたのは、今となっては信じられませんね(笑)
1つ5秒で設定したら1000秒、約16分ですからなんと無駄に時間を使っていたことか。万が一オブジェクトの大きさを修正することになったら、また16分かかっちゃいますね。それを思えばスクリプトを書いちゃった方が遥かに簡単です。試しに時間を測ってみたら3分くらいで書けたので、5分の1以下で、かつ何度も使えるものを作ったのです。スクリプトすごい。
こちらのサンプルもコードを公開しています。応用としてPrefabの大きさから並べる位置を計算する方法も記載しているので、よかったらご覧ください。
UIも生成できるよ
ここまでの部分ではゲーム内の空間に配置するオブジェクトをInstantiateで配置しました。3Dのオブジェクトや2Dのオブジェクトだけではなく、UIを生成できる点も意識しておきましょう。
例えばこれは別で作っているゲームの画面ですが、スクロールビューで選択できる項目をPrefabとして作ってあります。画像では木のプレートの部分です。このPrefabをInstantiateした後、アイテムのデータを読み込んで名前や数字をセットしていく、なんてやり方もできます。
UIのPrefabをインスタンス化してGameObjectを作成する場合は特に親オブジェクトの設定が大切になります。
obj.transform.SetParent(親オブジェクトのTransform);
みたいに親オブジェクトの指定をした後にスケールや位置を設定することでビューの大きさに合わせた場所に表示することができます。UIの場合は設定によってとんでもないところにインスタンス化されることもあるので、意図した位置やスケールで表示されるように設定することを意識しましょう。
まとめ
スクリプトを使ってPrefabをInstantiateすることで、大量のオブジェクトを自動的に生成することができます。ゲームの実行中にオブジェクトを生成したい場面は多いので、まずは基本的なInstantiateメソッドだけでも使ってみてください。
だんだんと位置の計算をスクリプトからやるようになって、スクロールビューの要素もスクリプトから生成するようになって……とやりたいことが増えていくと思います。ようこそPrefabの沼へ!
というわけで、ぜひ手を動かしてやってみてくださいな。
作成したオブジェクトを削除したい場合は以下の記事もご参照ください。
ゲーム開発の攻略チャートを作りました!
-
前の記事
【ゲーム作り】やったことないジャンルのゲームも手を出してみよう 2020.09.01
-
次の記事
【ストーリー作り】キャラクターの行動理念を大切にしよう 2020.09.02
Prefabのインスタンス化についての説明、とても分かりやすかったです!特に、オブジェクトの配置の効率化に役立ちそうなテクニックが学べました。実際に試してみます!ありがとうございます!