mcpField
U
unity-scripting

unity-scripting

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

unity-scripting について

Unity 開発者向けの C# スクリプティングリファレンスです。ゲーム実行時とエディター用スクリプトを対象に、ライフサイクル、非同期処理、シリアライズ、API と注意点をまとめています。

主な機能

  1. 01MonoBehaviour のライフサイクルとコールバック
  2. 02コルーチンと Unity 6 Awaitable
  3. 03イベントと ScriptableObject のパターン
  4. 04シリアライズ規則と属性
  5. 05主要 API とよくあるアンチパターン

活用シーン

  1. 01ゲームロジックに使うコールバックの選択
  2. 02フレームをまたぐ非同期処理の実装
  3. 03設定やイベントデータの共有
  4. 04性能やライフサイクル上の問題の回避

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/nice-wolf-studio/unity-claude-skills --skill unity-scripting

インストール後、Claude Code で/unity-scriptingを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
coroutines-and-async.md
monobehaviour-lifecycle.md
object-pool.md
scriptableobjects.md

詳細

Unity C# スクリプティング

スクリプトの基本

C# スクリプト(.cs ファイル)は Assets フォルダーに保存されます。スクリプトは、組み込み型を継承することで Unity の機能を利用できます。

  • UnityEngine.Object -- カスタム型を Inspector フィールドに割り当てられるようにします
  • MonoBehaviour -- GameObject にコンポーネントとしてアタッチし、シーン内の動作を制御します
  • ScriptableObject -- GameObject にアタッチされない、独立したデータアセットです

スクリプトは次の 2 つのコンテキストで動作します。

  • ランタイムスクリプト -- Player ビルドで実行されます(UnityEngine 名前空間を使用)
  • エディタースクリプト -- エディター内でのみ実行されます(UnityEditor 名前空間を使用し、Editor フォルダーに配置します)

MonoBehaviour のライフサイクル

MonoBehaviour は常に GameObject のコンポーネントとして存在します。ライフサイクルイベント関数は厳密な順序で実行されます。Script Execution Order の設定で指定しない限り、異なる GameObject 間で同じイベント関数が呼び出される順序を前提にすることはできません。

実行順序(ASCII 図)
INITIALIZATION
  |
  v
[Awake] ---------> Called when script instance loads (once per lifetime)
  |
  v
[OnEnable] ------> Called when object/component becomes enabled
  |
  v
(SceneManager.sceneLoaded fires here -- after OnEnable, before Start)
  |
  v
[Start] ---------> Called before first frame Update (once per lifetime)
  |
  |
  |  +===========================================+
  |  |          PHYSICS LOOP (fixed timestep)    |
  |  |                                           |
  +->| [FixedUpdate] --> Internal Physics ------>|
  |  |       |                                   |
  |  | [yield WaitForFixedUpdate resumes]        |
  |  +===========================================+
  |
  v
[Update] --------> Called once per frame
  |
  v
[yield null / yield WaitForSeconds resumes]
  |
  v
(Internal Animation Update)
  |   [OnAnimatorMove]
  |   [OnAnimatorIK]
  |
  v
[LateUpdate] ----> Called after all Update functions complete
  |
  v
RENDERING
  | [OnWillRenderObject]
  | [OnPreCull] [OnBecameVisible/Invisible]
  | [OnPreRender]
  | [OnRenderObject]
  | [OnPostRender]
  | [OnRenderImage]
  |
  v
[OnGUI] ---------> Called for GUI rendering events
  |
  v
[yield WaitForEndOfFrame resumes]
  |
  v
DEACTIVATION / TEARDOWN
  |
  v
[OnDisable] -----> Called when component/object is disabled
  |
  v
[OnDestroy] -----> Called before object destruction
主なライフサイクルコールバック
コールバックタイミング用途
Awake()スクリプトインスタンスの読み込み時一度だけ行う初期化、参照のキャッシュ
OnEnable()コンポーネントの有効化時イベントの購読
Start()最初の Update の前他の Awake() 呼び出しに依存する初期化
FixedUpdate()固定タイムステップ(デフォルト 0.02 秒)物理計算、Rigidbody の力
Update()毎フレーム入力、物理演算以外のゲームロジック
LateUpdate()すべての Update 呼び出しの後カメラの追従、Update 後の調整
OnDisable()コンポーネントの無効化時イベントの購読解除
OnDestroy()破棄の前最終的なクリーンアップ
物理コールバック
// 3D Physics
void OnCollisionEnter(Collision collision) { }
void OnCollisionStay(Collision collision) { }
void OnCollisionExit(Collision collision) { }
void OnTriggerEnter(Collider other) { }
void OnTriggerStay(Collider other) { }
void OnTriggerExit(Collider other) { }

