エフアンダーバー

個人開発の記録

Unityの基礎 その3 『ゲームオブジェクト』

まだ説明していない部分をぼかしながら書くのがつらい・・・(特にスクリプト関係)
いっそもうUnity再入門くらいにしたほうがいいんだろうか。

プロジェクト、シーンときたので第三回はゲームオブジェクトです。

ゲームオブジェクト(GameObject)とは?

プロジェクトやシーンと比べるとゲームオブジェクトは何を指しているのかよくわからない言葉です。 しかし、ヒエラルキービューに表示されているやつと言われればだいたい理解できるでしょう。

ゲームオブジェクトはその名前からしてゲームの根幹をなすオブジェクトのように思えますが、実際には単なる整理用のコンテナ *1 です。 ゲームオブジェクト自体がもつ機能はほとんどありません。 ファイルシステムでいうところのフォルダに近い役割であり、 機能本体はファイルにあたるコンポーネント(次回以降に説明)が持っています。 ただし、ゲームオブジェクトは操作の対象として非常に便利なのでコンテナと思って侮るべからず。

ひとつのシーンは複数のゲームオブジェクトを含みます。 そしてそれぞれのシーンは基本的にはゲームオブジェクトを共有しません。 シーン間でゲームオブジェクトを共有したい場合には例外として設定する必要があります。

ゲームオブジェクトの編集

ゲームを構成する機能はコンポーネントにあるといいましたが、 コンポーネントはゲームオブジェクトにくっつける形でしか使用できません。 よってシーンの編集はまずゲームオブジェクトをつくるところから始まります。

基本的にはメニューからCreate Emptyを選択して空のゲームオブジェクトを作成します。 そのほかにもCubeやCameraなど様々な選択肢が表示されるとは思いますが、 これらはゲームオブジェクトにあらかじめコンポーネントが設定されている単なるプリセットであることに注意しましょう。 例えば、Cubeから作成したゲームオブジェクトのBox Colliderコンポーネントを削除しても何ら問題ありませんし、 Create Emptyで作成した空のゲームオブジェクトにCameraコンポーネントなどを追加すればCameraで作成したものと同じものをつくれます。

Create Emptyによって作成された空のゲームオブジェクトには、 ”空”と言っておきながらTransform(あるいはRectTransform)コンポーネントがついています。 これは特殊なコンポーネントで、ゲームオブジェクトとは切り離せない関係にあるため削除できません *2。 ただし、決してこれはゲームオブジェクトの機能などではなく、ひとつのコンポーネントなので注意(多くの点で優遇されてはいますが)。

「じゃあゲームオブジェクトの機能ってどこよ?」というと、インスペクタの最上部にある部分になります。

f:id:fspace:20150902173559p:plain
この細い部分

何度も言いますがゲームオブジェクトは単なるコンテナであって、 その主たる目的はコンポーネントを整理するためにあるのです。

ゲームオブジェクトの実体

ゲームオブジェクトにはそれぞれを表す実体となるファイルはありません。 というか、すでにどこに記録されているかみています。

前回のシーンファイルの中身です(再掲)

--- !u!1 &285478055
GameObject:
  m_ObjectHideFlags: 0
  m_PrefabParentObject: {fileID: 0}
  m_PrefabInternal: {fileID: 0}
  serializedVersion: 4
  m_Component:
  - 4: {fileID: 285478060}
  - 20: {fileID: 285478059}
  - 92: {fileID: 285478058}
  - 124: {fileID: 285478057}
  - 81: {fileID: 285478056}
  m_Layer: 0
  m_Name: Main Camera
  m_TagString: MainCamera
  m_Icon: {fileID: 0}
  m_NavMeshLayer: 0
  m_StaticEditorFlags: 0
  m_IsActive: 1

すべて書いてありますね。

シーンファイルの他にはプレハブファイルの中に記録されることもあります。 形式はシーンファイルとほぼ変わりません。 プレハブについては次回以降の記事で説明します *3

ゲームオブジェクトの親子関係

ゲームオブジェクトはヒエラルキービューに表示されているものだと言いました。 どうしてヒエラルキー(階層)ビューに表示されているかといえば親子関係が設定できるためです。

というのは、実は若干ウソです。
エディタ上では確かにゲームオブジェクトが他のゲームオブジェクトを含んでいるように見えますが、 実際にはこの機能を持っているのはTransformコンポーネントになります。 ゲームオブジェクトは必ずTransformコンポーネントを持たなければならないという制約をつけることでこの機能を借りているわけです (さらには他のコンポーネントがゲームオブジェクトを介してこの機能を又借りしていたりもします)。

さて、借り物とはいえゲームオブジェクトには親子関係があります。 そしてゲームオブジェクトの機能はこの親子関係を考慮して動作したりしなかったりします。 この辺が非常にややこしいので、それぞれの機能ごとに親の設定が子にどう影響するのか確認するようにしましょう。

ゲームオブジェクトの親子関係はゲームの状態を整理する上で非常に重要になるのですが、 それ以外にもゲームオブジェクトの検索の際に重要になります (エディタ上での検索ではなくプログラムコードからの検索です)。 検索は対象となる数(いくつのものの中から探すか)によって時間のかかり方に大きな差がでます。 そのシーンに含まれるゲームオブジェクト全体から検索するのと、 あるゲームオブジェクトの子の中から検索するのでは速度がまるで違います。 適切な親子関係をつけた上で適切な検索手法を選んで検索するようにしましょう。

