Unityでインディゲーム道!

プログラム、Unity初心者がインディゲーム制作を目指して日々思うことなどを書き綴ります。

UnityでGetComponentを使って"参照"を得るということ。Unityにおけるオブジェクト指向とは、的な。

Unityでのスクリプト作成、コーディングにおいて重要であり、かつ基本的なメソッドである"GetComponent"について、まとめてみます。

そして、そもそも"参照(リファレンス)を得る"ということはどういうことか?という基本的な部分についても書いてみたいと思います。

 

GetComponentオブジェクトの参照を得るためのメソッド

 

オブジェクトとは

まず"オブジェクト"とは"クラス"の変数です。"クラス"は設計図のようなモノであり、そこから作られた実体、みたいな説明がありますが、結局は"クラス"は複数の変数、メソッドを保有できるデータ・タイプ(いわゆる"型")なワケで、そのタイプのデータを格納するための変数が"オブジェクト"です。

変数とは少し違うんだぞ!という区別をするために"オブジェクト"あるいは"インスタンス"といっているのであって、何らかのデータを格納するための器のようなものという性質は同じです。私達人間も様々なデータを内部に保有しています。

 

Unityでコードのコンポーネント"スクリプト"と呼んでいますが、要は"クラス化された、あるひとつのコード"であるということに注意が必要です。つまり、プログラム全体のコードの一部分です。またUnityのスクリプト上で、"スクリプト"を含む他のコンポーネントのオブジェクトを作るということは、他のクラスのオブジェクトを作るということにもなります。(ここは後で詳しく説明します。)

 

変数である以上、宣言して使うわけですが、それだけでは"中身"は入っていません。

int year = 2017;

このように値を代入しなければいけません。

 

オブジェクト指向とは"クラス"で分割し、そこからオブジェクトを作り、オブジェクトを中心にプログラムを動かしていくことで、現実世界の概念や自然言語に近づける、というものです。当然、オブジェクトには中身が必要です。

問題はUnityにおけるオブジェクトに入れる中身とは何か?ということです。つまり、それは実際のゲームオブジェクトにアタッチされているコンポーネントになります。

(もちろんプログラミングにおける一般的な意味でのオブジェクトも作られます。)

 

Unityにおける"コンポーネント"

直訳すれば『部品』となりますが、Unityでは何かしらの機能のまとまり、ゲームオブジェクトに機能を持たせるための部品になります。

要は物理演算のための"Rigidbody"や当たり判定の"コライダー"、音を出すための"オーディオ・ソース"、などのUnityで(比較的)手軽にゲームを作ることの出来る、有難い部品の数々です。

と同時にプログラミング的にこれらは全て"クラス"でもあります。つまり、スクリプト上でUnityのコンポーネントを動すには、そのコンポーネントの"クラス"からオブジェクトを作る必要があるということです。

 

そして、上記で書いたようにオブジェクトには中身が必要です。繰り返しますが、それがゲームオブジェクトに実際にアタッチされているコンポーネントになるわけです。

つまり、その中身がなければ、

Rigidbody rb; // Rigidbodyのオブジェクト、インスタンス

rb.AddForce( Vector3.up * jumpPower ); 

とコードを書いても、Unity上では何も起こらないわけです。ここでいう"rb"というRigidbodyのオブジェクトに中身を入れる必要があります。

 

つまり、オブジェクトにアタッチされているコンポーネント"参照"(リファレンス)を得る必要があります。(例えばプレイヤーのゲームオブジェクトにアタッチされているRigidbodyのことになります。)

 

そもそも"参照"とは?

"参照する"とは単純に言えば、結びついている、関連しているということです。具体的な行為としては、何らかの本を参考にするみたいなことです。より詳しく言うと、"どこ"にある"どの本"の"何ページ目"の"何行目"を明示した上で、参考にする、というのが"参照する"ということです。

プログラミングにおいても同様で、より正確な所在地を指し示しつつ、参考にしている、引用している、ということになります。

 