// 2D Physics
void OnCollisionEnter2D(Collision2D collision) { }
void OnTriggerEnter2D(Collider2D other) { }
MonoBehaviour のプロパティ(Unity 6)
プロパティ用途
destroyCancellationTokenMonoBehaviour の破棄時に通知されるトークン(非同期処理のキャンセル用)
didAwakeAwake が呼び出されたかどうか
didStartStart が呼び出されたかどうか
runInEditModeエディター内でのスクリプト実行を許可します

ライフサイクルの詳細については、references/monobehaviour-lifecycle.md を参照してください。

コルーチンと Async/Await

Unity では、複数フレームにわたる処理を行う方法として、2 つのパターンがサポートされています。

コルーチン(IEnumerator)

yield return で中断し、yield 命令に基づいて再開するメソッドです。

IEnumerator Fade()
{
    Color c = renderer.material.color;
    for (float alpha = 1f; alpha >= 0; alpha -= 0.1f)
    {
        c.a = alpha;
        renderer.material.color = c;
        yield return new WaitForSeconds(0.1f);
    }
}

void Update()
{
    if (Input.GetKeyDown("f"))
    {
        StartCoroutine(Fade());
    }
}

Yield 命令:

  • yield return null -- 次のフレーム(Update の後)に再開します
  • yield return new WaitForSeconds(t) -- t 秒後に再開します
  • yield return new WaitForFixedUpdate() -- FixedUpdate の後に再開します
  • yield return new WaitForEndOfFrame() -- レンダリング後に再開します
  • yield return new WaitUntil(() => condition) -- 条件が true になったときに再開します
  • yield return StartCoroutine(other) -- 入れ子のコルーチンが完了するまで待機します

重要: コルーチンはメインスレッドで実行されます。enabled = false にして MonoBehaviour を無効にしても、コルーチンは停止しません。GameObject を非アクティブにするか、MonoBehaviour を破棄すると、コルーチンは停止します。

Awaitable(Unity 6 の Async/Await)

Awaitable は Unity 独自の非同期型で、通常はイテレーターを使うコルーチンよりも効率的です。割り当てを抑えるため、プールされています。

async Awaitable SampleSchedulingJobsForNextFrame()
{
    await Awaitable.EndOfFrameAsync();
    var jobHandle = ScheduleSomethingWithJobSystem();
    await Awaitable.NextFrameAsync();
    jobHandle.Complete();
}

Awaitable のメソッド:

  • Awaitable.NextFrameAsync() -- 次のフレームで再開します
  • Awaitable.FixedUpdateAsync() -- 次の FixedUpdate で再開します
  • Awaitable.EndOfFrameAsync() -- フレームの最後に再開します
  • Awaitable.WaitForSecondsAsync(float) -- 指定した時間の経過後に再開します
  • Awaitable.MainThreadAsync() -- メインスレッドで継続するように強制します
  • Awaitable.BackgroundThreadAsync() -- バックグラウンドスレッドで継続するように強制します

重要な制約: Awaitable インスタンスはプールされるため、同じインスタンスに対して await を複数回実行しないでください。複数回の await は未定義の動作を引き起こします。

機能コルーチンAwaitable
戻り値なしあり(Awaitable<T>)
スレッド切り替えなしあり
メモリ割り当てyield ごとにオーバーヘッドありプール済みで最小限
キャンセルStopCoroutine を手動で実行CancellationToken をサポート
エラー処理try/catch なしtry/catch/finally を完全サポート

非同期処理の完全なリファレンス:references/coroutines-and-async.md

イベントと通信パターン

C# イベントとデリゲート
public class Health : MonoBehaviour
{
    public event System.Action<float> OnDamageTaken;
    public event System.Action OnDeath;

    private float _hp = 100f;

    public void TakeDamage(float amount)
    {
        _hp -= amount;
        OnDamageTaken?.Invoke(amount);
        if (_hp <= 0f)
            OnDeath?.Invoke();
    }
}

public class UIHealthBar : MonoBehaviour
{
    [SerializeField] private Health _health;

