Unityでゲーム制作を始めようとしたとき、「オブジェクトってどうやって作るの?」と戸惑った方も多いのではないでしょうか?
オブジェクトは、キャラクター、背景、UIなど、あらゆる要素の”土台”となる存在です。
この記事では、Unityにおけるオブジェクト作成の基本操作から、スクリプトを使った動的生成まで、プログラミング初心者にも分かるように詳しく解説します。
Unityにおけるオブジェクトの基礎知識

GameObjectとは何か?
Unityでは、ゲーム内の見えるもの・見えないものすべてが「GameObject(ゲームオブジェクト)」として扱われます。こ
れはUnity開発の最も重要な概念の一つです。
GameObjectの基本概念
- 基本単位:ゲーム内のすべての要素の基礎
- コンテナ:様々な機能(コンポーネント)を入れる箱
- 階層構造:親子関係を持つことができる
- 一意性:各オブジェクトは固有のIDを持つ
GameObjectの例
- プレイヤーキャラクター:3Dモデル + アニメーション + 移動スクリプト
- 敵キャラクター:モデル + AI + 当たり判定
- 背景オブジェクト:3Dモデル + テクスチャ
- UI要素:ボタン + テキスト + クリックイベント
- カメラ:視点制御 + 描画設定
- ライト:照明効果 + 影の設定
コンポーネントシステム
コンポーネントとは
GameObjectに機能を付け加える”パーツ”のことです。レゴブロックのように、必要な機能を組み合わせて複雑なオブジェクトを作ります。
主要なコンポーネント
描画関連
- Mesh Renderer:3Dモデルの描画
- Sprite Renderer:2D画像の描画
- Material:表面の質感やテクスチャ
物理関連
- Rigidbody:物理演算(重力、衝突)
- Collider:当たり判定
- Physics Material:摩擦や反発の設定
動作関連
- Transform:位置、回転、スケール
- Animator:アニメーション制御
- Script:カスタム動作
例:ボールオブジェクトの構成
GameObject: "Ball"
├── Transform(位置・回転・大きさ)
├── Mesh Renderer(球の見た目)
├── Sphere Collider(球状の当たり判定)
├── Rigidbody(物理演算)
└── BallController(カスタムスクリプト)
プレハブ(Prefab)の概念
プレハブとは 再利用可能なオブジェクトの”設計図”です。一度作成すれば、何度でも同じオブジェクトを生成できます。
プレハブの利点
- 効率性:同じオブジェクトを簡単に複製
- 一括変更:プレハブを修正すると全インスタンスに反映
- 管理性:オブジェクトの種類を整理しやすい
- 再利用性:他のシーンやプロジェクトでも使用可能
プレハブの活用例
- 敵キャラクター:同じ敵を複数配置
- アイテム:コインやポーションなど
- UI要素:ボタンやパネル
- エフェクト:爆発や魔法のエフェクト
Unityエディタでのオブジェクト作成方法

