mcpField
U
unity-foundations

unity-foundations

github.com/nice-wolf-studio開発4072 回インストール6 週間前に更新
リポジトリ

unity-foundations について

初心者や開発者向けにUnityの基礎をまとめたスキルです。GameObject、コンポーネント、Transform、シーン、Prefabに加え、ScriptableObject、API、実践パターン、注意点を解説します。

主な機能

  1. 01GameObjectとコンポーネントの基礎
  2. 02Transformと親子階層
  3. 03シーンとPrefabの管理
  4. 04ScriptableObjectのデータコンテナ
  5. 05タグ、レイヤー、LayerMask
  6. 06一般的なAPIとアンチパターン

活用シーン

  1. 01Unityのシーンオブジェクトの構成を理解
  2. 02Prefab設定を作成して再利用
  3. 03ScriptableObjectで共有データを管理
  4. 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.md
gameobject-api.md
prefabs-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.mainMainCamera タグが付いたカメラを取得Unity によってキャッシュされます

関連スキル

  • unity-scripting -- C# スクリプトのパターン、MonoBehaviour のライフサイクル、コルーチン、イベント
  • unity-physics -- Rigidbody、Collider、物理レイヤー、レイキャスト、トリガー
  • unity-editor-tools -- カスタムインスペクター、エディターウィンドウ、ギズモ、ビルドパイプライン

Additional Resources

コメント