i-school - パフォーマンス最適化のポイント
 ゲーム開発において重要な、パフォーマンス最適化のポイントについて詳しく解説します。

 以下の4つのポイントについて、それぞれの概要とサンプルコードを提示します。

 1.ガベージコレクションの回避
 2.非同期処理の活用
 3.プールパターンの使用
 4.データ構造やアルゴリズムの選択



1.ガベージコレクションの回避


 ガベージコレクション(GC)は、不要になったメモリを解放する仕組みですが、GCが発生するとパフォーマンスに影響を与えます
GCの回避方法として、以下の点に注意しましょう。

 ・一時的なオブジェクトの生成を減らす
 ・配列やリストの事前確保や再利用を行う
 ・クラスを構造体に変更する場合がある

 ここではガベージコレクション(GC)を回避するための2つのサンプルコードについて提示し、詳しく解説します。


1.一時的なオブジェクトの生成を減らす


 このサンプルコードは、オブジェクトの位置を更新する際に、一時的なオブジェクトの生成を減らすことでGCの回避を図る例です。

クリックすると開きます


 このコードでは、オブジェクトの現在の位置を取得し、そのx座標に1.0f * Time.deltaTime(1秒間に1ユニット移動する)を加算しています。
そして、計算された新しい位置をオブジェクトに適用しています。

 この方法では、新しいVector3オブジェクトを毎フレーム生成する代わりに、現在の位置を直接変更することで一時的なオブジェクトの生成を減らし効率化を図っています


2.配列やリストの事前確保や再利用を行う


 このサンプルコードは、敵キャラクターのオブジェクトをスポーンする際に、事前にリストを確保し、再利用することでGCの回避を図る例です。


クリックすると開きます


 このコードでは、まず100個の敵オブジェクトを格納できるリストを作成しています。
そして、SpawnEnemiesメソッドで指定された数の敵をスポーンさせる際、リストの中に既に存在するオブジェクトを再利用します。
リストに不足分がある場合のみ、新しい敵オブジェクトをインスタンス化してリストに追加します。

 これにより、敵オブジェクトの生成と破棄を繰り返すことで発生するGCのオーバーヘッドを最小限に抑えることができます。



 これらのサンプルコードを適用することでゲーム開発において、ガベージコレクションの回避によりパフォーマンスが向上します。
特に、リアルタイムの処理が求められるゲームでは、こうした最適化がプレイヤーに快適なゲームプレイ体験を提供する上で非常に重要です。

 GCの回避を意識したコーディングを行うことで、効率的で高品質なコードを作成することができるようになります。


2.非同期処理の活用


 非同期処理を活用することで、メインスレッドの負荷を軽減し、パフォーマンスを向上させることができます。
特に、重い処理やネットワーク通信などは非同期で行うことが重要です。

 ここでは非同期処理を行うサンプルコードについて詳しく解説します。
非同期処理を活用することで、パフォーマンスを最適化し、ユーザーに快適なゲームプレイ体験を提供することができます。


1.非同期処理を用いたデータの取得と処理


 このサンプルコードは、UniTaskを使用してデータを非同期で取得し、処理する例です。

クリックすると開きます


 このコードでは、まずLoadDataAsyncメソッドが呼ばれると、非同期でデータを取得するFetchDataAsyncメソッドが実行されます。
FetchDataAsyncメソッドでは、UnityWebRequestを使用して、指定されたURLからデータを取得します。
このデータ取得処理は非同期で行われるため、メインスレッドがブロックされることなく、他の処理を並行して行うことができます。

 データ取得が完了すると、LoadDataAsyncメソッド内でProcessDataメソッドが呼ばれ、取得したデータを処理します。
このように、非同期処理を活用することで、パフォーマンスの低下やUIのフリーズを防ぐことができます。


3.プールパターンの使用


 プールパターンは、オブジェクトの生成と破棄に伴うコストを削減するためのデザインパターンです。
オブジェクトプールとも呼ばれます。

 オブジェクトを再利用することで、GCの回避やパフォーマンス向上につながります


1.オブジェクトプールを実装したObjectPoolクラス


 このサンプルコードは、オブジェクトプールを実現するためのObjectPoolクラスの実装例です。

クリックすると開きます


 このコードでは、まずStartメソッドで初期化時にオブジェクトプールに指定された数のオブジェクトを生成し、リストpooledObjectsに格納しています。
この時点では、生成されたオブジェクトは非アクティブ状態です。

 GetObjectメソッドでは、リストである pooledObjects 内から非アクティブなオブジェクトを探し、アクティブにして返します
リスト内に利用可能なオブジェクトがない場合は、新しいオブジェクトを生成し、リストに追加して返します。
これにより、オブジェクト生成のオーバーヘッドを最小限に抑えることができます。

 ReturnObjectメソッドでは、使用済みのオブジェクトを非アクティブにし、再利用可能な状態に戻します

 つまり、使わなくなったゲームオブジェクトをその都度破棄するのではなく、再利用するために非アクティブの状態にして保持しておく構造です。


4.データ構造やアルゴリズムの選択


 適切なデータ構造やアルゴリズムを選択することで、パフォーマンスを最適化することができます。
例えば、検索が多い場合はハッシュセットを使う、要素の追加・削除が多い場合はリンクリストを使うなど、状況に応じた選択が重要です。


1.データの検索が多い場合のハッシュセットの使用例


 このサンプルコードは、データ検索が多い場合にハッシュセットを使用する例です。


クリックすると開きます


 ハッシュセットは、データの追加・削除・検索が高速に行えるデータ構造です。

 このコードでは、itemSetというハッシュセットにアイテムを追加するAddItemメソッドと、
アイテムが含まれているかどうかを検索するHasItemメソッドが実装されています。

 データの検索が多い場合にハッシュセットを使用することで、高速な処理が可能になります。


2.要素の追加・削除が多い場合のリンクリストの使用例


 このサンプルコードは、要素の追加・削除が多い場合にリンクリストを使用する例です。


クリックすると開きます


 リンクリストは、要素の追加・削除が高速に行えるデータ構造です。

 このコードでは、numberListというリンクリストに数値を追加するAddNumberメソッドと、
数値を削除するRemoveNumberメソッドが実装されています。

 要素の追加・削除が多い場合にリンクリストを使用することで、高速な処理が可能になります。


 まとめ


 これらのパフォーマンス最適化のポイントを理解し、実践することで、
ゲーム開発において、より効率的で高品質なコードを作成することができます。

 また、プレイヤーに快適なゲームプレイ体験を提供することができるようになります。