unity-foundations について
初心者や開発者向けにUnityの基礎をまとめたスキルです。GameObject、コンポーネント、Transform、シーン、Prefabに加え、ScriptableObject、API、実践パターン、注意点を解説します。
主な機能
- 01GameObjectとコンポーネントの基礎
- 02Transformと親子階層
- 03シーンとPrefabの管理
- 04ScriptableObjectのデータコンテナ
- 05タグ、レイヤー、LayerMask
- 06一般的なAPIとアンチパターン
活用シーン
- 01Unityのシーンオブジェクトの構成を理解
- 02Prefab設定を作成して再利用
- 03ScriptableObjectで共有データを管理
- 04オブジェクト検索とシーン読み込みの処理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ nice-wolf-studio/ unity-claude-skills --skill unity-foundationsインストール後、Claude Code で/unity-foundationsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/component-lifecycle.mdgameobject-api.mdprefabs-and-scenes.md詳細
Unity の基礎
基本概念
GameObject
GameObject は Unity における基本的な構成要素です。シーン内のすべてのオブジェクト(キャラクター、小道具、風景、カメラ、ライトなど)は GameObject です。GameObject はコンテナーであり、単独では機能せず、機能を持たせるには コンポーネント が必要です。すべての GameObject には、取り外すことのできない Transform コンポーネントが自動的に含まれます。
コンポーネント
コンポーネントは、すべての GameObject を構成する機能部品です。Unity は 継承より合成を優先する アーキテクチャを採用しています。深いクラス階層を継承するのではなく、複数のコンポーネントを GameObject に追加して動作を構築します。各 GameObject には Transform コンポーネントが必ず 1 つだけ必要です。追加のコンポーネント(Rigidbody、Collider、MeshRenderer、カスタム MonoBehaviour)によって、オブジェクトの動作が定義されます。
ドキュメントに記載されている制約:
- コンポーネントは、対象の GameObject と同じプロジェクト内に存在する必要があります
- コンポーネントの取得元として、別のプロジェクト、アタッチされていないスクリプト、または未インストールのパッケージを使用することはできません
Transform
Transform コンポーネントは 位置、回転、スケール を保持します。それぞれ、親を基準とした相対値(ローカル座標)か、ワールド原点を基準とした値(ワールド座標)です。ドキュメントに記載されている主なポイント:
- 子の Transform には、親を基準とした値が表示されます
- ルート GameObject(親がないもの)には、ワールド座標が表示されます
- 物理エンジンは 1 単位を 1 メートルとして扱います
- 子を追加する前に親の位置を
(0,0,0)に設定すると、ローカル座標がグローバル座標と一致します - 実行時に Transform の Scale を調整するのは避け、モデルアセットを実寸で作成してください。不均一なスケーリングは Collider、Light、Audio Source に問題を引き起こします
シーン
シーンは、ゲームやアプリケーションのすべて、または一部を含むアセットです。新しいシーンには、デフォルトで Camera とディレクショナル Light が含まれます。プロジェクトでは、単一のシーンまたは複数のシーン(たとえば、レベルごとに 1 つ)を使用できます。シーンテンプレートは、新しいシーンを作成するためのひな型として機能します。
Prefab
Prefab は、GameObject の完全な構成(すべてのコンポーネント、プロパティ値、子 GameObject)を保存する再利用可能なアセットテンプレートです。主な機能:
- ネストされた Prefab:Prefab インスタンスを他の Prefab 内に含めます
- Prefab バリアント:ベースとなる Prefab との関係を維持しながら、あらかじめ定義されたバリエーションを作成します
- オーバーライド:テンプレートに影響を与えず、特定のインスタンス上のコンポーネントやデータを変更します
- Prefab の解除:Prefab インスタンスをスタンドアロンの GameObject に戻します
ScriptableObject
ScriptableObject は、UnityEngine.Object を継承するシリアル化可能な Unity の型であり、GameObject から独立したデータコンテナーとして機能します。MonoBehaviour とは異なり、ScriptableObject はプロジェクトレベルの .asset ファイルとして存在します。主な用途:
- 共有データコンテナー(Prefab 間でデータを複製する代わりに 1 つのアセットを参照することで、メモリ使用量を削減します)
- エディターツールの基盤(EditorTool、EditorWindow は ScriptableObject を継承します)
- 実行時設定の保存
重要:Unity は、編集モードでスクリプトから変更された ScriptableObject を自動保存しません。変更後に EditorUtility.SetDirty() を呼び出す必要があります。
タグ
タグは、スクリプトで使用するために GameObject に割り当てられる参照用識別子です。各 GameObject に割り当てられるタグは 1 つだけですが、複数の GameObject に同じタグを割り当てることができます。組み込みタグ:Untagged、Respawn、Finish、EditorOnly、MainCamera、Player、GameController。
MainCamera:エディターはこれらをキャッシュします。Camera.mainは最初に見つかった有効な結果を返しますEditorOnly:このタグが付いた GameObject は、ビルド時に破棄されます- タグ名は、一度作成すると変更できません
レイヤー
レイヤーは、カメラのレンダリング、ライティング、物理的な衝突、カスタムコードのロジックなどで、GameObject を選択的に処理するために使用します。Unity は最大 32 個のレイヤーをサポートします。LayerMask は、API 呼び出しの対象となるレイヤーを定義します。
よくあるパターン
コンポーネントの作成とアクセス
using UnityEngine;
public class ComponentAccess : MonoBehaviour
{
void Start()
{
// Get a component on this GameObject
Rigidbody rb = GetComponent<Rigidbody>();
// Get a component on a child GameObject
Collider childCollider = GetComponentInChildren<Collider>();
// Get all components of a type on this and children
MeshRenderer[] renderers = GetComponentsInChildren<MeshRenderer>();
// Add a component at runtime
BoxCollider box = gameObject.AddComponent<BoxCollider>();
// Remove a component (destroys it)
Destroy(box);
}
}
GameObject の検索
using UnityEngine;
public class FindingObjects : MonoBehaviour
{
void Start()
{
// Find by tag (returns first match)
GameObject player = GameObject.FindWithTag("Player");
// Find all with tag
GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");
// Find by name (slow -- avoid in Update)
GameObject manager = GameObject.Find("GameManager");
// Compare tags efficiently (no GC allocation)
if (gameObject.CompareTag("Player"))
{
Debug.Log("This is the player");
}
}
}
Prefab のインスタンス化
Unity ドキュメントの例:
using UnityEngine;
public class InstantiationExample : MonoBehaviour
{
// Reference to the prefab. Drag a prefab into this field in the Inspector.
public GameObject myPrefab;
void Start()
{
// Instantiate at position (0, 0, 0) and zero rotation.
Instantiate(myPrefab, new Vector3(0, 0, 0), Quaternion.identity);
}
}
親の Transform を指定してインスタンス化する
using UnityEngine;
public class SpawnWithParent : MonoBehaviour
{
public GameObject prefab;
public Transform parentTransform;
void SpawnChild()
{
// Instantiate as child of a parent transform
GameObject instance = Instantiate(prefab, parentTransform);
// Instantiate at specific world position under a parent
GameObject positioned = Instantiate(
prefab,
new Vector3(5, 0, 0),
Quaternion.identity,
parentTransform
);
}
}
ScriptableObject のデータコンテナー
Unity のドキュメントより:
using UnityEngine;
[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/SpawnData", order = 1)]
public class SpawnDataScriptableObject : ScriptableObject
{
public GameObject prefab;
public int count;
public Vector3[] positions;
}
using UnityEngine;
public class SpawnManager : MonoBehaviour
{
public SpawnDataScriptableObject spawnData;
void Start()
{
for (int i = 0; i < spawnData.count; i++)
{
Instantiate(spawnData.prefab, spawnData.positions[i], Quaternion.identity);
}
}
}
シーン管理
using UnityEngine;
using UnityEngine.SceneManagement;
public class SceneLoader : MonoBehaviour
{
void Start()
{
// Subscribe to scene loaded event
SceneManager.sceneLoaded += OnSceneLoaded;
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
Debug.Log("Loaded: " + scene.name);
}
public void LoadLevel(string sceneName)
{
// Load scene by name (replaces current)
SceneManager.LoadScene(sceneName);
}
public void LoadAdditive(string sceneName)
{
// Load scene additively (keeps current scenes)
SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
}
public void UnloadLevel(string sceneName)
{
SceneManager.UnloadSceneAsync(sceneName);
}
public void GetSceneInfo()
{
Scene active = SceneManager.GetActiveScene();
Debug.Log("Active scene: " + active.name);
Debug.Log("Loaded scene count: " + SceneManager.loadedSceneCount);
}
}
タグベースの生成
Unity ドキュメントの例より:
using UnityEngine;
public class RespawnSystem : MonoBehaviour
{
public GameObject respawnPrefab;
private GameObject respawn;
void Update()
{
if (respawn == null)
respawn = GameObject.FindWithTag("Respawn");
if (respawn != null)
{
Instantiate(respawnPrefab, respawn.transform.position,
respawn.transform.rotation);
}
}
}
GameObject の有効化と無効化
using UnityEngine;
public class ToggleVisibility : MonoBehaviour
{
public GameObject target;
public void Toggle()
{
// SetActive controls whether the GameObject is active
target.SetActive(!target.activeSelf);
// activeSelf: this object's own active state
// activeInHierarchy: effective state (considers parent chain)
Debug.Log("Self: " + target.activeSelf);
Debug.Log("InHierarchy: " + target.activeInHierarchy);
}
}
レイキャスト用のレイヤーマスク
using UnityEngine;
public class LayerRaycast : MonoBehaviour
{
void Update()
{
// Create a layer mask for layer named "Ground"
int groundLayer = LayerMask.NameToLayer("Ground");
int layerMask = 1 << groundLayer;
// Raycast only against the Ground layer
if (Physics.Raycast(transform.position, Vector3.down, out RaycastHit hit, 100f, layerMask))
{
Debug.Log("Hit ground at: " + hit.point);
}
// Use GetMask for multiple layers
int combinedMask = LayerMask.GetMask("Ground", "Water");
Physics.Raycast(transform.position, Vector3.forward, 50f, combinedMask);
}
}
アンチパターン
1. Update で GameObject.Find を使用する
// BAD: Find is expensive -- runs every frame with string lookup
void Update()
{
GameObject player = GameObject.Find("Player"); // Avoid!
}
// GOOD: Cache the reference
private GameObject player;
void Start()
{
player = GameObject.FindWithTag("Player");
}
2. Transform の不均一なスケール
ドキュメントより:「Transform コンポーネントで GameObject の Scale を調整しないでください。」不均一なスケーリング(例:2、4、2)によって、次の問題が発生します。
- Collider、Light、Audio Source が正しく動作しない
- 回転した子オブジェクトが歪んで見える
- スケールされたオブジェクトをインスタンス化する際にパフォーマンスが低下する
代わりに、実物大のスケールでモデルアセットを作成してください。
3. ScriptableObject を使わず、Prefab 間でデータを重複させる
// BAD: Every prefab instance duplicates this data
public class EnemyStats : MonoBehaviour
{
public int health = 100;
public float speed = 5f;
public string enemyName = "Goblin";
}
// GOOD: Use a ScriptableObject -- one asset, many references
[CreateAssetMenu(menuName = "ScriptableObjects/EnemyConfig")]
public class EnemyConfig : ScriptableObject
{
public int health = 100;
public float speed = 5f;
public string enemyName = "Goblin";
}
public class Enemy : MonoBehaviour
{
public EnemyConfig config; // All instances share the same asset
}
4. ScriptableObject の変更時に EditorUtility.SetDirty を忘れる
// BAD: Changes in Edit mode won't persist
settings.value += 10;
// GOOD: Mark asset dirty so Unity saves it
settings.value += 10;
EditorUtility.SetDirty(settings);
5. 文字列によるタグ比較
// BAD: Allocates a new string for comparison (GC pressure)
if (gameObject.tag == "Player") { }
// GOOD: CompareTag avoids allocation
if (gameObject.CompareTag("Player")) { }
6. 親子間の Transform の関係を無視する
子オブジェクトを追加する前に、親の位置を (0,0,0) に設定してください。そうしないと、子のローカル座標とグローバル座標が一致せず、オブジェクトの配置時に混乱が生じます。
主要 API クイックリファレンス
| API | 説明 | 備考 |
|---|---|---|
GetComponent<T>() | 同じ GameObject 上のコンポーネントを取得 | 見つからない場合は null を返します |
GetComponentInChildren<T>() | 自身または子オブジェクト上のコンポーネントを取得 | 深さ優先で検索します |
GetComponentsInChildren<T>() | 階層内で一致するすべてのコンポーネントを取得 | 配列を返します |
AddComponent<T>() | 実行時に新しいコンポーネントを追加 | 新しいコンポーネントを返します |
Destroy(obj) | GameObject または Component を破棄 | フレームの終了時に実行されます |
Instantiate(prefab, pos, rot) | 指定した位置と回転で Prefab のクローンを作成 | クローンを返します |
GameObject.FindWithTag(tag) | タグが一致する最初の有効な GO を検索 | 見つからない場合は null を返します |
GameObject.FindGameObjectsWithTag(tag) | タグが一致するすべての有効な GO を検索 | 配列を返します |
GameObject.Find(name) | 名前で検索(処理負荷が高い) | Update ループ内での使用は避けてください |
gameObject.SetActive(bool) | 有効化/無効化 | すべてのコンポーネントを無効にします |
gameObject.CompareTag(tag) | GC 割り当てを発生させずにタグを比較 | == tag よりも推奨されます |
SceneManager.LoadScene(name) | シーンを読み込む(現在のシーンと置き換え) | Build Settings に含める必要があります |
SceneManager.LoadSceneAsync(name, mode) | シーンを非同期で読み込む | Additive または Single モード |
SceneManager.UnloadSceneAsync(name) | 読み込み済みのシーンをアンロード | AsyncOperation を返します |
SceneManager.GetActiveScene() | 現在のアクティブシーンを取得 | Scene 構造体を返します |
LayerMask.NameToLayer(name) | 名前からレイヤーインデックスを取得 | int を返します |
LayerMask.GetMask(names) | レイヤー名から複合マスクを取得 | string 配列を受け取ります |
Camera.main | MainCamera タグが付いたカメラを取得 | Unity によってキャッシュされます |
関連スキル
- unity-scripting -- C# スクリプトのパターン、MonoBehaviour のライフサイクル、コルーチン、イベント
- unity-physics -- Rigidbody、Collider、物理レイヤー、レイキャスト、トリガー
- unity-editor-tools -- カスタムインスペクター、エディターウィンドウ、ギズモ、ビルドパイプライン