基本的な作成手順
手順1:メニューからオブジェクト作成
- Unityエディタを開く
- メニューバーの「GameObject」をクリック
- 作成したいオブジェクトのカテゴリを選択
- 具体的なオブジェクトタイプを選択
手順2:Hierarchyビューでの確認
- 作成されたオブジェクトがHierarchyビューに表示される
- オブジェクトの名前をダブルクリックで変更可能
- ドラッグ&ドロップで親子関係を設定
手順3:Sceneビューでの配置
- Sceneビューでオブジェクトの位置を確認
- **移動ツール(W)**でオブジェクトを移動
- **回転ツール(E)**で向きを変更
- **スケールツール(R)**で大きさを調整
よく使う3Dオブジェクト
基本形状オブジェクト
Cube(立方体)
用途:
- プラットフォーム
- 建物のベース
- テスト用オブジェクト
- 当たり判定の確認
作成方法:
GameObject → 3D Object → Cube
Sphere(球体)
用途:
- ボール
- 惑星
- 爆発エフェクトの中心
- 敵キャラクターの簡易モデル
作成方法:
GameObject → 3D Object → Sphere
Capsule(カプセル)
用途:
- キャラクターの当たり判定
- 柱や円柱状のオブジェクト
- プレイヤーの仮モデル
作成方法:
GameObject → 3D Object → Capsule
Plane(平面)
用途:
- 地面
- 壁
- UI背景
- 水面
作成方法:
GameObject → 3D Object → Plane
2Dオブジェクトの作成
Sprite(スプライト)
用途:
- 2Dキャラクター
- 背景画像
- UIアイコン
- 2Dゲームの要素
作成手順:
1. GameObject → 2D Object → Sprite
2. Inspectorで"Sprite"フィールドに画像を設定
3. 画像のImport Settingsで"Sprite (2D and UI)"に設定
特殊なオブジェクト
Empty GameObject
用途:
- オブジェクトグループの親
- スクリプト専用オブジェクト
- マネージャーオブジェクト
- エフェクトの発生点
特徴:
- 見た目はない(Transformのみ)
- 他のオブジェクトの管理に使用
- 軽量で処理負荷が少ない
Light(ライト)
種類:
- Directional Light:太陽光(平行光)
- Point Light:電球のような点光源
- Spot Light:懐中電灯のようなスポット光
- Area Light:面光源(Baked専用)
作成方法:
GameObject → Light → [ライトの種類]
Camera(カメラ)
用途:
- プレイヤーの視点
- ミニマップ用カメラ
- セキュリティカメラ演出
- UI専用カメラ
設定項目:
- Field of View:視野角
- Clipping Planes:描画距離
- Culling Mask:描画するレイヤー
Inspectorでのオブジェクト設定