    void OnEnable()
    {
        _health.OnDamageTaken += HandleDamage;
    }

    void OnDisable()
    {
        _health.OnDamageTaken -= HandleDamage;
    }

    private void HandleDamage(float amount)
    {
        // Update UI
    }
}
UnityEvents(Inspector で割り当て可能)
using UnityEngine.Events;

public class GameManager : MonoBehaviour
{
    public UnityEvent OnGameStart;
    public UnityEvent<int> OnScoreChanged;

    public void StartGame()
    {
        OnGameStart?.Invoke();
    }
}
ScriptableObject のイベントチャネル
[CreateAssetMenu(menuName = "Events/Void Event Channel")]
public class VoidEventChannel : ScriptableObject
{
    private System.Action _onEventRaised;

    public void RaiseEvent()
    {
        _onEventRaised?.Invoke();
    }

    public void Subscribe(System.Action listener) => _onEventRaised += listener;
    public void Unsubscribe(System.Action listener) => _onEventRaised -= listener;
}

ScriptableObjects

ScriptableObject は、UnityEngine.Object を継承したシリアル化可能な Unity の型です。GameObject にアタッチされず、独立したプロジェクトアセットとして存在します。共有データ、設定、イベントチャネルに使用します。

[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/SpawnManagerScriptableObject", order = 1)]
public class SpawnManagerScriptableObject : ScriptableObject
{
    public string prefabName;
    public int numberOfPrefabsToCreate;
    public Vector3[] spawnPoints;
}

主な動作:

  • Edit モードでは Inspector での変更が自動的に保存されます。スクリプトによる変更には EditorUtility.SetDirty() が必要です
  • 実行時、プレイヤーは ScriptableObject のデータを読み取れますが、変更をディスクに永続化することはできません
  • メモリ効率に優れています。データを複製する代わりに、複数のオブジェクトが同じアセットインスタンスを参照します

ScriptableObject の完全なリファレンス:references/scriptableobjects.md

シリアル化クイックリファレンス

Unity は、以下のすべての条件を満たすフィールドをシリアル化します。

  1. public である、または [SerializeField] 属性が付いている
  2. static、const、readonly ではない
  3. シリアル化可能な型である

シリアル化可能な型: プリミティブ型、列挙型(32 ビット以下)、Unity 組み込み型(Vector2、Vector3、Rect、Color、AnimationCurve など)、[Serializable] を付けたカスタムクラス/構造体、UnityEngine.Object の参照、上記の型を要素とする List<T> および配列。

主な属性:

[SerializeField] private float _speed = 5f;        // Serialize private field
[field: SerializeField] public float Speed { get; private set; } // Auto-property
[NonSerialized] public float tempValue;             // Exclude from serialization
[HideInInspector] public float hiddenValue;         // Serialize but hide from Inspector
[SerializeReference] private IMyInterface _impl;    // Polymorphic serialization

サポート対象外: 多次元配列、ジャグ配列、辞書、ネストされたコンテナー。サポート対象外の型をカスタムシリアル化するには、ISerializationCallbackReceiver を使用します。

コア API クイックリファレンス

Vector3
// Static direction shortcuts
Vector3.zero;      // (0, 0, 0)
Vector3.one;       // (1, 1, 1)
Vector3.up;        // (0, 1, 0)
Vector3.forward;   // (0, 0, 1)
Vector3.right;     // (1, 0, 0)

// Common operations
float dist = Vector3.Distance(a, b);
float dot = Vector3.Dot(a.normalized, b.normalized);
Vector3 cross = Vector3.Cross(a, b);
Vector3 smoothed = Vector3.Lerp(from, to, t);
Vector3 moved = Vector3.MoveTowards(current, target, maxDelta);
Vector3 projected = Vector3.ProjectOnPlane(velocity, groundNormal);

プロパティ: magnitude、sqrMagnitude(比較に使用すると平方根の計算を避けられます)、normalized。

Quaternion
// Creation
Quaternion.identity;                             // No rotation
Quaternion.Euler(0f, 90f, 0f);                  // From Euler angles (degrees)
Quaternion.LookRotation(direction, Vector3.up);  // Face a direction
Quaternion.AngleAxis(45f, Vector3.up);           // Rotate around axis
Quaternion.FromToRotation(Vector3.up, normal);   // Rotation between directions

// Interpolation
Quaternion.Slerp(from, to, t);                  // Spherical interpolation
Quaternion.Lerp(from, to, t);                   // Linear interpolation

// Operations
float angle = Quaternion.Angle(a, b);           // Angle in degrees (0-180)
Vector3 rotatedPoint = rotation * point;         // Rotate a vector
Quaternion combined = rotA * rotB;               // Combine rotations

x、y、z、w を直接変更しないでください。 Euler()、AngleAxis()、または LookRotation() を使用してください。

Time
Time.deltaTime       // Seconds since last frame (use in Update)
Time.fixedDeltaTime  // Fixed timestep interval (use in FixedUpdate)
Time.time            // Time since game start
Time.timeScale       // 0 = paused, 1 = normal, 2 = double speed
Time.unscaledDeltaTime // Ignores timeScale (for UI animations during pause)
Debug
Debug.Log("Message");
Debug.LogWarning("Warning");
Debug.LogError("Error");
Debug.DrawRay(origin, direction, Color.red, duration);
Debug.DrawLine(start, end, Color.green, duration);

一般的なパターン

コンポーネント参照のキャッシュ
public class PlayerMovement : MonoBehaviour
{
    private Rigidbody _rb;
    private Transform _transform;

    void Awake()
    {
        _rb = GetComponent<Rigidbody>();
        _transform = transform; // Cache the transform property
    }

    void FixedUpdate()
    {
        // Use cached references -- never call GetComponent in Update/FixedUpdate
        _rb.AddForce(Vector3.up * 10f);
    }
}
シングルトンパターン
public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }

    void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        DontDestroyOnLoad(gameObject);
    }
}
キャンセル処理付きの非同期処理(Unity 6)
public class AsyncExample : MonoBehaviour
{
    async void Start()
    {
        try
        {
            await LoadAndProcessAsync(destroyCancellationToken);
        }
        catch (OperationCanceledException) { }
    }

