なぜ Unity は C# を選び、Unreal は選ばなかったのかについての個人の考察
はじめに
ゲームエンジンの言語の話になると、よくこんな対比が出てきます。
Unity は C#。
Unreal は C++ と Blueprint。
この違い、ただの好みではありません。
エンジンの設計思想そのものに関わっています。
C# が優れているとか、C++ が古いとか、そういう単純な話ではない。
両者はそもそも目指している開発体験が違うのです。
Unity が辿った言語統一の歴史
今でこそ「Unity といえば C#」という印象がありますが、最初からそうだったわけではありません。
初期の Unity では、開発者のバックグラウンドに合わせて複数の言語を選択できるのが特徴でした。
具体的には次の3つです。
-
C#
-
UnityScript(JavaScript 風の独自言語)
-
Boo(Python 風の言語)
当時の Unity は「スクリプト言語の自由度」を売りの一つにしており、Web 開発者は UnityScript、Python 系に慣れている人は Boo、という具合に選べるようになっていました。
しかし、この方針は長くは続きませんでした。
Unity 2017 頃から段階的に非推奨となり、
Unity 2019.1 を最後に UnityScript と Boo は完全に削除されます。
結果として、Unity のスクリプト言語は C# のみに統一されました。
参考
https://discussions.unity.com/t/which-release-removed-support-unityscript-and-boo/1624299
なぜこうなったのか。理由はいくつかあります。
まず、複数言語の維持コストです。
エンジン側はコンパイラ、ツールチェーン、デバッグ環境などをすべてサポートし続ける必要があります。
さらに重要なのは、UnityScript と Boo の実態です。
これらは見た目こそ別言語でしたが、内部では Mono 上で動作するスクリプトでした。
つまり、
-
実行環境は共通(Mono)
-
文法だけが違う
という状態だったのです。
その結果、コミュニティでも徐々に C# が主流になっていきました。
エコシステム(ライブラリ・チュートリアル・Asset Store など)も C# 中心に発展していきます。
こうして Unity は最終的に、
「C# を第一言語とするエンジン」
という現在の形に落ち着きました。
Unity が C# を中心に据えた理由
Unity を少し触るとすぐ分かりますが、このエンジンはかなりツール寄りです。
インスペクターで値を触りながら動作を見る。
エディタ拡張を書く。
小さく作ってすぐ動かす。
このワークフローと C# の相性は、正直かなりいい。
実際に C# でツールを書いたことがある人なら分かると思いますが、
Reflection がとにかく便利です。
例えば Unity では、
[SerializeField]
[Range]
みたいな Attribute を付けるだけで、インスペクターの挙動が変わります。
これ、内部では Reflection で型情報を読んでいます。
つまり Unity の設計は、
コードにメタデータを書いて、ツール側がそれを読む
というスタイルです。
この仕組みを C++ でやろうとするとかなり面倒になります。
C# なら比較的素直に書ける。
それだけでも、エンジンの開発体験はかなり変わる。
C# の「ちょうどよさ」
もう一つ大きいのが、言語としての扱いやすさです。
GC があるのでメモリ管理はかなり楽。
コンパイルも速い。
例外も普通に使える。
C++ でゲームロジックを書いていると、
ビルド待ちの時間にコーヒーをもう一杯入れたくなる瞬間があります。
Unity のスクリプト開発は、そのストレスがかなり少ない。
エンジンのコアはネイティブ。
ゲームロジックはマネージド。
Unity はこの割り切りをかなり早い段階で決めました。
結果として、C# が事実上の第一言語になったわけです。
Unreal は最初から C++ の世界だった
一方の Unreal。
こちらは最初からC++ が中心のエンジンです。
しかも普通の C++ ではありません。
Unreal のコードを見ると、こんなマクロが大量に出てきます。
UCLASS()
UPROPERTY()
UFUNCTION()
最初はただのマクロに見えます。
でも実際には違う。
これらは UnrealHeaderTool(UHT) というツールで解析され、
エンジンのリフレクション情報が生成されます。
つまり Unreal は、
C++ をベースにした独自のメタ言語環境
と言った方が近い。
この構造の上に、
-
GC
-
シリアライズ
-
エディタ連携
が全部乗っています。
UObject と GC の深い結びつき
Unreal の GC は、普通の GC 言語とは少し違います。
UPROPERTY を通じて参照関係を追跡し、
UObject を回収します。
つまり、
UObject
UPROPERTY
GC
この3つは完全に一体です。
ここに C# を持ち込むとどうなるか。
マネージド GC と UObject GC。
GC が二重になる。
オブジェクトの寿命管理はかなり複雑になるでしょう。
エンジンの根っこをいじる話になります。
もう一つの問題:ネイティブ境界
C# をエンジンに組み込む場合、必ず出てくる問題があります。
ネイティブ境界です。
C# から C++ を呼ぶ。
そのたびに P/Invoke やラッパーを通る。
普通のアプリなら問題になりません。
でもゲームエンジンは少し事情が違う。
毎フレーム、何千回も API を呼びます。
Transform を読む。
コンポーネントを取得する。
Actor を生成する。
この境界コストは無視できません。
AAA タイトルを想定した Unreal にとっては、
かなり気になる部分だったはずです。
C# からネイティブコードを呼ぶ場合、通常は P/Invoke などの仕組みを使います。
https://learn.microsoft.com/en-us/dotnet/standard/native-interop/pinvoke
Unreal がスクリプト言語を捨てた理由
実は Unreal にも昔はスクリプト言語がありました。
UnrealScript です。
UE3 までは、
C++
UnrealScript
という2層構造でした。
しかし UE4 で Epic はこの言語を捨てます。
理由はいくつもありますが、
一番大きいのはメンテナンスコストです。
言語を2つ維持するのは単純に大変です。
コンパイラ
デバッガ
ランタイム
バインディング
全部必要になります。
そこで Epic が選んだのが、
C++ + Blueprint
という構成でした。
Blueprint は「別の答え」
Blueprint はスクリプト言語ではありません。
ノードベースのビジュアルスクリプトです。
ただし中身は完全に C++ 側と連携しています。
UFUNCTION を付けると Blueprint から呼べる。
UPROPERTY を付けるとエディタに出る。
つまり Blueprint は
C++ メタシステムのフロントエンド
です。
新しい言語を増やしたわけではない。
C++ の世界をツールで拡張しただけ。
これが Unreal の答えでした。
それでも C# を Unreal で使いたい人たち
とはいえ、C# を Unreal で書きたいという人はずっといます。
実際、コミュニティではいくつかのプロジェクトが作られています。
例えば
UnrealCLR
や
UnrealSharp
どちらも .NET を Unreal に橋渡しする試みです。
公式ではありませんが、長く続いているプロジェクトです。
それだけ C# の開発体験に魅力がある、ということでもあります。
結局、言語選択はエンジンの思想
ここまで見ると、構図はわりとシンプルです。
Unity は
ツールと開発速度を重視したエンジン。
Unreal は
ネイティブ統合とパフォーマンスを突き詰めたエンジン。
その結果として、
Unity は C# を選び、
Unreal は C++ に残った。
どちらが正しいという話ではありません。
エンジンの思想が違えば、
選ばれる言語も変わる。
それだけのことなのです。
追記:UnrealがC++中心になった「時代」と、Verseが生まれた理由
この記事に多くの反響をいただき、ありがとうございます。
いくつか補足しておきたい点が出てきたため、追記します。
まず一点目は、UnrealがC++を中心に設計された「時代背景」についてです。
初代Unreal Engineが登場したのは1998年。
当時はまだ .NET も C# もこの世に存在していませんでした。
今の視点で見ると「なぜC#を採用しなかったのか」という議論になりがちですが、
当時はゲームエンジン=ネイティブコード(C++)が絶対的な前提だった時代です。
Unity(2005年登場)のように、マネージド言語を前提とした設計は、
当時のゲームエンジンではかなり珍しいアプローチでした。
この「出発点」の違いが、現在の両エンジンのアーキテクチャの根幹に、
今も色濃く残っているのだと感じます。
そしてもう一点、触れておきたいのが 新言語「Verse」 の存在です。
「Game Developers Conference 2024(GDC 2024)」などの講演でも触れられていましたが、
Epicは将来的にUE6世代でVerseをより深く統合していく方針を示しています。
ここで「それなら今からでもC#を採用すればよかったのでは?」という疑問も浮かびますが、
Verseの設計思想を見ると、その狙いは単なる「スクリプト言語の置き換え」ではないことがわかります。
Verseが目指している方向性として、
公開されている講演や資料を見る限りでは、
特に次のような言語的特徴が強調されているように見えます。
失敗コンテキスト(failure context):
Verseでは処理の成功・失敗を言語レベルで扱う仕組みがあり、
条件に応じて処理を安全に中断したり、
ゲームロジックの整合性を保ちやすくする設計になっていると説明されています。
式ベースの言語設計:
Verseは「式(expression)」を中心に構成された言語であり、
多くの処理が値を返す式として記述できるよう設計されています。
これは従来の命令型言語とはやや異なるスタイルです。
また、言語レベルで並行処理を扱える点も、
特徴の一つとして紹介されています。
もちろん、これらの処理自体はC#のような汎用言語でも
実装すること自体は可能です。
しかしVerseでは、それらをライブラリやフレームワークではなく、
言語機能として提供することが特徴だと説明されています。
そのため、従来のゲームスクリプト言語の延長というよりも、
関数型や宣言的なアプローチなどを取り入れた、
新しいプログラミングモデルを志向した言語設計になっているように見えます。
Discussion