Transformコンポーネント
Position(位置)
X軸:左右(Left ← → Right)
Y軸:上下(Down ← → Up)
Z軸:前後(Back ← → Forward)
設定例:
Position (0, 0, 0):原点
Position (5, 2, -3):右に5、上に2、手前に3
Rotation(回転)
オイラー角での指定:
X軸回転:ピッチ(うなずき)
Y軸回転:ヨー(首振り)
Z軸回転:ロール(首かしげ)
設定例:
Rotation (0, 90, 0):Y軸に90度回転
Rotation (45, 0, 0):X軸に45度回転
Scale(スケール)
各軸の拡大縮小率:
Scale (1, 1, 1):等倍(デフォルト)
Scale (2, 2, 2):2倍に拡大
Scale (0.5, 1, 0.5):X・Z軸を半分に縮小
注意点:
- 負の値で反転可能
- 物理演算への影響を考慮
オブジェクトの階層管理
親子関係の設定
- Hierarchyビューで子にしたいオブジェクトを選択
- 親にしたいオブジェクトにドラッグ&ドロップ
- 子オブジェクトがインデントされて表示される
親子関係の効果
- Transform継承:親の移動・回転・スケールが子に影響
- 一括操作:親を選択すると子も一緒に操作
- 組織化:関連オブジェクトをグループ化
実践例:車のオブジェクト構成
Car (親)
├── Body(車体)
├── Wheels(タイヤグループ)
│ ├── FrontLeft
│ ├── FrontRight
│ ├── RearLeft
│ └── RearRight
├── Lights(ライトグループ)
│ ├── Headlight_L
│ ├── Headlight_R
│ ├── Taillight_L
│ └── Taillight_R
└── Engine(エンジン音)
スクリプトによるオブジェクト動的生成
基本的なInstantiate()の使用法
基本構文
// 基本的なオブジェクト生成
Instantiate(生成するオブジェクト, 位置, 回転);
// 戻り値を受け取る場合
GameObject newObject = Instantiate(prefab, position, rotation);
シンプルな生成スクリプト
using UnityEngine;
public class SimpleObjectCreator : MonoBehaviour
{
// プレハブをInspectorで設定
public GameObject prefab;
void Start()
{
// 原点に回転なしで生成
Instantiate(prefab, Vector3.zero, Quaternion.identity);
}
}
より実用的な生成システム
敵キャラクターの自動生成
using UnityEngine;
public class EnemySpawner : MonoBehaviour
{
[Header("スポーン設定")]
public GameObject enemyPrefab; // 敵のプレハブ
public float spawnInterval = 2.0f; // 生成間隔(秒)
public int maxEnemies = 10; // 最大敵数
public Transform[] spawnPoints; // 生成ポイント
private float timer = 0f;
private int currentEnemyCount = 0;
void Update()
{
timer += Time.deltaTime;
// 生成条件をチェック
if (timer >= spawnInterval && currentEnemyCount < maxEnemies)
{
SpawnEnemy();
timer = 0f;
}
}
void SpawnEnemy()
{
// ランダムなスポーンポイントを選択
int randomIndex = Random.Range(0, spawnPoints.Length);
Transform spawnPoint = spawnPoints[randomIndex];
// 敵を生成
GameObject newEnemy = Instantiate(enemyPrefab,
spawnPoint.position,
spawnPoint.rotation);
// 生成した敵に名前をつける
newEnemy.name = "Enemy_" + currentEnemyCount;
// カウントを増やす
currentEnemyCount++;
Debug.Log("敵を生成しました: " + newEnemy.name);
}
}
アイテム生成システム
using UnityEngine;
public class ItemGenerator : MonoBehaviour
{
[Header("アイテム設定")]
public GameObject[] itemPrefabs; // 複数のアイテムプレハブ
public int itemCount = 20; // 生成するアイテム数
public Vector3 areaSize = new Vector3(20, 0, 20); // 生成エリア
void Start()
{
GenerateItems();
}
void GenerateItems()
{
for (int i = 0; i < itemCount; i++)
{
// ランダムな位置を計算
Vector3 randomPosition = new Vector3(
Random.Range(-areaSize.x/2, areaSize.x/2),
0,
Random.Range(-areaSize.z/2, areaSize.z/2)
);
// ランダムなアイテムを選択
int randomItemIndex = Random.Range(0, itemPrefabs.Length);
GameObject selectedPrefab = itemPrefabs[randomItemIndex];
// アイテムを生成
GameObject newItem = Instantiate(selectedPrefab,
transform.position + randomPosition,
Quaternion.identity);
// 親オブジェクトを設定(整理のため)
newItem.transform.SetParent(transform);
}
}
}
ボタンによるインタラクティブ生成
UI ボタンでオブジェクト生成
using UnityEngine;
using UnityEngine.UI;
public class InteractiveCreator : MonoBehaviour
{
[Header("UI要素")]
public Button createButton;
public Button deleteButton;
[Header("生成設定")]
public GameObject objectPrefab;
public Transform spawnPoint;
private GameObject lastCreatedObject;
void Start()
{
// ボタンイベントを設定
createButton.onClick.AddListener(CreateObject);
deleteButton.onClick.AddListener(DeleteLastObject);
}
public void CreateObject()
{
if (objectPrefab != null)
{
lastCreatedObject = Instantiate(objectPrefab,
spawnPoint.position,
spawnPoint.rotation);
Debug.Log("オブジェクトを作成しました!");
}
}
public void DeleteLastObject()
{
if (lastCreatedObject != null)
{
Destroy(lastCreatedObject);
lastCreatedObject = null;
Debug.Log("オブジェクトを削除しました!");
}
}
}
プレハブの作成と管理