    async Awaitable LoadAndProcessAsync(CancellationToken token)
    {
        await Awaitable.BackgroundThreadAsync();
        // Heavy computation here (off main thread)
        var result = ComputeExpensiveData();

        await Awaitable.MainThreadAsync();
        // Back on main thread -- safe to use Unity API
        ApplyResult(result);
    }
}
条件付き待機(WaitUntil の Awaitable による置き換え)
public static async Awaitable AwaitableUntil(Func<bool> condition, CancellationToken token)
{
    while (!condition())
    {
        token.ThrowIfCancellationRequested();
        await Awaitable.NextFrameAsync();
    }
}

アンチパターン

アンチパターン問題修正方法
Update() 内での GetComponent<T>()毎フレーム、割り当てが発生し、検索が行われますAwake() でキャッシュします
Update() 内での GameObject.Find()毎フレーム、高コストな文字列検索が行われます参照をキャッシュするか、シリアライズされたフィールドを使用します
処理頻度の高い箇所で new Vector3() を繰り返す毎フレーム、不要なコンストラクターのオーバーヘッドが発生しますVector3.zero、Vector3.one、または再利用可能な値をキャッシュして使用します
Quaternion.x/y/z/w を直接変更する無効な回転が生成されますEuler()、AngleAxis()、LookRotation() を使用します
Update() 内で物理演算のロジックを実行するフレームレートが変動すると一貫性が失われますRigidbody の力には FixedUpdate() を使用します
FixedUpdate() 内で Time.deltaTime を使用する動作します(Unity が暗黙的に fixedDeltaTime を返します)が、読み手には分かりにくいです明確にするため、Time.fixedDeltaTime を明示的に使用します
OnDisable でイベントの登録解除を忘れるメモリーリークや null 参照エラーが発生します必ず OnDisable() で登録を解除します
同じ Awaitable を 2 回 await する動作は未定義です(インスタンスがプールされるため)1 回だけ await するか、.AsTask() でラップします
空の Update()/FixedUpdate() メソッドUnity は引き続き呼び出すため、オーバーヘッドが発生します空のイベント関数を削除します
文字列ベースの Invoke("MethodName", t)コンパイル時の安全性がなく、名前の変更で動作しなくなります代わりにコルーチンまたは Awaitable を使用します

関連スキル

  • unity-foundations -- GameObject、コンポーネント、Transform、シーン、Prefab
  • unity-physics -- Rigidbody、Collider、レイキャスト、物理マテリアル
  • unity-input -- Input System、InputActions、PlayerInput コンポーネント

追加リソース

コメント