そもそもコンポーネントってなんやねん?
Hierarchyビュー上で何らかのオブジェクトを選択してみてください。
下図はCubeオブジェクトの例になります。
罫線で区切られたTransform, Cube(Mesh Filter),Mesh Renderer, Box Colliderの4つがそれぞれコンポーネントです。
↑ Cubeオブジェクトのコンポーネント例
Transformコンポーネントは基本的にすべてのオブジェクトに存在します。
Unityではこのコンポーネントの有無と組み合わせによってオブジェクトの振る舞いや性質を変えます。
- Transform:位置、角度、大きさを管理
- Cube (Mesh Filter):メッシュ(形状)を管理
- Mesh Rederer:メッシュのレンダリング(見た目)を管理
- Box Collider:箱のコライダー(衝突)を管理
Unityではゲーム制作に必要なコンポーネントが予め多く用意されています。
しかし、それらのコンポーネントだけではゲームを作れないので、C#でコンポーネントを自作する必要があります。
単機能
コンポーネントは単機能であることが望ましく、例えば Transformコンポーネントは位置と角度大きさ以外のことには一切関与しません。
- 表示に関するコンポーネントは表示だけを管理
- 衝突に関するコンポーネントは衝突だけを管理
- 移動に関するコンポーネントは移動だけを管理
……という風に単機能のコンポーネントの組み合わせによってオブジェクトのバリエーションを作ります。
コンポーネントの追加と削除と無効化
AddComponent
InspectorのAddAddComponentボタンで任意のコンポーネントを追加できます。
Unityが予め用意したものか自分で書いたC#のScriptを追加できます。
remove
↓コンポーネント横の歯車マークをクリックしてremove
貼ってあるコンポーネントを剥がします。
チェックを外す
CubeのBoxコライダーコンポーネントのチェックを外した例
↑この場合、衝突しないすり抜けるオブジェクトになります。
逆にMeshRenderコンポーネントをオフにしてBox Colliderをオンにすると視えないけど衝突するcubeになります。
removeと違い、コンポーネントを一時的に無効化したいときに使います。
(ただし、厳密に言うと完全に無効化されているわけではないので、注意が必要です)
注意: MonoBehaviour を無効化するチェックボックス(エディタ上)により Start(), Awake(), Update(), FixedUpdate(), および OnGUI() が実行されなくなります。 もし、これらのいずれも存在しない場合はチェックボックス自体が表示されません。
今日のUnity知見。コンポーネントのインスペクタ上でチェックを外して非アクティブにしてもOnTriggerEnterとかは機能する。マジで? リムーブコンポーネントと違って一時的にコンポーネントの機能を切りたいときに便利だったんだけど…
— 栗坂こなべ@C97落選 (@kurisaka_konabe) October 7, 2019
ゲットって何なん? ゲットできたら何が嬉しいの?
ゲットできたら嬉しいのはポケモンだけではありません。
コンポーネントをゲットできると様々なパラメータを弄ることができます。
Inspectorから弄ることができるパラメータは大体スクリプトからも弄ることができるんだな…っと思って貰っていいです。
コンポーネントが提供しているパブリックなメソッド(関数)を呼び出して、何かいい感じの機能を使うこともできるようになります(↓の例ではAddForceという力を加えて吹っ飛ばすメソッドを呼び出しています)。
自作のコンポーネントであっても同様に、パラメータ(パブリックなメンバ変数)を書き換えたり、パブリックなメソッドを呼び出したりできるようになります。
Rigidbody rb = GetComponent<Rigidbody>();
rb.mass = 2.0f; // 質量のパラメータを2.0に設定
rb.velocity = new Vector3(0, 10.0f, 0); // 速度をY方向に10.0のベクトルに設定
rb.useGravity = false; // 重力の使用を無効化
// Rigidbodyクラスに用意されたメソッド(関数)を呼び出している
rb.AddForce(force); // forceには既にVector3型の3次元ベクトル(3次元方向と長さ)が入っているものとする
基礎知識の確認
「変数のスコープ(有効範囲) 何それ? 美味しいの?」
っという方は
初心者がGetComponentを理解する前に最低限知っておくべき前提基礎知識「代入、型、スコープ」を読んでおいてください。
コンポーネントを取得する5つのパターン
1.自身から取得
thisってなんなん?
this.GetComponent<コンポーネント名>( );
「this.」は自分自身を指す(省略可能で大抵の入門書では省略)。
一般的には省略して
GetComponent<コンポーネント名>( ); と書かれがちです。
自分自身って何?
そのコンポーネント自身を指します。
this.GetComponent<コンポーネント名>( );という記述は
「そのthisが書かれたコンポーネントがアタッチされているゲームオブジェクトにアタッチされている<コンポーネント名>のコンポーネントを取得」してきます。
典型的なパターンは、プレイヤーを操作するPlayerコンポーネントがアタッチされいているゲームオブジェクトからTransformやRigidbodyを取得して、それらのパラメータにアクセスしたりTranslateやAddForceのような関数を呼び出したり……っといった使い方です。
余談 this.gameObject
this.gameObjectはそのコンポーネント自身がアタッチされているゲームオブジェクトを指します(これも省略して単にgameObjectと書かれがちです)。
2.衝突した相手から取得
OnCollisionEnter等からぶつかった相手を調べ、その相手のコンポーネントをゲットしてpublicな関数を実行する…っという流れの使い方が多いです。
//よくある使用例:
if (もし、ぶつかった相手のタグが「敵」なら)
{
collision.gameObject.GetComponent<敵コンポーネント型名>( ).ダメージを与える関数(ダメージ量);
}
3.検索した結果から取得
// 一旦、変数に代入してから呼び出す方法
var foundObj = オブジェクトを探す関数名( );
var comp = foundObj.GetComponent<ゲットしたいコンポーネント名>( );
comp.実行したい関数名( );
// メソッドチェーンで1行で書く方法
オブジェクトを探す関数名( ).GetComponent<ゲットしたいコンポーネント名>( ).実行したい関数名( );
4.自身のInspectorから取得
publicなGameObject型のメンバ変数
publicなGameObject型のメンバ変数を用意してInspector上セットし、そこからアクセスする方法です。
GameObject型のメンバ変数で宣言しておくと、Hierarchyビュー上のどんなオブジェクトでもドラッグ&ドロップでInspectorにセットできます。
public class Aaa : MonoBehaviour
{
public GameObject target; // 適当な変数名
void Start()
{
target.GetComponent<コンポーネント名>().実行したいpublicな関数();
// オブジェクトを非アクティブ化する例
// targetはGameObject型の変数なので
target.SetActive(false); // .gameObjectの記述が不要
// オブジェクトを破壊する例
Destroy(target); // 引数にGameObjet型の変数が要求されている
}
}
publicなコンポーネント型のメンバ変数
publicな任意型のメンバ変数を用意してInspectorでセット。そこからアクセスする方法です。
※こちらの場合、その任意型のコンポーネントがアタッチされていないオブジェクトはInspector上でセットできないことに注意!
メリットとしては、GetComponent<コンポーネント名>()を記述する必要がありません。そのオブジェクトの任意のコンポーネントにしか用事がないときは、この方がコード全体を簡潔に記述できます。
デメリットとして、Destroy関数やSetActive関数のような.gameObjectを渡す必要があるときは記述量が若干増えてしまいます。
public class Aaa : MonoBehaviour
{
public コンポーネント名 target; // 適当な変数名
void Start()
{
// GetComponent<コンポーネント名>()が不要
target.実行したいpublicな関数();
// オブジェクトを非アクティブ化する例
// targetはGameObject型の変数じゃないので
target.gameObject.SetActive(false); // .gameObjectの記述が必
// オブジェクトを破壊する例
Destroy(target.gameObject); // 引数にGameObjet型の変数が要求されている
}
}
publicなTransform型メンバ変数の使用例
public class Aaa : MonoBehaviour
{
public Transform target; // 適当な変数名
void Start()
{
// .GetComponent<Transform>()もしくは.tranformが不要
// 例えばもし、GameObjet型のメンバ変数なら
// target.GetComponent<Transform>().position = new Vector3(0, 0, 0);
// もしくは省略記法なら
// target.transform.position = new Vector3(0, 0, 0);
target.position = new Vector3(0, 0, 0);
}
}
5.Instantiateで生成したオブジェクトから取得
Instantiate関数には戻り値があります。
第1引数で指定した型の変数で返ってきます。
↓以下の例ではGameObjet型で返ってきた変数からRigidbodyにアクセスしています。
public class BulletShooter : MonoBehaviour
{
public GameObject bulletPrefab;
void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
var bullet = Instantiate(bulletPrefab, transform.position, Quaternion.identity);
var rb = bullet.GetComponent<Rigidbody>();
rb.AddForce(Vector3.right * 4, ForceMode.Impulse)
}
}
}
ただ、この例だと、用事のあるRigidbody型でprefabを持っておいた方がいいです。Inspector上でRigidbodyコンポーネントが付与されているオブジェクト以外セットできなくなるのでミスも防げます。
public class BulletShooter : MonoBehaviour
{
public Rigidbody bulletPrefab;
void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
// var bullet はRigidbody型になる
var bullet = Instantiate(bulletPrefab, transform.position, Quaternion.identity);
// .GetComponent<Rigidbody>()が不要
bullet.AddForce(Vector3.right * 4, ForceMode.Impulse)
}
}
}
##一旦変数に入れる方法と1行で書く方法
一旦変数にキャッシュする方法
典型的なパターンです。ゲットしたいコンポーネント型のメンバ変数(この例ではRigidbody型変数の rb )を用意しておきます。そしてStart()関数で起動時にゲットしてきてメンバ変数に格納します(「キャッシュしておく」と言うそうです)。
public class Rabbit : MonoBehaviour
{
private Rigdbody rb;
void Start()
{
rb = this.GetComponent<Rigidbody>();
}
// <中略>
void Jump()
{
// <中略>
rb.AddForce(force); // forceには既にVector3型の3次元ベクトル(3次元方向と長さ)が入っているものとする
}
}
メソッドチェーンで1行で書いちゃう方法
メソッド(関数)はチェーンコンボみたいに繋がり、上記3行の処理は次のように1行で書けちゃいます。
this.GetComponent<Rigidbody>().AddForce(force);
これなら rb という変数をわざわざ用意しないで済みます。ただし、GetComponentは少し重い処理なので、頻繁に呼ぶ処理を毎回毎回ゲットしてくるのはあんまりよろしくないです。
大した処理負荷ではないので、初心者はそこまで神経質ならなくてもいいと思いますが、Update()関数等で毎秒約60回、何百個ものオブジェクトがGetGetComponentしている…っというのは避けておきたいです。
あと、処理速度の問題を抜きにしても、記述上 「GetComponent().」を書きまくるより「rb.」 で済む方がコードがスッキリして読みやすいです。
省略記法と名前の衝突に関する注意点
Transform コンポーネント(座標と角度と大きさを管理)
厳密に書くとGetComponentが要りますが、「transform」で省略できます。
Transform tf = this.GetComponent<Transform>();
tf.position = new Vector3(0, 10.0f, 0);
this.GetComponent<Transform>().position = new Vector3(0, 10.0f, 0);
transform.position = new Vector3(0, 10.0f, 0);
Rigidbody コンポーネント(物理挙動を管理)
Rigidbody rb;
※歴史的経緯でrigidbodyという名前は避けたいです。
Rigidbodyは過去(たぶんUnity4時代くらい)には、「transform」と同様に「rigidbody」という省略記法でGetComponentを呼び出さずに書けた時代がありました。今はその省略記法で書くことが許されなくなっています。その歴史的経緯により…
省略記法の名残りと変数名の衝突
自作のクラス(コンポーネント)だと
「頭文字大文字のクラス名(型名) 小文字始まりの変数名」
Rabbit rabbit;
っという変数の命名をすることが多く、
これと同じノリで…
Rigidbody rigidboy;
と書いてしまうと、旧省略記法と被って紛らわしいので非推奨です。
Rigidbody rb;
っという命名を筆者は好んで使います。
省略記法名は避けたいです。
「gameObject」という記述は自分自身のゲームオブジェクトを指すので避けます。
「camera」もrigidibodyと同様に非推奨です。
(他にもあった気がするので、思い付いたら追記します)
↑ 回避名の例
tranformという省略記法
Transform transform;
という変数名は困ります。今現在使われている省略記法名と被ります。
Transform tf;
の様な命名で回避しておきたいです。
よくわからなければ、Unity側であらかじめ用意されている型名の場合、
Hoge hoge;
のような型名をそのまま小文字にした変数名は避けておいた方が無難です。
余談
trannsformという省略記法もrigidbodyという省略記法と同様、将来的には書けなくなるかも…?
参考
gameObject.GetComponent() と transform の違い(または Unity における省略記法について)
更新履歴
2021年 06月27日 thisに関する説明を修正、その他細かい部分を修正
2019年 12月12日 取り敢えず仮公開
2019年 12月07日 取り敢えず仮投稿