プレハブの作成手順
手順1:オブジェクトの準備
- Hierarchyでオブジェクトを作成・設定
- 必要なコンポーネントを追加
- 材質(Material)やテクスチャを設定
- スクリプトをアタッチ
手順2:プレハブ化
- 完成したオブジェクトをHierarchyで選択
- Projectビューの任意のフォルダにドラッグ&ドロップ
- プレハブファイル(青いアイコン)が作成される
- 元のオブジェクトがプレハブインスタンスになる
手順3:プレハブの編集
- Projectビューでプレハブをダブルクリック
- プレハブ編集モードに入る
- 変更を加える
- 左上の「←」ボタンでシーンに戻る
プレハブバリアント
バリアントとは 元のプレハブをベースに、一部を変更した派生版を作る機能です。
作成方法
- 元のプレハブを右クリック
- 「Create → Prefab Variant」を選択
- バリアント用の設定を変更
- 元プレハブの変更がバリアントにも反映される
活用例
敵キャラクター(基本)
├── 敵_赤(バリアント): 色が赤、HPが高い
├── 敵_青(バリアント): 色が青、スピードが速い
└── 敵_金(バリアント): 色が金、特殊攻撃あり
プレハブのベストプラクティス
命名規則
分類_種類_詳細
例:
- Enemy_Goblin_Warrior
- Item_Potion_Health
- UI_Button_MainMenu
- Effect_Explosion_Fire
フォルダ構造
Assets/
├── Prefabs/
│ ├── Characters/
│ │ ├── Player/
│ │ └── Enemies/
│ ├── Items/
│ ├── UI/
│ └── Effects/
├── Scripts/
├── Materials/
└── Textures/
オブジェクト管理とパフォーマンス最適化
Object Pooling(オブジェクトプーリング)
概念 頻繁に生成・削除されるオブジェクトを事前に作成しておき、再利用する仕組みです。
メリット
- メモリ効率:ガベージコレクションの負荷軽減
- パフォーマンス:Instantiate/Destroyの処理時間削減
- 安定性:フレームレートの安定化
シンプルなオブジェクトプール
using System.Collections.Generic;
using UnityEngine;
public class SimpleObjectPool : MonoBehaviour
{
[Header("プール設定")]
public GameObject prefab;
public int poolSize = 50;
private Queue<GameObject> pool = new Queue<GameObject>();
void Start()
{
// プールを初期化
InitializePool();
}
void InitializePool()
{
for (int i = 0; i < poolSize; i++)
{
GameObject obj = Instantiate(prefab);
obj.SetActive(false);
pool.Enqueue(obj);
}
}
public GameObject GetFromPool()
{
if (pool.Count > 0)
{
GameObject obj = pool.Dequeue();
obj.SetActive(true);
return obj;
}
else
{
// プールが空の場合は新規作成
return Instantiate(prefab);
}
}
public void ReturnToPool(GameObject obj)
{
obj.SetActive(false);
pool.Enqueue(obj);
}
}
パフォーマンス監視
Stats ウィンドウでの確認
- Gameビューの右上「Stats」ボタンをクリック
- 以下の項目を確認:
- Tris(三角形数):描画負荷の指標
- Verts(頂点数):ジオメトリの複雑さ
- Batches:描画呼び出し回数
Profiler での詳細分析
- メニューバー「Window → Analysis → Profiler」
- CPU Usage:スクリプト処理時間
- Memory:メモリ使用量
- Rendering:描画処理時間
最適化のコツ
オブジェクト数の管理
// 最大オブジェクト数を制限
public class LimitedSpawner : MonoBehaviour
{
public int maxObjects = 100;
private int currentCount = 0;
public void SpawnObject()
{
if (currentCount < maxObjects)
{
// オブジェクト生成
currentCount++;
}
}
}
距離による最適化
// プレイヤーから遠いオブジェクトを非アクティブ化
public class DistanceOptimizer : MonoBehaviour
{
public float maxDistance = 50f;
private Transform player;
void Start()
{
player = GameObject.FindWithTag("Player").transform;
}
void Update()
{
float distance = Vector3.Distance(transform.position, player.position);
if (distance > maxDistance)
{
gameObject.SetActive(false);
}
}
}
トラブルシューティング

