はじめに
何となくUnityでの開発を始めて、コードも、Three.jsとよく似てるので似た感覚で書いてましたが、それでもJavaScriptとUnity c#は別物なので、ざっと基礎を学ぶために連載記事を企画。
例によって過去記事通り、講師はAIです。
マニュアルや、Qiitaなどのネット情報なども出来るだけ読んでます。
プログラム初学者向けじゃないんで、いきなり突っ込んでいく内容かもです。
変数についても独特なので、やりたいところですが…。
プロローグ
「Unityは普通のC#じゃない」という前提は、 初心者だけじゃなく Web/JS/Java/C#経験者すべてが最初に迷子になるポイントだから。
目的は“教える”じゃなくて、Unityの世界観を正しくセットすること。
Unityは“普通のC#プログラム”ではない
Unityでコードを書き始めると、 誰もが最初にこう思う。
- main がない
- new しない
- 自分で呼ばない
- でも動いてる
- 意味が分からない
これはあなたが初心者だからではなく、 Unity が普通のプログラミングと前提を共有していないから。
Unityのコードは 「プログラム」ではなく「部品」として扱われる。
1. Unityには Main() が存在しない
C# や Java、Python、C++ など、 一般的なプログラムは「どこから実行が始まるか」が明確に決まっている。
たとえば C# なら、こうだ。
static void Main()
{
実行開始
}
この Main() が プログラムの入口。 ここから処理が始まり、上から順にコードが実行されていく。
これは多くの言語・多くの環境で共通している前提だ。
Unityには、この Main() が存在しない
Unityでプロジェクトを作っても、 Main() はどこにも見当たらない。
それでもゲームは動くし、 Update() は毎フレーム呼ばれる。
これは異常に見えるが、Unityではそれが正常。
なぜ Main() がないのか
理由は一つしかない。
Unityは、エンジン側がすでに実行ループを持っているから。
Unityエディタを起動した瞬間から、内部ではすでに
- 描画ループ
- 入力処理
- 物理更新
- シーン管理
といった巨大なループが回り続けている。
開発者が書く C# コードは、 そのループの「中身を置き換えるための部品」に過ぎない。
Unityの実行モデルはこうなっている
イメージとしては、こう。
Unityエンジンがループを回す
↓
フレーム更新のタイミングになる
↓
条件を満たした GameObject を探す
↓
該当する MonoBehaviour の Update() を呼ぶ
↓
次のフレームへ
つまり、
- プログラマーが処理を開始するのではない
- Unityが処理を開始し、必要なときにコードを呼ぶ
この順番が、普通のC#プログラムと完全に逆。
「プログラムを書く」というより「割り込ませる」
Unityでのコード記述は、
プログラムを書く ではなく Unityの処理に割り込ませる処理を書く
に近い。
Update() は「自分で呼ぶ関数」ではなく、 Unityが「今はこの処理を呼ぶべきだ」と判断して呼ぶ関数。
だから、
- Main がない
- 実行順を自分で制御しない
- 呼び出し元が見えない
という構造になる。
ここが分からないと、全部が魔法に見える
この前提を知らないと、
- なぜ Update が勝手に動くのか
- なぜ Start が一度だけ呼ばれるのか
- なぜ new していないのにオブジェクトが存在するのか
すべてが「おまじない」に見えてしまう。
逆に言えば、 Unityには Main がない という一点を理解するだけで、 UnityのC#はかなり読みやすくなる。
2. new しないのにインスタンスが生まれる理由
普通の C# なら、クラスを使うには必ずこう書く:
var p = new Player();
インスタンスは自分で new を呼び出すことで作られる。
これはオブジェクト指向の大前提。
でも Unity のスクリプトではこうなる:
public class Player : MonoBehaviour
{
void Update() { }
}
new Player() なんて1行も書いていない。
なのに Update が動く。オブジェクトも存在している。
これは初心者が Unity を見て最初に混乱するポイント。
Unity の実体は「GameObject に貼った瞬間、Unity が new してくれる」
Unityでは、スクリプトを
- Projectビューで作る
- GameObject にアタッチする
この時点で、Unity が裏側で new を行う。
開発者が new するのではなく、
GameObject にコンポーネントを貼る → Unity がそのクラスをインスタンス化 → 管理リストに登録 → ライフサイクルを開始
という仕組み。
だから、あなたは「new」ではなく “アタッチ” でインスタンスを作ることになる。
Unity では「new」しないのが正解
Unityのコンポーネントは、 普通の C# のように自分で new してはいけない。
var p = new Player(); // ❌ Unityではこれをやらない
これは MonoBehaviour として正しく動かない。
Unity が生成したインスタンスだけが、
- Update()
- Start()
- Awake()
- OnTriggerEnter()
などのイベントを受け取れる。
つまりUnityでは、
自分で生成するクラス(通常C#) と Unityが生成するクラス(MonoBehaviour) の2種類がある。
この違いが最初は理解しにくい理由。
結論:Unityの “オブジェクト生成モデル” が特殊すぎる
Unity は「コード主導」ではなく「エンジン主導」。
- new しない
- GameObject に貼るだけ
- Unity が必要なタイミングで生成する
この世界観を理解していないと、 MonoBehaviour がただの“おまじない”に見えてしまう。
3. 自分で呼ばないのに動く
Unityでスクリプトを書いていると、 必ず一度は不思議に思う。
Update()
Start()
Awake()
OnTriggerEnter()
これらのメソッドは、 自分で一度も呼んでいない。
それなのに、
- 毎フレーム動く
- 開始時に勝手に実行される
- 衝突した瞬間に呼ばれる
普通のC#ではありえない挙動に見える。
なぜ勝手に動くのか?
理由はシンプルで、これしかない。
Unity が「必要なタイミングで呼んでいる」から。
自分のコードが主導しているわけではない。 Unityエンジンが主導して、こちらを呼び出している。
Unityは「イベント駆動」
Unityの実行モデルは、 上から順に処理が流れていくタイプではない。
代わりに、
- フレーム更新の直前
- オブジェクトが生成された瞬間
- 物理衝突が発生したとき
といった イベント が発生し、
そのイベントに対応するメソッドが Unity側から呼ばれる。
だから、
- Update() は「毎フレーム更新イベント」
Start()は「初期化イベント」OnTriggerEnter()は「衝突イベント」
という扱いになる。
メソッド名が「契約」になっている
重要なのは、 これらのメソッドは ただの名前付きメソッドではない という点。
Unityでは、
- メソッド名
- 引数の形
- 戻り値
が 厳密に決まっている。
その形に一致したメソッドを見つけると、 Unityはそれを
「このイベントに対応する処理だ」
と判断して呼び出す。
つまり、
void Update()
という名前そのものが、 Unityとの 契約 になっている。
自分で呼ばない=制御を握っていない
ここが、普通のC#との決定的な違い。
- 自分で呼ばない
- 呼び出し元が見えない
- 実行順はエンジンが決める
だから Unity のコードは、
「自分が主導するプログラム」
ではなく、
「エンジンに呼ばれるコールバック集」
になる。
これが分かると、違和感が消える
ここまで来ると、
- なぜ Update を呼ばないのか
- なぜ Start が勝手に動くのか
- なぜ MonoBehaviour が必要なのか
全部が一本につながる。
Unityでは、
コードは「実行するもの」ではなく 「呼ばれるもの」
この前提を理解して初めて、 MonoBehaviour は「おまじない」ではなく 役割を持った仕組みとして見えてくる。
4. Unityは「コードが主役」ではない
C# や Java の世界では、
クラス(コード)が中心で、 そのクラスがプログラム全体を支配する。
これは普通のプログラマーが体に染みついている考え方。
しかし Unity では、 その前提が完全に逆転する。
Unityの主役は「Scene・GameObject・Component」
Unityの中心にあるのは、コードではなく オブジェクト だ。
- Scene … ゲーム世界そのもの(舞台)
- GameObject … Sceneに存在するあらゆる物体
- Component … GameObjectに機能を追加するパーツ
そして Unity の C# スクリプトは、 この Component の一種 に過ぎない。
◇ スクリプトは「装備品」に近い
「武器」や「防具」をキャラに装備するように、 GameObject にスクリプト(Component)を貼る。
スクリプト単体では何もできないし、 GameObject に貼らないと実体化すらしない。
普通の C# では考えられない仕組みだが、 Unityではむしろこれが本流。
◇ クラス ≠ オブジェクト
C# の世界での常識:
var p = new Player();
Unity の世界:
// ダメ:new では動かない
var p = new Player(); // ❌
// 正解:GameObject に貼る
gameObject.AddComponent<Player>(); // ✔
Unityでは「new」はオブジェクトを作らない。 オブジェクトは GameObject と Component の組み合わせでしか存在しない。
◇ C# の文脈で考えると、Unityの構造は理解しにくい
C#エンジニアほど最初に迷子になる理由はここにある。
C#では
- クラスを書く
- new して
- メソッドを自分で呼ぶ
- 処理の流れは自分で決める
Unityでは真逆。
- クラスを書く
- GameObjectに貼る
- Unityが new する
- Unityがメソッドを呼ぶ
- 処理の流れはエンジンが決める
これは プログラムというより「世界の構築」に近い。
◇ Unityではコードは“補助的な要素”
Unityの本質は「エディタ+シーン構築」。
- 3D空間を作る
- オブジェクトを配置する
- カメラやライトを置く
- そこにコンポーネントを貼る
コードは、 その動きに“ロジック”を追加する係でしかない。
だから、Unityでは
コードは主役ではない。 主役は Scene・GameObject・Component である。
この逆転を理解すると、 MonoBehaviour の役割がより鮮明に見えてくる。
5. なぜこんな設計なのか?
ここまで読んだ時点で、 普通のC#エンジニアなら一度はこう思う。
「何でこんな特殊な仕組みにしたのか?」
実際、Unityの構造は 他のプログラムの常識から大きく外れている。
でも Unity のこの設計には、 一貫した“思想” がある。
Unityの思想:
「プログラム」ではなく「世界」を作らせる
Unityがやりたいのは、
- コードを書く人
- 美術を作る人
- アニメーションを作る人
- ゲームデザイナー
- 企画者
こういう人たちが 同じ場所で「世界」を作れる環境を用意すること。
だから Unity は、 プログラム中心の設計をあえて捨てている。
GameObject を置く
→ そこに機能(Component)を貼る
→ Unityエンジンが動かす
この仕組みは、
- コードを書ける人
- 書けない人
- デザイナー
- アーティスト
全員が共通のルールでゲーム空間を扱えるようにするためのもの。
コードはその世界を動かすためのパーツに過ぎない。
「Main はどこ?」
そんなものは必要ない
Unityは、
「プログラマーが上から順に処理を書く世界」ではなく、 「エンジンが世界全体を回し続ける世界」
その中で開発者は 「必要な場所に必要なロジックを置く」。
Update も Start も OnTriggerEnter も、 全部 “世界の仕組み” に紐づいたイベント。
Unityの設計は「ゲームを作る」という目的に最適化している
ゲームは
- 毎フレームの更新
- 多数のオブジェクト
- 入力
- 物理
- アニメーション
- サウンド
- カメラ
- ライティング
- AI
こうした処理を プログラムの“書き始め”にまとめることは不可能。
だから Unityは逆にした。
エンジンが世界を動かす。 開発者はその世界にロジックを差し込む。
これが Unity が選んだ構造。
これこそが
“Unityは普通のC#プログラムではない”という本質
- Main がない
- new しない
- 自分で呼ばない
- 実行順序を持たない
- シーンとオブジェクトが主役
- コードは“動作パーツ”
すべては
「ゲーム世界を構築する」ための設計
プログラムを書くのではなく、 ゲームという「世界」を組み立てるためのエンジン。
この思想が理解できると、 MonoBehaviour の存在意義も自然に見えてくる。
💬 コメント