【Unity】Destroyで不要なゲームオブジェクトを削除【スクリプト】
UnityではGameObject(ゲームオブジェクト)を生成するだけではなく、作ったGameObjectを削除したいことも多々あります。
開発中にHierarchy(ヒエラルキー)ウィンドウからGameObjectを削除することもありますし、ゲームの実行中に動的にGameObjectを削除することもあります。
このページでは画面上で削除する方法と、スクリプトから削除する方法の両方について紹介します。また、オブジェクトの生成、削除を行う時の注意点についても記載してあるので、もし余裕があればこの点も意識してみるとひとつレベルアップした開発者になれます。
環境
macOS Catalina 10.15
Unity2019.4.4f1
参考文献
Unity公式のマニュアルがあるのでこちらもご覧ください。
もしオブジェクトの生成(Instantiate)についても興味があれば、このブログでも解説を行っているのでこちらも参考までに。
GameObject(ゲームオブジェクト)の削除
GameObjectを削除したい場面は大きく分けて2つあります。
ひとつは開発中にシーン内のオブジェクトが不要になった時です。不要なオブジェクトがシーン内にあるとその分メモリを使用してしまうことになるので、不要なGameObjectは削除しておくことで負荷を減らせます。また、Hierarchy(ヒエラルキー)ウィンドウが整理できるという点も開発をスムーズに進めるために役立ちます。
もうひとつはゲームの実行中にオブジェクトが不要になった時です。例えばシューティングゲームなどで主人公が撃った弾のGameObjectが生成されるとしましょう。この時、遠くに飛んで行った弾はゲームプレイに影響を及ぼしませんが、オブジェクトが存在している限りはメモリ等のリソースが使われるのでなるべく減らしたいところです。
この2つについてGameObjectを削除する方法を紹介します。
ヒエラルキーウィンドウからGameObjectを削除
まずはヒエラルキーウィンドウからGameObjectを削除する方法から。
こちらの操作は簡単なのでもうあなたもご存知かもしれませんが確認のため。
ヒエラルキーウィンドウで不要になったGameObjectをクリックし、右クリックまたは二本指タップでメニューを開き、[Delete] を選択します。
ショートカットキーだとWindowsなら [Ctrl] + [delete] 、Macなら [command] + [delete] で削除できます。
スクリプトからGameObjectを削除
スクリプトからGameObjectを削除する方法についても紹介します。どちらかというとこちらがメインな気もします。
まずはサンプルコードをご覧あれ。
このスクリプトでは、インスペクターウィンドウでセットしたGameObjectをStart()のタイミングで削除(破棄)しています。
スクリプトからオブジェクトを削除する場合はDestroy()メソッドを使います。Destroy()メソッドの引数としてGameObjectを渡せばそのGameObjectが削除されますし、コンポーネントへの参照を渡せばそのコンポーネントが削除されます。
試しに『Plane』オブジェクトの上に『Sphere』オブジェクトを配置してみます。『ObjectDestroyer』のスクリプトをアタッチするためのオブジェクトである『ObjectDestroyer』オブジェクト(そのまんま)を作成し、削除対象のGameObjectとして『Sphere』オブジェクトを指定してあります。
ゲームを実行すると以下のように『Sphere』オブジェクトが削除されます。
よくあるうっかりさんケースとしては、このスクリプトがアタッチされているGameObjectを削除したいと思って以下のように書いちゃうケース。これだとスクリプトだけがGameObjectから削除されてGameObjectはそのまま残ってしまいます。これではいけませんね。
自分自身のGameObjectを削除したい場合は「gameObject」を指定しましょ。小文字で書かれている「gameObject」はこのスクリプトがアタッチされているGameObjectを意味します。
また、以下のようにfloat型で秒数を渡すことで指定秒数後に削除することもできます。例えば削除対象のGameObjectで消える時の効果音を再生して、その再生秒数だけ待った後にオブジェクト削除する、なんて使い方ができます。
サンプル: コライダーに当たったら削除する
サンプルとして、コライダーに当たったらゲームオブジェクトを削除するスクリプトを作ってみます。
OnCollisionEnter()のメソッドは、このスクリプトがアタッチされているGameObjectのコライダーが他のコライダーと衝突したときに呼ばれます。例えばこのスクリプトを平面(Plane)オブジェクトにアタッチしたとしたら、空から落ちてきた別のオブジェクトが地面に落ちたときに呼ばれます。
引数の「other」は衝突相手の情報が渡されます。「other.gameObject」のように衝突相手のGameObjectを取得することもできるので、今回はこの方法でGameObjectを取得してDestroy()で闇の彼方に消え去ってもらいます。
シーンでは以下のようにオブジェクトを配置しています。画面下にある『Plane』オブジェクトに『ObjectEraser』のスクリプトをアタッチしています。黄色い『Sphere』オブジェクトには『Rigidbody』コンポーネントをアタッチしてあるので、ゲームを開始すると重力に引かれて『Plane』オブジェクトに向かって落ちていきます。
ゲームを開始すると、以下のように衝突したときに『Sphere』オブジェクトが削除されます。
たくさんのオブジェクトがある場合でも、『Plane』オブジェクトに衝突すると削除されます。
ステージの外側にこうした衝突を検知するオブジェクトを用意しておくことで、冒頭で少し触れた主人公が撃った弾が遠くに行ったらオブジェクトを削除する、みたいなことができます。
また、このスクリプトでは衝突相手が誰であろうと削除していますが、実際に使う場合は以下のように衝突相手のタグが特定のタグであったら削除する、というようにすると親切です。例えばこの例では衝突相手のタグが「Bullet」(弾)だったら削除しています。
GameObjectを削除する注意点
GameObjectを削除する時の注意点についても。
コンポーネントの参照に注意
GameObjectを削除すると、そのGameObjectにアタッチされているコンポーネントについても一緒に削除されます。GameObjectはコンポーネントの入れ物なのでこれはその通りなのですが、もし他のスクリプトで削除対象のGameObjectにアタッチされているコンポーネントにアクセスするとエラーが出るので注意が必要です。
例えば、あるスクリプトから別のオブジェクトのRigidbodyを参照していたとします。対象のオブジェクトが削除されるとRigidbodyへの参照もnullになるので、操作しようとしてエラーが発生することがあります。
この場合はnullチェックを行うなどの対策が必要です。nullチェックについては以下の記事もご参照ください。
削除されるタイミング
また、Destroy()を呼んだとしても、実はそのフレーム内ではまだオブジェクトが生きているのでアクセスできちゃうんですよね。オブジェクトが削除されるのはフレームが更新された後、つまり呼び出したフレームの処理が全て完了したタイミングなので、別のスクリプトでオブジェクトの数をカウントするなどの処理を入れている場合は注意が必要です。
フレーム内の処理中であっても呼び出した瞬間に削除するDestroyImmediate()のメソッドもありますが、こちらはマニュアルでも非推奨の扱いになっています。この辺に「すぐ削除した方がいいじゃん!」とDestroyImmediate()を使って痛い目を見た人がいるらしいですよ?(1敗)
Editorスクリプトを書く場合には、フレーム終了時にオブジェクトを削除するというDestroy()の動きが使えないため、DestroyImmediate()を使います。なので、ゲーム実行中にDestroyImmediate()を使うのはあまりお勧めできません。
「Immediate(すぐに)」の文字につられて使うと大変なので、大人しくマニュアルの「Destroy()の使用を強く推奨します」の言葉に従いましょう(笑)
(中級者向け)生成と削除は重い
これはパフォーマンスを考え始める中級者以上の方に注意して欲しい点ですが、オブジェクトの生成や削除の処理はちょっと重い処理だったりします。
Instantiate()を使ってオブジェクトを生成して、Destroy()を使ってオブジェクトを削除する、という流れはオブジェクトの数が少ない場合にはそこまで影響はありませんが、オブジェクトの数が多くなるほどパフォーマンスへの影響が出てきます。
この場合は、SetActive()のメソッドを使ってオブジェクトを非表示にしてプールしておくのもひとつの手です。オブジェクトをプールすること(プーリング)についても調べておくと開発者としてレベルアップできます。
まとめ
GameObjectを削除する方法として2通りの方法を紹介しました。ひとつはヒエラルキーウィンドウから手動で削除する方法、もうひとつはスクリプトから削除する方法です。
ゲームの実行中にGameObjectを削除したい場合はスクリプトから削除することになるので、Destroy()メソッドの使い方を覚えておくと便利です。
生成についてもこのブログで紹介しているので、よかったらこちらも合わせてご覧くださいな。
ゲーム開発の攻略チャートを作りました!
-
前の記事
【Unity】日本語化のパックを適用したら初心者でも始めやすい 2020.09.03
-
次の記事
【Unity】Prefabからオブジェクトをたくさん生成するサンプル 2020.09.05
この記事は非常に役に立ちました!特にDestroyメソッドの使い方についての詳しい説明がありがたいです。これからのプロジェクトで活用させていただきます!
ありがとうございます!
お役に立てたのなら幸いです!