[Unity] 入門講座 #00 : Unityは“普通のC#プログラム”ではない

はじめに

何となく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 の存在意義も自然に見えてくる。