ゲームオブジェクトの機能

ゲームオブジェクトにも多少ながら機能があります。
これらについて簡単に説明していきます。

名前(name)

ゲームオブジェクトの名前です。 基本的に一目見てそのゲームオブジェクトの役割がわかる名前をつけておけば問題ありません。

名前はゲームオブジェクトの検索に利用することができますが、これはあまりオススメしません。 何故かといえば名前を変えるだけでゲームが動かなくなることがあるからです。 普通は名前の変更がゲームの進行に影響するとは考えないため、 何が原因で動かなくなったのか突き止めづらくなります。 またタイプミスにより無駄にデバッグの時間を割くことにもなりがちです。

もしも名前を検索に使いたいのであれば、 今後絶対に変更しないかつ単純な名前にするか、 一時的なコードのみに限定するべきです。

アクティブ(active)状態

ゲームオブジェクトが有効かどうかです。 有効ならばアクティブ(active)、無効なら非アクティブ(inactive)と呼びます。

非アクティブなゲームオブジェクトのコンポーネントは機能が無効になります。 Unityの提供するイベントシステムのイベントにも反応しないので注意。 また、非アクティブなゲームオブジェクトは一部の検索にも引っかかりません。

非アクティブ状態は親の状態から影響を受けるので注意してください。 親(あるいは祖先)が非アクティブならばその子はすべて非アクティブ状態になります。

タグ(tag)

タグは主にゲームオブジェクトの検索のために使用できます。 シーン全体から検索する場合にはタグによる検索が最速になります (おそらくタグごとのゲームオブジェクトリストを持っていると思われる)。

Unityではデフォルトでいくつかのタグが定義されています。

タグ名 意味
Respawn 復活地点
Finish ゴール地点
EditorOnly エディタ専用
MainCamera メインカメラ
Player 操作キャラクター
GameController ゲームの進行管理

このうちEditorOnlyとMainCameraは特殊なタグでそれぞれ効果があります。 EditorOnlyのタグがつけられたゲームオブジェクトはエディタ上でのみ存在し、ビルド時には含まれません。 MainCameraのタグがつけられたゲームオブジェクトのCameraコンポーネントはスクリプトからCamera.mainによって静的にアクセスできます。

正直この二つ以外はサンプルプロジェクト(あるいは標準アセット)に必要だから入っているのではないかと思っています。 PlayerとGameControllerはともかくRespawnやFinishは特定のゲームでしか使わないので標準で入れるには不適切です。 アセットをインポートするのに使うunitypackageはタグやレイヤーに関してあまり便利でないようなのでその辺の問題じゃないかと推測してます。

レイヤー(layer)

レイヤーは主にUnityの標準機能の対象を選択(フィルタリング)するのに使用します。 今後の記事で順次説明していきますが、カメラに映す対象やライトを当てる対象、物理演算で衝突する対象などの指定に用います。

Unityではデフォルトでいくつかのレイヤーが定義されています。

レイヤー名 意味
Default デフォルト
TransparentFX 透明効果
IgnoreRaycast レイキャスト無視
Water
UI UI

このうちTransparentFXとIgnoreRaycastは特殊なレイヤーでそれぞれ効果があります。 TransparentFXレイヤーのゲームオブジェクトはレンズフレアの遮蔽物にならなくなります。 IgnoreRaycastレイヤーのゲームオブジェクトはレイキャストにヒットしないようになります。

タグ同様WaterとUIはサンプルプロジェクトや標準アセット周りの関係じゃないかと思います。 もしかしたら特別な効果があるのかもしれませんが・・・

静的(static)かどうか

静的という言葉はこの業界ではいろいろな意味で使われるのでわかりづらいですが、 ここでは読んで字のごとく静止した(移動しない)ゲームオブジェクトであるかどうかです。 これは開発者が最適化のためにUnityに対して与えるヒントになります。 移動しないようにする設定ではないので注意。

ゲームオブジェクトの座標が変化しない場合、あらゆる事象を事前計算できる可能性があります。 しかし、この「座標が変化しない」ということを正確に解析することは困難なため、 開発者がUnityに教えてあげる必要があります。

どうしても実行速度が足りない場合に事前計算のメリット・デメリットを理解した上で使用するものなので、 よくわからないのであれば(たとえ、移動しないことがわかっていたとしても)触らないのが無難です。

おわりに

やっぱり内容的には長くなったけど、プロジェクトやシーンに比べれば、 淡々と機能の説明するだけなので楽ですね。

SendMessageとかhideFlagsとかも説明しようか迷ったけど、 スクリプトについてまだ説明していないのでやめました。 きっとそのうち、いや、いつか説明するかもしれません。

参考

https://docs.unity3d.com/Manual/GameObjects.html

https://docs.unity3d.com/ScriptReference/GameObject.html

https://answers.unity3d.com/questions/44112/default-tags-and-layers-in-unity.html



執筆時のUnityのバージョン:5.1.2

*1:マニュアルでは「容器」と表現されています。

*2:GameObjectのスクリプトリファレンスをみるとtransformプロパティの欄に「もしアタッチされていない場合はnull」と書いてあります。 アタッチされていない場合がどういう場合なのかは不明です。

*3:忘れなければ。今のところの予定には入っていません。