【Unity】ゲーム会社でスマホ向けゲームを開発して得た知識 拡張メソッド編

2年目のゲームプログラマがゲーム会社でUnityを使用して
スマートフォン向けのゲームを開発して感じたことをまとめておきます

* [【Unity】ゲーム会社でスマホ向けゲームを開発して得た知識 コーディング編](http://qiita.com/baba_s/items/f2ad850dd7fc84165e96)
* [【Unity】ゲーム会社でスマホ向けゲームを開発して得た知識 拡張メソッド編](http://qiita.com/baba_s/items/a0490ea36f6a5bcf8b07)
* [【Unity】ゲーム会社でスマホ向けゲームを開発して得た知識 デバッグメニュー編](http://qiita.com/baba_s/items/08cecb26e656900c3bb3)
* [【Unity】ゲーム会社でスマホ向けゲームを開発して得た知識 UI編](http://qiita.com/baba_s/items/c8d25456c0ee4ef52f94)
* [【Unity】ゲーム会社でスマホ向けゲームを開発して得た知識 アセットバンドル編](http://qiita.com/baba_s/items/a64b97de1b3cd41460f9)

## 拡張メソッド

拡張メソッドを使用することで既存のクラスに関数を追加することができます
今回関わったゲーム開発では拡張メソッドを使用することでコードを短くわかりやすく記述できたので
定義して良かった拡張メソッドを紹介していきます

### SafeGetComponent

指定されたコンポーネントを返します
指定されたコンポーネントがアタッチされていない場合は追加してから返します

```cs
public static T SafeGetComponent<T>( this GameObject self ) where T : Component
{
	return self.GetComponent<T>() ?? self.AddComponent<T>();
}

public static T SafeGetComponent<T>( this Component self ) where T : Component
{
	return self.GetComponent<T>() ?? self.gameObject.AddComponent<T>();
}
```

コンポーネントを取得する時にこの拡張メソッドを使用することで
Unityエディタ上でスクリプトをアタッチし忘れていたとしてもnull参照を防ぐことができます

```cs
var player = gameObject.SafeGetComponent<Player>();
```

### GetChildren

すべての子オブジェクトを返します

```cs
public static GameObject[] GetChildren( this GameObject self, bool includeInactive = false )
{
	return self
		.GetComponentsInChildren<Transform>( includeInactive )
		.Where( c => c != self.transform )
		.Select( c => c.gameObject )
		.ToArray();
}

public static GameObject[] GetChildren( this Component self, bool includeInactive = false )
{
	return self
		.GetComponentsInChildren<Transform>( includeInactive )
		.Where( c => c != self.transform )
		.Select( c => c.gameObject )
		.ToArray();
}
```

すべての子オブジェクトを検索して一括で処理したい時に役に立ちます

```cs
foreach ( var n in gameObject.GetChildren() )
{
}
```

### GetChildrenWithoutGrandchildren

孫オブジェクトを除くすべての子オブジェクトを返します

```cs
public static GameObject[] GetChildrenWithoutGrandchildren( this GameObject self )
{
	var result = new List<GameObject>();
	foreach ( Transform n in self.transform )
	{
		result.Add( n.gameObject );
	}
	return result.ToArray();
}

public static GameObject[] GetChildrenWithoutGrandchildren( this Component self )
{
	var result = new List<GameObject>();
	foreach ( Transform n in self.transform )
	{
		result.Add( n.gameObject );
	}
	return result.ToArray();
}
```

前述の`GetChildren`は孫オブジェクトも取得してしまうため
孫オブジェクトは検索対象に含めたくない場合はこの拡張メソッドを使用しました

```cs
foreach ( var n in gameObject.GetChildrenWithoutGrandchild() )
{
}
```

### GetComponentsInChildrenWithoutSelf

自分自身を除くすべての子オブジェクトにアタッチされている指定されたコンポーネントをすべて返します

```cs
public static T[] GetComponentsInChildrenWithoutSelf<T>( this GameObject self, bool includeInactive = false ) where T : Component
{
	return self
		.GetComponentsInChildren<T>( includeInactive )
		.Where( c => self != c.gameObject )
		.ToArray();
}

public static T[] GetComponentsInChildrenWithoutSelf<T>( this Component self, bool includeInactive = false ) where T : Component
{
	return self
		.GetComponentsInChildren<T>( includeInactive )
		.Where( c => self.gameObject != c.gameObject )
		.ToArray();
}
```

標準の`GetComponentsInChildren`は自分自身も検索対象にしてしまうため
自分自身は検索対象に含めたくない場合にこの拡張メソッドを使用しました

```cs
var uiWidgetList = gameObject.GetComponentsInChildrenWithoutSelf<UIWidget>();
```

### RemoveComponent

指定されたコンポーネントを削除します

```cs
public static void RemoveComponent<T>( this GameObject self ) where T : Component
{
	Object.Destroy( self.GetComponent<T>() );
}

public static void RemoveComponent<T>( this Component self ) where T : Component
{
	Object.Destroy( self.GetComponent<T>() );
}
```

通常、ゲームオブジェクトやコンポーネントを削除するときは`Object.Destroy`を使用しますが
コンポーネントの削除を直感的に記述したかったのでこの拡張メソッドを用意しました

```cs
gameObject.RemoveComponent<Character>();
```

### RemoveComponentImmediate

指定されたコンポーネントを即座に削除します

```cs
public static void RemoveComponentImmediate<T>( this GameObject self ) where T : Component
{
	GameObject.DestroyImmediate( self.GetComponent<T>() );
}

public static void RemoveComponentImmediate<T>( this Component self ) where T : Component
{
	GameObject.DestroyImmediate( self.GetComponent<T>() );
}
```

エディタ拡張で即座にコンポーネントを削除したいときはこちらの拡張メソッドを使用しました

```cs
gameObject.RemoveComponentImmediate<Character>();
```

### RemoveComponents

指定されたコンポーネントをすべて削除します

```cs
public static void RemoveComponents<T>( this GameObject self ) where T : Component
{
	foreach( var n in self.GetComponents<T>() )
	{
		GameObject.Destroy( n );
	}
}

public static void RemoveComponents<T>( this Component self ) where T : Component
{
	foreach( var n in self.GetComponents<T>() )
	{
		GameObject.Destroy( n );
	}
}
```

`iTween`のように関数を呼び出すたびにコンポーネントがアタッチされるプラグインを使用している時に
一括でそれらのコンポーネントを削除したいことが時々あったのでこの拡張メソッドを作成しました

```cs
gameObject.RemoveComponents<iTween>();
```

### HasComponent

指定されたコンポーネントがアタッチされている場合 true を返します

```cs
public static bool HasComponent<T>( this GameObject self ) where T : Component
{
	return self.GetComponent<T>() != null;
}

public static bool HasComponent<T>( this Component self ) where T : Component
{
	return self.GetComponent<T>() != null;
}
```

コンポーネントがアタッチされているかどうかは確認したいけれど
コンポーネント自体は必要ない場合にこの拡張メソッドを使用しました

```cs
if ( gameObject.HasComponent<UISprite>() )
{
}
```

戻り値のnullチェックを省略できるので少しだけコードをわかりやすく記述できます

### Find

指定された名前で子オブジェクトを検索します

```cs
public static Transform Find( this GameObject self, string name )
{
	return self.transform.Find( name );
}

public static Transform Find( this Component self, string name )
{
	return self.transform.Find( name );
}
```

通常、ゲームオブジェクトや`MonoBehaviour`を継承した独自のコンポーネントから
子オブジェクトを検索したい時は`transform`プロパティを介して`Find`関数を呼び出します

```cs
var player = gameObject.transform.Find( "Player" );
```

このように、子オブジェクトを検索する時に毎回`transform`プロパティを介すことが煩わしく感じたので
この拡張メソッドを作成して次のように書けるようにしました

```cs
var player = gameObject.Find( "Player" );
```

### FindGameObject

指定された名前で子オブジェクトを検索して GameObject 型で返します

```cs
public static GameObject FindGameObject( this GameObject self, string name )
{
	var result = self.transform.Find( name );
	return result != null ? result.gameObject : null;
}

public static GameObject FindGameObject( this Component self,  string name )
{
	var result = self.transform.Find( name );
	return result != null ? result.gameObject : null;
}
```

`transform.Find`は`Transform`型で検索結果を返してくれますが
子オブジェクトを検索した後は`transform.gameObject`にアクセスすることが多かったです

```cs
var player = gameObject.transform.Find( "Player" ).gameObject;
```

なので、`GameObject`型で検索結果を返してくれるこの拡張メソッドを作成しました
この拡張メソッドを使用することで次のように検索処理を記述できます

```cs
var player = gameObject.FindGameObject( "Player" );
```

### FindComponent

指定された名前で子オブジェクトを検索して
その子オブジェクトから指定されたコンポーネントを取得します

```cs
public static T FindComponent<T>( this GameObject self, string name ) where T : Component
{
	var t = self.transform.Find( name );
	if ( t == null )
	{
		return null;
	}
	return t.GetComponent<T>();
}

public static T FindComponent<T>( this Component self, string name ) where T : Component
{
	var t = self.transform.Find( name );
	if ( t == null )
	{
		return null;
	}
	return t.GetComponent<T>();
}
```

子オブジェクトを検索した後は`GetComponent`を呼び出すことも多かったです

```cs
var player = gameObject.transform.Find( "Player" ).GetComponent<Player>();
```

この拡張メソッドを使用することで次のように少しだけ簡潔に処理を記述できます

```cs
var player = gameObject.FindComponent<Player>( "Player" );
```

### FindDeep

指定された名前で子オブジェクトを深い階層まで検索して GameObject 型で返します

```cs
public static GameObject FindDeep( this GameObject self, string name, bool includeInactive = false )
{
	var children = self.GetComponentsInChildren<Transform>( includeInactive );
	foreach ( var transform in children )
	{
		if ( transform.name == name )
		{
			return transform.gameObject;
		}
	}
	return null;
}

public static GameObject FindDeep( this Component self, string name, bool includeInactive = false )
{
	var children = self.GetComponentsInChildren<Transform>( includeInactive );
	foreach ( var transform in children )
	{
		if ( transform.name == name )
		{
			return transform.gameObject;
		}
	}
	return null;
}
```

`transform.Find`で孫オブジェクトを検索する場合は次のように親子関係も指定する必要があります

```cs
var player = transform.Find( "Character/Player" );
```

この拡張メソッドを使用することで親子関係を意識せずに対象のオブジェクトの名前だけで孫オブジェクトを検索できます

```cs
var player = transform.FindDeep( "Player" );
```

ただし、同じ名前のオブジェクトが2つ以上存在する場合はどちらか片方しか取得されないので注意が必要です

### SetParent

親オブジェクトを設定します

```cs
public static void SetParent( this GameObject self, Component parent )
{
	self.transform.SetParent( parent.transform );
}

public static void SetParent( this GameObject self, GameObject parent )
{
	self.transform.SetParent( parent.transform );
}

public static void SetParent( this Component self, Component parent )
{
	self.transform.SetParent( parent.transform );
}

public static void SetParent( this Component self, GameObject parent )
{
	self.transform.SetParent( parent.transform );
}
```

Unity4.6で追加された`SetParent`は`Transform`型のインスタンスを引数に取るため
親オブジェクトを設定したいときは毎回`transform`プロパティを渡す必要がありました

```cs
player.SetParent( parent.transform );
```

その手間を解消するために、`GameObject`型のインスタンスも`Component`型のインスタンスも受け取れる
`SetParent`関数を拡張メソッドで実現しました

```cs
player.SetParent( parent );
```

### HasChild

子オブジェクトが存在するかどうかを返します

```cs
public static bool HasChild( this GameObject self )
{
	return 0 < self.transform.childCount;
}

public static bool HasChild( this Component self )
{
	return 0 < self.transform.childCount;
}
```

再帰処理などで子オブジェクトが存在する場合にのみ何か処理をしたい場合に
毎回`transform.childCount`を参照して子オブジェクトが存在するかどうかを
判定する手間を省くためにこの拡張メソッドを作成しました

```cs
if ( gameObject.HasChild() )
```

### HasParent

親オブジェクトが存在するかどうかを返します

```cs
public static bool HasParent( this GameObject self )
{
	return self.transform.parent != null;
}

public static bool HasParent( this Component self )
{
	return self.transform.parent != null;
}
```

こちらも前述の`HasChild`と同じように
毎回`transform.parent`を参照してnullチェックする手間を省くために作成しました

```cs
if ( gameObject.HasParent() )
```

### GetChild

指定されたインデックスの子オブジェクトを返します

```cs
public static GameObject GetChild( this GameObject self, int index )
{
	var t = self.transform.GetChild( index );
	return t != null ? t.gameObject : null;
}

public static GameObject GetChild( this Component self, int index )
{
	var t = self.transform.GetChild( index );
	return t != null ? t.gameObject : null;
}
```

`transform`プロパティを参照しなくても
`GetChild`関数を呼び出せるようにするためにこの拡張メソッドを作成しました

```cs
var child = gameObject.GetChild( 25 );
```

### GetParent

親オブジェクトを返します

```cs
public static GameObject GetParent( this GameObject self )
{
	var t = self.transform.parent;
	return t != null ? t.gameObject : null;
}

public static GameObject GetParent( this Component self )
{
	var t = self.transform.parent;
	return t != null ? t.gameObject : null;
}
```

`transform`プロパティを参照しなくても
`parent`プロパティを参照できるようにするためにこの拡張メソッドを作成しました

```cs
var parent = gameObject.GetParent();
```

### GetRoot

ルートとなるオブジェクトを返します

```cs
public static GameObject GetRoot( this GameObject self )
{
	var root = self.transform.root;
	return root != null ? root.gameObject : null;
}

public static GameObject GetRoot( this Component self )
{
	var root = self.transform.root;
	return root != null ? root.gameObject : null;
}
```

`transform`プロパティを参照しなくても
`root`プロパティを参照できるようにするためにこの拡張メソッドを作成しました

```cs
var root = gameObject.GetRoot();
```

### SetLayer

レイヤー名を使用してレイヤーを設定します

```cs
public static void SetLayer( this GameObject self, string layerName )
{
	self.layer = LayerMask.NameToLayer( layerName );
}

public static void SetLayer( this Component self, string layerName )
{
	self.gameObject.layer = LayerMask.NameToLayer( layerName );
}
```

もしゲームオブジェクトのレイヤーをレイヤー名で指定したい場合に
`LayerMask.NameToLayer`を呼び出す手間を省くためにこの拡張メソッドを作成しました
この拡張メソッドを使用することでレイヤー名でレイヤーを指定できます

```cs
gameObject.SetLayer( "UI" );
```

### SetLayerRecursively

自分自身を含めたすべての子オブジェクトのレイヤーを設定します

```cs
public static void SetLayerRecursively( this GameObject self, int layer )
{
	self.layer = layer;

	foreach ( Transform n in self.transform )
	{
		SetLayerRecursively( n.gameObject, layer );
	}
}

public static void SetLayerRecursively( this Component self, int layer )
{
	self.gameObject.layer = layer;

	foreach ( Transform n in self.gameObject.transform )
	{
		SetLayerRecursively( n, layer );
	}
}

public static void SetLayerRecursively( this GameObject self, string layerName )
{
	self.SetLayerRecursively( LayerMask.NameToLayer( layerName ) );
}

public static void SetLayerRecursively( this Component self, string layerName )
{
	self.SetLayerRecursively( LayerMask.NameToLayer( layerName ) );
}
```

ゲームオブジェクトのレイヤーを変更する場合は
子オブジェクトのレイヤーも一括で変更したいことが多いのでこの拡張メソッドを作成しました
これを使用することで自分自身を含めたすべての子オブジェクトのレイヤーを一括で設定できます

```cs
gameObject.SetLayerRecursively( 8 );
gameObject.SetLayerRecursively( "UI" );
```