よくある問題と解決法
問題1:Instantiateでオブジェクトが見えない
原因と解決法
// 問題のあるコード
Instantiate(prefab, new Vector3(0, -1000, 0), Quaternion.identity);
// 解決法1:適切な位置に配置
Vector3 cameraPosition = Camera.main.transform.position;
Vector3 spawnPosition = cameraPosition + Vector3.forward * 5;
Instantiate(prefab, spawnPosition, Quaternion.identity);
// 解決法2:カメラの視野内で生成
Vector3 screenPosition = Camera.main.ScreenToWorldPoint(
new Vector3(Screen.width / 2, Screen.height / 2, 10));
Instantiate(prefab, screenPosition, Quaternion.identity);
問題2:スクリプトでprefabがnull
診断方法
public class SafeObjectCreator : MonoBehaviour
{
public GameObject prefab;
void Start()
{
// nullチェック
if (prefab == null)
{
Debug.LogError("プレハブが設定されていません!");
return;
}
// 安全に生成
GameObject newObject = Instantiate(prefab);
Debug.Log("オブジェクトを生成しました: " + newObject.name);
}
}
問題3:生成したオブジェクトが勝手に動く
原因:Rigidbodyコンポーネントによる物理演算
解決法
// 生成時に物理演算を制御
GameObject newObject = Instantiate(prefab, position, rotation);
Rigidbody rb = newObject.GetComponent<Rigidbody>();
if (rb != null)
{
rb.velocity = Vector3.zero; // 速度をリセット
rb.angularVelocity = Vector3.zero; // 回転をリセット
rb.useGravity = false; // 重力を無効(必要に応じて)
}
問題4:大量生成でパフォーマンス低下
解決法1:生成数の制限
public class PerformanceAwareSpawner : MonoBehaviour
{
public int maxObjectsPerFrame = 5;
private int objectsSpawnedThisFrame = 0;
void Update()
{
objectsSpawnedThisFrame = 0; // フレーム開始時にリセット
}
public bool TrySpawnObject(GameObject prefab, Vector3 position)
{
if (objectsSpawnedThisFrame < maxObjectsPerFrame)
{
Instantiate(prefab, position, Quaternion.identity);
objectsSpawnedThisFrame++;
return true;
}
return false;
}
}
解決法2:コルーチンによる分散処理
using System.Collections;
public class DelayedSpawner : MonoBehaviour
{
public void SpawnManyObjects(GameObject prefab, int count)
{
StartCoroutine(SpawnObjectsCoroutine(prefab, count));
}
IEnumerator SpawnObjectsCoroutine(GameObject prefab, int count)
{
for (int i = 0; i < count; i++)
{
Instantiate(prefab, Random.insideUnitSphere * 10, Quaternion.identity);
// 5個ごとに1フレーム待機
if (i % 5 == 0)
{
yield return null;
}
}
}
}
実践的な応用例

シューティングゲームの弾丸システム
弾丸生成とライフサイクル
public class BulletManager : MonoBehaviour
{
[Header("弾丸設定")]
public GameObject bulletPrefab;
public Transform firePoint;
public float bulletSpeed = 20f;
public float bulletLifetime = 5f;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
FireBullet();
}
}
void FireBullet()
{
// 弾丸を生成
GameObject bullet = Instantiate(bulletPrefab,
firePoint.position,
firePoint.rotation);
// 物理的な動きを追加
Rigidbody rb = bullet.GetComponent<Rigidbody>();
if (rb != null)
{
rb.velocity = firePoint.forward * bulletSpeed;
}
// 自動削除を設定
Destroy(bullet, bulletLifetime);
}
}
RPGのアイテムドロップシステム
確率的アイテム生成
[System.Serializable]
public class DropItem
{
public GameObject itemPrefab;
public float dropChance; // 0.0 〜 1.0 の確率
public int minAmount = 1;
public int maxAmount = 1;
}
public class ItemDropper : MonoBehaviour
{
[Header("ドロップ設定")]
public DropItem[] possibleDrops;
public float dropRadius = 2f;
public void TriggerDrop()
{
foreach (DropItem dropItem in possibleDrops)
{
// 確率判定
if (Random.Range(0f, 1f) <= dropItem.dropChance)
{
int amount = Random.Range(dropItem.minAmount,
dropItem.maxAmount + 1);
for (int i = 0; i < amount; i++)
{
// ランダムな位置にドロップ
Vector3 dropPosition = transform.position +
Random.insideUnitSphere * dropRadius;
dropPosition.y = transform.position.y; // Y軸は固定
GameObject droppedItem = Instantiate(dropItem.itemPrefab,
dropPosition, Quaternion.identity);
// 物理的な散らばりを追加
Rigidbody rb = droppedItem.GetComponent<Rigidbody>();
if (rb != null)
{
Vector3 randomForce = new Vector3(
Random.Range(-5f, 5f),
Random.Range(2f, 5f),
Random.Range(-5f, 5f));
rb.AddForce(randomForce, ForceMode.Impulse);
}
}
}
}
}
}
パーティクルシステムとの連携
エフェクト付きオブジェクト生成
public class EffectSpawner : MonoBehaviour
{
[Header("生成設定")]
public GameObject objectPrefab;
public ParticleSystem spawnEffect;
public AudioClip spawnSound;
public float effectDelay = 0.5f;
private AudioSource audioSource;
void Start()
{
audioSource = GetComponent<AudioSource>();
}
public void SpawnWithEffect(Vector3 position)
{
// エフェクトを再生
if (spawnEffect != null)
{
spawnEffect.transform.position = position;
spawnEffect.Play();
}
// 音を再生
if (spawnSound != null && audioSource != null)
{
audioSource.PlayOneShot(spawnSound);
}
// 少し遅れてオブジェクトを生成
StartCoroutine(DelayedSpawn(position));
}
IEnumerator DelayedSpawn(Vector3 position)
{
yield return new WaitForSeconds(effectDelay);
GameObject newObject = Instantiate(objectPrefab, position, Quaternion.identity);
// 生成時のアニメーション
StartCoroutine(SpawnAnimation(newObject));
}
IEnumerator SpawnAnimation(GameObject obj)
{
// 小さく始まって徐々に大きくなる
obj.transform.localScale = Vector3.zero;
Vector3 targetScale = Vector3.one;
float duration = 0.3f;
float elapsed = 0f;
while (elapsed < duration)
{
float progress = elapsed / duration;
obj.transform.localScale = Vector3.Lerp(Vector3.zero, targetScale, progress);
elapsed += Time.deltaTime;
yield return null;
}
obj.transform.localScale = targetScale;
}
}
デバッグとテスト手法

