unity-csharp-scripting について
Unity 6 で MonoBehaviour スクリプトを作成・修正する開発者向けの C# リファレンススキルです。ライフサイクル、コンポーネント参照、Inspector のシリアライズ、コルーチン、プロジェクト検査を扱います。
主な機能
- 01MonoBehaviour のライフサイクル指針
- 02コンポーネント参照のキャッシュ
- 03Inspector 向けシリアライズ
- 04コルーチンによる時間制御
- 05.meta と GUID のプロジェクト検査
活用シーン
- 01ゲーム動作に応じたコールバック選択
- 02デザイナー向け調整フィールドの公開
- 03遅延処理や順次処理の実装
- 04ファイル変更後のアセット参照確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ gamedev-skills/ awesome-gamedev-agent-skills --skill unity-csharp-scriptingインストール後、Claude Code で/unity-csharp-scriptingを入力して読み込まれたか確認します。
同梱ファイルreferences/lifecycle-and-coroutines.mdscripts/check_unity_project.py
詳細
Unity C# スクリプティング(MonoBehaviour)
Unity 6 で正しく、慣用的なゲームプレイスクリプトを作成します。ライフサイクル、コンポーネントへのアクセス、シリアライズ、コルーチンを適切に扱い、動作を決定論的にして、インスペクターを便利に保ちます。対象は Unity 6.3 LTS (6000.3)、C# / .NET Standard 2.1 です。
使用する場面
MonoBehaviourの作成や修正時に使用します。適切なライフサイクルコールバックの選択、コンポーネントの取得やキャッシュ、インスペクターに表示するフィールドの設定、コルーチンを使った時間制御ロジックの実行などが対象です。- プロジェクトに
*.csファイル、Assembly-CSharpまたは*.asmdef、およびProjectSettings/フォルダーがある場合に使用します。
使用しない場面: リジッドボディの移動や衝突応答 → unity-physics、プレイヤー入力の読み取り → unity-input-system、共有データアセットや設定 → unity-scriptableobjects、Animator パラメーター → unity-animation。このスキルの対象はスクリプトのライフサイクルと C# の配線であり、これらのサブシステムではありません。
基本的なワークフロー
- 慣習ではなく目的に応じてコールバックを選択します。
Awake(参照をキャッシュし、ロード時に一度だけ実行)、OnEnable(イベントを購読)、Start(ほかのオブジェクトのAwakeに依存する初期化)、Update(フレームごとのロジックや入力のポーリング)、FixedUpdate(物理演算)、LateUpdate(移動後のカメラ追従)、OnDisable/OnDestroy(購読解除やクリーンアップ)。 - コンポーネントの取得結果は
Awakeでキャッシュします — 毎フレームGetComponentを呼び出してはいけません。 - 調整可能な値は
[SerializeField] privateで公開します。publicフィールドではなく、ほかのコードから変更できない一方で、デザイナーがインスペクターで編集できるようにします。 Updateではフレームごとの値にTime.deltaTimeを掛けます(FixedUpdateではTime.fixedDeltaTimeの意味が自動的に適用されます)。- 時間順に実行するロジックにはコルーチンを使います(遅延、トゥイーン、「X を実行してから待ち、その後 Y を実行」など)。
StartCoroutineで開始し、確実に停止します。 - Play モードで確認します。Console で null 参照例外がないか確認し、インスペクターの値が想定どおり更新されることを確認します。
Updateの負荷が高い場合は Profiler も確認します。 - 作業を終える前に
.metaファイルを確認します。 エディター外でスクリプトやアセットを作成、移動、名前変更、削除する場合は、必ずファイルと.metaを一緒に移動します(新しい.metaは新しい GUID を意味し、参照が壊れます)。次にpython scripts/check_unity_project.py <project-dir>を実行し(機械可読形式の出力には--jsonを指定)、すべてのERROR(GUID の重複や形式不正、.metaを伴わないファイル移動、スクリプト参照の欠落)を修正して、終了コードが 0 になるまで再実行します。新規ファイルに.metaがない場合は警告にとどまり、次回のインポート時に Unity が作成します。スクリプトのパスはこのスキルディレクトリを基準にするか、先にインストール済みのスキルパスを解決してください。このスクリプトには Python 3.10 以降のみが必要で、それ以外の依存関係はありません。テキスト形式でシリアライズされたアセットのみを読み取り、Unity でプロジェクトを開く作業の代わりにはなりません。
パターン
1. ライフサイクルとコンポーネントのキャッシュ(基本のひな形)
using UnityEngine;
[RequireComponent(typeof(Rigidbody))] // auto-adds the dependency, prevents null refs
public class PlayerController : MonoBehaviour
{
[SerializeField] private float moveSpeed = 6f; // editable in Inspector, private in code
private Rigidbody _rb; // cached, not fetched per frame
private void Awake() => _rb = GetComponent<Rigidbody>(); // cache once on load
private void Update()
{
// Per-frame, non-physics work. Scale by deltaTime so it is frame-rate independent.
transform.Rotate(0f, 90f * Time.deltaTime, 0f);
}
private void FixedUpdate()
{
// Physics work belongs here (fixed timestep). See the unity-physics skill.
_rb.MovePosition(_rb.position + transform.forward * moveSpeed * Time.fixedDeltaTime);
}
}
2. TryGetComponent による安全なコンポーネントアクセス
// Avoids allocating a null and is clearer than GetComponent + null check.
if (other.TryGetComponent<Health>(out var health))
health.Apply(-10);
3. インスペクターに正しく表示されるシリアライズ
[SerializeField, Range(0f, 1f)] private float volume = 0.8f; // slider
[SerializeField] private string playerName = "Hero"; // private but serialized
[System.Serializable] // REQUIRED for a plain class to serialize/show
public class Stats { public int hp = 100; public int mana = 50; }
[SerializeField] private Stats stats = new(); // nested struct-like data in the Inspector
4. 時間順に実行するロジックのためのコルーチン
private void Start() => StartCoroutine(FlashThenHide());
private System.Collections.IEnumerator FlashThenHide()
{
yield return new WaitForSeconds(0.5f); // wait half a second of game time
GetComponent<Renderer>().enabled = false;
yield return null; // resume next frame
}
注意点
Update内でのGetComponent— フレームごとに検索するため、パフォーマンスが大幅に低下します。参照はAwake/Startでキャッシュしてください。Update内での物理演算 —FixedUpdate以外で力やMovePositionを使ってRigidbodyを動かすと、動きが不安定になり、タイムステップに依存した挙動になります。入力はUpdateで読み取り、物理演算はFixedUpdateで適用してください。- オブジェクト間の
Startの実行順に依存すること —StartはすべてのAwakeの後に実行されますが、Start同士の順序は未定義です。オブジェクト間の接続はStartで、自身のセットアップはAwakeで行ってください。 - Inspectorに表示するためだけにフィールドを
publicにすること — そうすると、ほかのスクリプトからも変更できてしまいます。代わりに[SerializeField] privateを使用してください。 - **
gameObject.tag == "Enemy"**は文字列を割り当てるため処理が遅くなります。gameObject.CompareTag("Enemy")を使用してください。 - GameObjectを無効にするとコルーチンが停止します — 無効化されたオブジェクトのコルーチンは終了します。切り替え後も継続させる必要がある場合は、
OnEnableで再度StartCoroutineを呼び出してください。 UpdateがStartより前に実行されることはありませんが、最初のUpdateはStartと同じフレームで実行される場合があります — セットアップを不自然に分割している場合は、まだ初期化されていないフィールドへのアクセスを防いでください。
参考資料
- イベント実行順序の一覧表や高度なコルーチンのパターン(カスタムの
CustomYieldInstruction、ハンドルによる停止、WaitUntil/WaitWhile)については、references/lifecycle-and-coroutines.mdを参照してください。 scripts/check_unity_project.py— 読み取り専用の.meta/GUIDおよびスクリプト欠落チェッカーです。--helpですべてのチェック項目と終了コードを確認できます。- 主要ドキュメント:Unity Manualの「Event function execution order」(
https://docs.unity3d.com/Manual/execution-order.html)およびScriptReference/MonoBehaviour。
関連スキル
unity-physics—Rigidbody、衝突、FixedUpdateでの移動。unity-input-system— これらのスクリプトにプレイヤー入力を読み込む方法。unity-scriptableobjects— シングルトンを使わずにスクリプト間でデータや設定を共有する方法。
コメント
Markdown 対応