2年目のゲームプログラマがゲーム会社でUnityを使用して
スマートフォン向けのゲームを開発して感じたことをまとめておきます
- 【Unity】ゲーム会社でスマホ向けゲームを開発して得た知識 コーディング編
- 【Unity】ゲーム会社でスマホ向けゲームを開発して得た知識 拡張メソッド編
- 【Unity】ゲーム会社でスマホ向けゲームを開発して得た知識 デバッグメニュー編
- 【Unity】ゲーム会社でスマホ向けゲームを開発して得た知識 UI編
- 【Unity】ゲーム会社でスマホ向けゲームを開発して得た知識 アセットバンドル編
拡張メソッド
拡張メソッドを使用することで既存のクラスに関数を追加することができます
今回関わったゲーム開発では拡張メソッドを使用することでコードを短くわかりやすく記述できたので
定義して良かった拡張メソッドを紹介していきます
SafeGetComponent
指定されたコンポーネントを返します
指定されたコンポーネントがアタッチされていない場合は追加してから返します
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参照を防ぐことができます
var player = gameObject.SafeGetComponent<Player>();
GetChildren
すべての子オブジェクトを返します
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();
}
すべての子オブジェクトを検索して一括で処理したい時に役に立ちます
foreach ( var n in gameObject.GetChildren() )
{
}
GetChildrenWithoutGrandchildren
孫オブジェクトを除くすべての子オブジェクトを返します
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
は孫オブジェクトも取得してしまうため
孫オブジェクトは検索対象に含めたくない場合はこの拡張メソッドを使用しました
foreach ( var n in gameObject.GetChildrenWithoutGrandchild() )
{
}
GetComponentsInChildrenWithoutSelf
自分自身を除くすべての子オブジェクトにアタッチされている指定されたコンポーネントをすべて返します
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
は自分自身も検索対象にしてしまうため
自分自身は検索対象に含めたくない場合にこの拡張メソッドを使用しました
var uiWidgetList = gameObject.GetComponentsInChildrenWithoutSelf<UIWidget>();
RemoveComponent
指定されたコンポーネントを削除します
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
を使用しますが
コンポーネントの削除を直感的に記述したかったのでこの拡張メソッドを用意しました
gameObject.RemoveComponent<Character>();
RemoveComponentImmediate
指定されたコンポーネントを即座に削除します
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>() );
}
エディタ拡張で即座にコンポーネントを削除したいときはこちらの拡張メソッドを使用しました
gameObject.RemoveComponentImmediate<Character>();
RemoveComponents
指定されたコンポーネントをすべて削除します
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
のように関数を呼び出すたびにコンポーネントがアタッチされるプラグインを使用している時に
一括でそれらのコンポーネントを削除したいことが時々あったのでこの拡張メソッドを作成しました
gameObject.RemoveComponents<iTween>();
HasComponent
指定されたコンポーネントがアタッチされている場合 true を返します
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;
}
コンポーネントがアタッチされているかどうかは確認したいけれど
コンポーネント自体は必要ない場合にこの拡張メソッドを使用しました
if ( gameObject.HasComponent<UISprite>() )
{
}
戻り値のnullチェックを省略できるので少しだけコードをわかりやすく記述できます
Find
指定された名前で子オブジェクトを検索します
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
関数を呼び出します
var player = gameObject.transform.Find( "Player" );
このように、子オブジェクトを検索する時に毎回transform
プロパティを介すことが煩わしく感じたので
この拡張メソッドを作成して次のように書けるようにしました
var player = gameObject.Find( "Player" );
FindGameObject
指定された名前で子オブジェクトを検索して GameObject 型で返します
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
にアクセスすることが多かったです
var player = gameObject.transform.Find( "Player" ).gameObject;
なので、GameObject
型で検索結果を返してくれるこの拡張メソッドを作成しました
この拡張メソッドを使用することで次のように検索処理を記述できます
var player = gameObject.FindGameObject( "Player" );
FindComponent
指定された名前で子オブジェクトを検索して
その子オブジェクトから指定されたコンポーネントを取得します
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
を呼び出すことも多かったです
var player = gameObject.transform.Find( "Player" ).GetComponent<Player>();
この拡張メソッドを使用することで次のように少しだけ簡潔に処理を記述できます
var player = gameObject.FindComponent<Player>( "Player" );
FindDeep
指定された名前で子オブジェクトを深い階層まで検索して GameObject 型で返します
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
で孫オブジェクトを検索する場合は次のように親子関係も指定する必要があります
var player = transform.Find( "Character/Player" );
この拡張メソッドを使用することで親子関係を意識せずに対象のオブジェクトの名前だけで孫オブジェクトを検索できます
var player = transform.FindDeep( "Player" );
ただし、同じ名前のオブジェクトが2つ以上存在する場合はどちらか片方しか取得されないので注意が必要です
SetParent
親オブジェクトを設定します
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
プロパティを渡す必要がありました
player.SetParent( parent.transform );
その手間を解消するために、GameObject
型のインスタンスもComponent
型のインスタンスも受け取れる
SetParent
関数を拡張メソッドで実現しました
player.SetParent( parent );
HasChild
子オブジェクトが存在するかどうかを返します
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
を参照して子オブジェクトが存在するかどうかを
判定する手間を省くためにこの拡張メソッドを作成しました
if ( gameObject.HasChild() )
HasParent
親オブジェクトが存在するかどうかを返します
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チェックする手間を省くために作成しました
if ( gameObject.HasParent() )
GetChild
指定されたインデックスの子オブジェクトを返します
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
関数を呼び出せるようにするためにこの拡張メソッドを作成しました
var child = gameObject.GetChild( 25 );
GetParent
親オブジェクトを返します
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
プロパティを参照できるようにするためにこの拡張メソッドを作成しました
var parent = gameObject.GetParent();
GetRoot
ルートとなるオブジェクトを返します
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
プロパティを参照できるようにするためにこの拡張メソッドを作成しました
var root = gameObject.GetRoot();
SetLayer
レイヤー名を使用してレイヤーを設定します
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
を呼び出す手間を省くためにこの拡張メソッドを作成しました
この拡張メソッドを使用することでレイヤー名でレイヤーを指定できます
gameObject.SetLayer( "UI" );
SetLayerRecursively
自分自身を含めたすべての子オブジェクトのレイヤーを設定します
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 ) );
}
ゲームオブジェクトのレイヤーを変更する場合は
子オブジェクトのレイヤーも一括で変更したいことが多いのでこの拡張メソッドを作成しました
これを使用することで自分自身を含めたすべての子オブジェクトのレイヤーを一括で設定できます
gameObject.SetLayerRecursively( 8 );
gameObject.SetLayerRecursively( "UI" );