デバッグ用ヘルパー関数
オブジェクト生成の詳細ログ
public class DebugObjectCreator : MonoBehaviour
{
[Header("デバッグ設定")]
public bool enableDebugLog = true;
public bool showVisualGuides = true;
public GameObject CreateObjectWithDebug(GameObject prefab, Vector3 position, Quaternion rotation)
{
if (enableDebugLog)
{
Debug.Log($"オブジェクト生成開始: {prefab.name}");
Debug.Log($"位置: {position}");
Debug.Log($"回転: {rotation.eulerAngles}");
}
// 生成実行
GameObject newObject = Instantiate(prefab, position, rotation);
if (enableDebugLog)
{
Debug.Log($"生成完了: {newObject.name} (InstanceID: {newObject.GetInstanceID()})");
}
// 視覚的なガイド表示
if (showVisualGuides)
{
StartCoroutine(ShowSpawnGuide(position));
}
return newObject;
}
IEnumerator ShowSpawnGuide(Vector3 position)
{
// 生成位置に一時的なマーカーを表示
GameObject marker = GameObject.CreatePrimitive(PrimitiveType.Sphere);
marker.transform.position = position;
marker.transform.localScale = Vector3.one * 0.2f;
// マーカーを赤色に
Renderer renderer = marker.GetComponent<Renderer>();
renderer.material.color = Color.red;
// 1秒後に削除
yield return new WaitForSeconds(1f);
Destroy(marker);
}
}
テスト用のオブジェクト生成ツール
エディタ拡張でのテストツール
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
[System.Serializable]
public class TestSpawnSettings
{
public GameObject prefab;
public int count = 10;
public Vector3 areaSize = Vector3.one * 10;
public bool randomRotation = true;
}
public class ObjectTestSpawner : MonoBehaviour
{
[Header("テスト設定")]
public TestSpawnSettings[] testSettings;
[Space]
[Header("クリア設定")]
public bool clearOnTest = true;
void Update()
{
// テスト用ショートカット
if (Input.GetKeyDown(KeyCode.T))
{
TestSpawn(0);
}
}
public void TestSpawn(int settingIndex)
{
if (settingIndex >= testSettings.Length) return;
TestSpawnSettings settings = testSettings[settingIndex];
if (clearOnTest)
{
ClearAllTestObjects();
}
for (int i = 0; i < settings.count; i++)
{
Vector3 randomPosition = new Vector3(
Random.Range(-settings.areaSize.x/2, settings.areaSize.x/2),
Random.Range(-settings.areaSize.y/2, settings.areaSize.y/2),
Random.Range(-settings.areaSize.z/2, settings.areaSize.z/2)
);
Quaternion rotation = settings.randomRotation ?
Random.rotation : Quaternion.identity;
GameObject testObject = Instantiate(settings.prefab,
transform.position + randomPosition, rotation);
// テストオブジェクトにタグを付ける
testObject.tag = "TestObject";
}
}
public void ClearAllTestObjects()
{
GameObject[] testObjects = GameObject.FindGameObjectsWithTag("TestObject");
foreach (GameObject obj in testObjects)
{
DestroyImmediate(obj);
}
}
}
// カスタムインスペクター
[CustomEditor(typeof(ObjectTestSpawner))]
public class ObjectTestSpawnerEditor : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
ObjectTestSpawner spawner = (ObjectTestSpawner)target;
GUILayout.Space(10);
GUILayout.Label("テスト実行", EditorStyles.boldLabel);
for (int i = 0; i < spawner.testSettings.Length; i++)
{
if (GUILayout.Button($"テスト {i + 1}: {spawner.testSettings[i].prefab?.name}"))
{
spawner.TestSpawn(i);
}
}
GUILayout.Space(5);
if (GUILayout.Button("すべてクリア", GUILayout.Height(30)))
{
spawner.ClearAllTestObjects();
}
}
}
#endif
高度なオブジェクト生成テクニック

