メインコンテンツまでスキップ

1. 通常のInstantiate

Unityでオブジェクトを生成する基本的な方法は、Instantiateメソッドです。オブジェクトをプレファブ(Prefab)として保存し、ゲーム内で必要なときにそれを動的に生成します。

基本的な手順

  1. プレファブの準備
    生成したいオブジェクトをプレファブ化して保存します。

  2. Instantiateの使用
    スクリプトからInstantiateを呼び出して、プレファブを生成します。

サンプルコード

using UnityEngine;

public class InstantiateExample : MonoBehaviour
{
public GameObject prefab; // プレファブへの参照

void Update()
{
if (Input.GetMouseButtonDown(0)) // クリックでプレファブ生成
{
Instantiate(prefab, new Vector3(0, 0, 0), Quaternion.identity);
}
}
}





2. GPU Instancing (GPUインスタンシング)

通常の`Instantiateの問題になりうる点

  • パフォーマンス低下: 大量のオブジェクトを生成・管理すると、CPU負荷が高くなる。
  • メモリ使用量: 各オブジェクトが個別にメモリを使用するため、大量の生成はメモリを圧迫する。

GPU Instancingは、同じメッシュ(モデル)を複数描画する際に、GPU側で効率的に処理する方法です。通常のInstantiateでは1つ1つのオブジェクトが個別に描画されますが、GPU Instancingを使うと同じメッシュを一度だけ送信し、複数の位置にインスタンスを描画できます。

基本的な手順

  1. マテリアルの設定
    GPU Instancingを使用するには、インスタンシング対応のマテリアルを使用します。Enable GPU Instancingにチェックを入れることで対応可能です。

  2. 同じメッシュを複数描画
    同じメッシュを異なる位置に効率的に描画できます。以下のコードは同じモデルを100個の異なる位置に描画する例です。

サンプルコード

using UnityEngine;

public class GPUInstancingExample : MonoBehaviour
{
public Mesh mesh; // 描画するメッシュ
public Material material; // マテリアル (GPU Instancing対応)

void Start()
{
for (int i = 0; i < 100; i++)
{
Vector3 position = new Vector3(i * 1.5f, 0, 0);
Graphics.DrawMesh(mesh, position, Quaternion.identity, material, 0);
}
}
}

メリット

  • パフォーマンス向上: 大量のオブジェクトを効率的に描画できる。
  • CPU負荷軽減: 同じメッシュを使うため、CPUでの計算を減らすことができる。

注意点

  • インスタンス化されるオブジェクトは、すべて同じマテリアルとメッシュを使用する必要があります。異なるメッシュやマテリアルを使用する場合、別の手法が必要です。





3. RenderMeshInstanced (DOTSを用いたインスタンシング)

Unityの「Data-Oriented Technology Stack (DOTS)」の中で提供されているRenderMeshInstancedは、大量のオブジェクトを管理・描画する際に使用される高効率な方法です。これはECS(Entity Component System)アーキテクチャに基づいており、数万〜数百万のオブジェクトを効率的に描画することが可能です。

基本的な手順

  1. EntityとComponentの準備
    DOTSでは、オブジェクトはEntityとして扱い、Componentとしてメッシュやトランスフォーム情報を管理します。

  2. RenderMeshInstancedの使用
    RenderMeshInstancedを使って、同じメッシュを大量に描画します。

メリット

  • 大規模なオブジェクト管理: ECSによって、大量のオブジェクトを管理しやすくなり、描画パフォーマンスが向上します。
  • 効率的なメモリ使用: オブジェクトごとのメモリ使用量を抑えながら、大規模なシーンを構築可能。

注意点

  • DOTSの習得には時間がかかる可能性があり、プロジェクトによっては導入のための追加労力が必要です。





4. Compute Shaderを用いたインスタンシング

Compute Shaderは、GPU上で独自の並列処理を行うプログラムです。大量のオブジェクトの位置計算や動的なエフェクトをGPUで効率的に処理できるため、大規模なオブジェクトの管理やアニメーションにも適しています。

基本的な手順

  1. Compute Shaderの作成
    UnityのShaderフォルダにCompute Shaderを作成し、必要な計算を記述します。

  2. バッファのセットアップ
    GPUとCPU間でデータをやり取りするために、バッファ(ComputeBuffer)を設定します。

  3. シェーダーの実行
    スクリプトでDispatchメソッドを使ってシェーダーを実行し、計算結果を取得します。

サンプルコード

using UnityEngine;

public class ComputeShaderExample : MonoBehaviour
{
public ComputeShader computeShader;
public GameObject prefab;
private ComputeBuffer positionsBuffer;
private Vector3[] positions;

void Start()
{
int count = 1000;
positions = new Vector3[count];
positionsBuffer = new ComputeBuffer(count, sizeof(float) * 3);
positionsBuffer.SetData(positions);

// コンピュートシェーダーを実行
computeShader.SetBuffer(0, "positions", positionsBuffer);
computeShader.Dispatch(0, count / 10, 1, 1);

// 結果を取得し、インスタンスを配置
positionsBuffer.GetData(positions);
for (int i = 0; i < count; i++)
{
Instantiate(prefab, positions[i], Quaternion.identity);
}
}

void OnDestroy()
{
// バッファの解放
positionsBuffer.Release();
}
}

メリット

  • 並列処理: GPUの並列処理を利用して、大量のデータを効率的に処理できる。
  • パフォーマンス向上: CPUで行っていた計算処理をGPUにオフロードすることで、パフォーマンスが向上します。





5. 画面の効率化(メモリとパフォーマンスの最適化)

カリング(Culling)を使用する

オブジェクトがカメラに映っていない場合、描画する必要がないため、カリング(視界外のオブジェクトを無視)を利用します。Occlusion CullingFrustum Cullingを使うことで、描画コストを抑えることができます。

  • Frustum Culling: カメラの視錐台外にあるオブジェクトを描画しない。
  • Occlusion Culling: 他のオブジェクトに隠れて見えないオブジェクトを描画しない。

LOD (Level of Detail) の使用

LODを使うと、カメラに近いオブジェクトは高解像度のメッシュで描画し、遠いオブジェクトは低解像度のメッシュで描画することでパフォーマンスを向上させます。これにより、遠景のオブジェクトが詳細すぎて無駄な描画をしないようにできます。

LODの設定例

  1. オブジェクトにLOD Groupコンポーネントを追加します。
  2. 複数のメッシュを設定し

、距離ごとに異なるメッシュを描画するようにします。






その他

プールリング(Object Pooling)

生成と破棄が頻繁に行われるオブジェクト(弾丸やエフェクトなど)は、毎回InstantiateDestroyを行うとパフォーマンスが悪化します。これを防ぐために、オブジェクトプールを使って一度生成したオブジェクトを再利用する手法があります。

サンプルコード(オブジェクトプール)

using UnityEngine;
using System.Collections.Generic;

public class ObjectPool : MonoBehaviour
{
public GameObject prefab;
private Queue<GameObject> pool = new Queue<GameObject>();

public GameObject GetObject()
{
if (pool.Count > 0)
{
GameObject obj = pool.Dequeue();
obj.SetActive(true);
return obj;
}
return Instantiate(prefab);
}

public void ReturnObject(GameObject obj)
{
obj.SetActive(false);
pool.Enqueue(obj);
}
}







おまけ
Unity6
【Unity】非同期にインスタンスを大量生成!新 API "InstantiateAsync"