プログラミングにおける"参照"(リファレンス)

少しプログラミングそのものに関する話です。C#では変数のデータの格納の仕方には二種類あります。値そのものを格納するデータ・タイプ、実際の値は格納せず、データ群が置かれたメモリ上のアドレスを格納するリファレンス・タイプの二つです。

 

オブジェクトはリファレンスタイプになります。つまり、オブジェクトは正確には該当する中身の"ある場所"を格納しているということになります。オブジェクトはメモリの住所を指し示している、とも言えます。どこにあるか分かっているからこそ、データやメソッドを動かすことができるということです。

 

GetComponentの働き

というわけで、いよいよ本題です。つまりGetComponentはUnity上の該当するコンポーネントどこにあるかをオブジェクトに示す(返す)ためのメソッドです。

スクリプト上のオブジェクトが真の実体を持つには、オブジェクトがUnity上のコンポーネントを指し示すことが出来なければいけないのです。

先ほどの例で言えば、

Rigidbody rb;

 

void Start(){

        rb = GetComponent<Rigidbody>();

}

 

これはRigidbodyのオブジェクトを作り、このスクリプトがアタッチされているゲームオブジェクトに同じくアタッチされているRigidbodyコンポーネントの場所を"rb"に指し示す、という内容になります。

注意としては、GetComponentの効力は、基本そのスクリプトがアタッチされているコンポーネントに限る、ということです。(ChildやParentは指定すれば可)

上の例が、プレイヤーキャラにアタッチされた"PlayerController"スクリプトだとすると、そのプレイヤーキャラにアタッチされたRigidbodyを呼び出していることになるのです。つまりrbというオブジェクトが、当コンポーネントの参照を得て、このコード上において実体をもった、ということになります。

 

アタッチされたゲームオブジェクト以外の参照

しかし、それだけでは参照を得るのに限界があります。例えば、敵キャラにアタッチされた"EnemyContoroller"等の他のスクリプト(他の"クラス")の参照を得ることはできないのでしょうか?でなければ、"PlayerController"側から、敵キャラを動かすことができないことになります。(攻撃したら吹っ飛ばす、みたいな)

 

スクリプトの中で、そのスクリプトのオブジェクトを作り、きちんと"参照"を得れば可能です。それにはいくつかの方法があります。

 

publicとして宣言して、インスペクター上にドラッグする。

public Rigidbody playerRb;

これは便利ですが、実際にシーンにおかれているゲームオブジェクトである必要があります。例えば、必ずシーンに存在するプレイヤーを参照する際に便利かもしれません。しかしドラッグし忘れるとnull(参照ないですよ!)エラーが発生します。

 

GameObject.FindWithTag()で引っ張ってくる。

タグの設定をする必要がありますが、確実な方法です。基本的にプレイヤーには、Playerタグを設定しておけば、FindWithTagでそのプレイヤーのゲームオブジェクトの参照を得られます。つまり、一回、ゲームオブジェクトのオブジェクトにそれを移して、そのオブジェクトからGetComponentする、という形になります。

GameObject playerObject = GameObject.FindWithTag("Player");

if(playerObject != null)

           rb = playerObject.GetComponent<Rigidbody>(); 

 

・接触したコライダーから参照を得る。

これはダイナミックな方法です。OnCollisionEnterなどで検出したコライダーからGetComponentするという方法です。応用するとPhysic.OverlapSphere等で検出したコライダーからを与えて、バクダンの爆発力を与えて吹き飛ばす、なんてこともできます。rigidbodyの場合は、collider.attachedRigidbodyから直接アクセスも可能です。

  

まとめ!

というわけで、Unity上で何かする際に必須な各コンポーネントスクリプト上でコントロールするためには、その実際のコンポーネントの参照を得る必要があり、そのためにGetComponentは必要である、という話でした。

基本ではありますが、大事な部分ではあると思うし、Unity上のC#を使う上での特徴的な所だと思うので、自分なりにまとめてみました。