手続き的生成(Procedural Generation)
地形やダンジョンの自動生成
public class ProceduralDungeonGenerator : MonoBehaviour
{
[Header("ダンジョン設定")]
public GameObject floorPrefab;
public GameObject wallPrefab;
public GameObject pillarPrefab;
public int width = 20;
public int height = 20;
public float roomProbability = 0.4f;
[Header("部屋設定")]
public int minRoomSize = 3;
public int maxRoomSize = 8;
private bool[,] dungeonMap;
void Start()
{
GenerateDungeon();
}
void GenerateDungeon()
{
dungeonMap = new bool[width, height];
// 部屋を生成
GenerateRooms();
// 廊下を生成
GenerateCorridors();
// オブジェクトを配置
PlaceObjects();
}
void GenerateRooms()
{
int roomCount = Random.Range(5, 10);
for (int i = 0; i < roomCount; i++)
{
int roomWidth = Random.Range(minRoomSize, maxRoomSize);
int roomHeight = Random.Range(minRoomSize, maxRoomSize);
int x = Random.Range(1, width - roomWidth - 1);
int y = Random.Range(1, height - roomHeight - 1);
// 部屋エリアを床にマーク
for (int dx = 0; dx < roomWidth; dx++)
{
for (int dy = 0; dy < roomHeight; dy++)
{
dungeonMap[x + dx, y + dy] = true;
}
}
}
}
void GenerateCorridors()
{
// シンプルな廊下生成ロジック
for (int x = 1; x < width - 1; x++)
{
for (int y = 1; y < height - 1; y++)
{
if (Random.Range(0f, 1f) < 0.1f)
{
dungeonMap[x, y] = true;
}
}
}
}
void PlaceObjects()
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
Vector3 position = new Vector3(x, 0, y);
if (dungeonMap[x, y])
{
// 床を配置
Instantiate(floorPrefab, position, Quaternion.identity);
}
else
{
// 壁を配置
Instantiate(wallPrefab, position, Quaternion.identity);
}
// 角に柱を配置
if (IsCorner(x, y))
{
Instantiate(pillarPrefab, position + Vector3.up, Quaternion.identity);
}
}
}
}
bool IsCorner(int x, int y)
{
if (x == 0 || x == width - 1 || y == 0 || y == height - 1)
return false;
bool[] neighbors = {
dungeonMap[x-1, y-1], dungeonMap[x, y-1], dungeonMap[x+1, y-1],
dungeonMap[x-1, y], dungeonMap[x, y], dungeonMap[x+1, y],
dungeonMap[x-1, y+1], dungeonMap[x, y+1], dungeonMap[x+1, y+1]
};
// 角の条件をチェック(簡単な例)
return !neighbors[4] && (neighbors[0] || neighbors[2] || neighbors[6] || neighbors[8]);
}
}
ウェーブ生成システム
段階的な敵出現システム
[System.Serializable]
public class Wave
{
public string waveName;
public EnemySpawnData[] enemies;
public float timeBetweenSpawns = 1f;
public float timeToNextWave = 10f;
}
[System.Serializable]
public class EnemySpawnData
{
public GameObject enemyPrefab;
public int count;
public float delay;
}
public class WaveManager : MonoBehaviour
{
[Header("ウェーブ設定")]
public Wave[] waves;
public Transform[] spawnPoints;
[Header("状態表示")]
public int currentWave = 0;
public bool waveInProgress = false;
private int enemiesAlive = 0;
void Start()
{
StartNextWave();
}
public void StartNextWave()
{
if (currentWave >= waves.Length)
{
Debug.Log("すべてのウェーブが完了しました!");
return;
}
Wave wave = waves[currentWave];
Debug.Log($"ウェーブ {currentWave + 1} 開始: {wave.waveName}");
waveInProgress = true;
StartCoroutine(SpawnWave(wave));
}
IEnumerator SpawnWave(Wave wave)
{
foreach (EnemySpawnData enemyData in wave.enemies)
{
yield return new WaitForSeconds(enemyData.delay);
for (int i = 0; i < enemyData.count; i++)
{
SpawnEnemy(enemyData.enemyPrefab);
yield return new WaitForSeconds(wave.timeBetweenSpawns);
}
}
// ウェーブの敵がすべて倒されるまで待つ
while (enemiesAlive > 0)
{
yield return new WaitForSeconds(0.5f);
}
waveInProgress = false;
currentWave++;
yield return new WaitForSeconds(waves[currentWave - 1].timeToNextWave);
StartNextWave();
}
void SpawnEnemy(GameObject enemyPrefab)
{
Transform spawnPoint = spawnPoints[Random.Range(0, spawnPoints.Length)];
GameObject enemy = Instantiate(enemyPrefab, spawnPoint.position, spawnPoint.rotation);
// 敵の死亡時コールバックを設定
EnemyController enemyController = enemy.GetComponent<EnemyController>();
if (enemyController != null)
{
enemyController.OnDeath += OnEnemyDeath;
}
enemiesAlive++;
}
void OnEnemyDeath()
{
enemiesAlive--;
}
}
まとめ
Unityオブジェクト作成のマスタープラン
学習段階別のロードマップ
初級レベル(基礎固め)
- 基本概念の理解
- GameObject、Component、Transformの概念
- Hierarchy、Inspector、Sceneビューの使い方
- 基本形状オブジェクトの作成と編集
- エディタ操作の習得
- メニューからのオブジェクト作成
- プロパティの調整
- 親子関係の設定
- プレハブの基本
- プレハブの作成と利用
- インスタンスとプレハブの関係理解
中級レベル(スクリプト活用)
- 基本的なスクリプト生成
- Instantiate()の使い方
- 位置と回転の指定
- コンポーネントアクセス
- 動的生成システム
- タイマーベースの生成
- イベント駆動の生成
- ランダム要素の追加
- 管理とパフォーマンス
- オブジェクトプーリング
- 生成数の制限
- メモリ管理
上級レベル(最適化と応用)
- 高度な生成システム
- 手続き的生成
- ウェーブシステム
- AI連携生成
- パフォーマンス最適化
- LOD(Level of Detail)システム
- 距離ベース最適化
- 非同期処理
- ツール開発
- エディタ拡張
- カスタムインスペクター
- 自動化ツール
実践で活かすベストプラクティス
設計原則
- 単一責任の原則:一つのスクリプトは一つの役割に集中
- 再利用性:プレハブとコンポーネントの効果的な活用
- 拡張性:将来の機能追加を考慮した設計
- 保守性:分かりやすい命名とコメント
パフォーマンス重視
- オブジェクトプーリング:頻繁な生成・削除を避ける
- バッチング:同じマテリアルのオブジェクトをまとめる
- LOD活用:距離に応じた詳細度調整
- プロファイリング:定期的な性能測定
デバッグとテスト
- 段階的テスト:小さな単位から確認
- ログ活用:適切なデバッグ出力
- 視覚的フィードバック:問題の早期発見
- エラーハンドリング:例外状況への対応
コメント