Imaginantia

思ったことを書きます

GameObjectとUObjectの違いを考える

折角なので (?) UnityとUEの設計思想について、各エンジンにおける「オブジェクト」概念を調べてみることで垣間見る、ということをやろうと思います。

純粋に「設計思想」について考えるだけであって、それぞれの完成度がどうとか、会社がどうとかという話は関係ありません。

できるだけニュートラルに書いてみようと思いますが、多少偏りはあると思います。

参照するドキュメントページはコレです:

どれも現在最新バージョンのドキュメントです。

Unity

世界 (Scene) に存在する全ての物体の基礎となるクラスがGameObjectです。各GameObjectは座標変換を表すtransformを持っていて、つまり「全てのオブジェクトは、空間内に位置する」という設計です。つまり例えばネットワーク管理オブジェクトなども、恐らく位置概念は不要であるにも関わらず、必ず位置の情報を持たなければなりません

transformは位置だけではなく階層構造を保持するものでもあり、各GameObjectは子GameObjectを複数持つことが出来ます。「オブジェクトは階層構造を成す」もので、これによって Scene 内のオブジェクト一覧は hierarchy と呼ばれています。

各GameObjectは複数のComponentを持ちます。GameObjectの挙動を指定するのは全てComponentなので、「オブジェクトはコンポーネントによって意味が規定される」わけです。つまり「GameObject自体は、とてもまっさらな存在」です。

 

Componentを作成するときに継承されるのがMonoBehaviourクラスです。名前の通り挙動 (Behaviour) を表すものです。Unity本体の動作と協調する為にたくさんのイベントハンドラを持っています。持っている変数には特筆すべきものはないですね。

イベントハンドラ群を見てみると、Start/Updateの他、Applicationの状態・可視性の変化・マウス操作・レンダリング時・Animator/AudioFilter/Collision/Joint/Particle などのイベントを受け取ることができるようになっています。「どんな挙動も、これらの外部イベントを受け取りたくなるだろう」という想定になっていると言えるわけです。加えて OnConnectedToServer OnFailedToConnect などもあり、「誰だって通信したくなる」気持ちも持っているのかもしれません。

…とは言え、多分過去の名残でこうなってしまっているだけで、最近のAPIだとインタフェースを追加実装するのが一般的かと思います。

Unreal Engine 5

UObjectは世界 (Level) に存在する物体、というよりももう少し広く、レベル間を跨ぐことも出来る感じがします。実際位置の情報などは持っておらず、"物体"というよりも"対象"という訳がふさわしいかもしれません。とは言え「子オブジェクト」の概念はあって、入れ子構造を表現できます。

UObjectは基本的にはそれだけで使うよりは継承してカスタムな挙動を作る為に使われるクラスです。例えばレベルに存在するオブジェクトはActorと呼ばれ、UObjectを継承しています。「子オブジェクト」も、別にUObjectがそのままぶら下がっているわけではなく、基本的には派生クラスが付いているものです。つまりは「オブジェクト自体が挙動を持つ」仕組みです。

なのでUObjectには外部イベントを拾うハンドラがたくさん存在しています。正直関数名から何に使うのかを想定するのはかなり厳しいように見えますが、まぁ色んなユースケースに合わせて拡張していった結果であることがわかります。実際 4.27 から 5.3 にかけてメソッドは10個程度増えているようです:

  • AppendToClassSchema
  • ConformSparseClassDataStruct
  • DeclareConstructClasses
  • DeclareCustomVersions
  • GetExtendedAssetRegistryTagsForSave
  • IsCapturingAsRootObjectForTransaction
  • OverrideConfigSection
  • PostCDOCompiled
  • PostLoadAssetRegistryTags
  • ResolveSubobject
  • SetEditChangePropagationFlags
  • TryUpdateDefaultConfigFile

UObjectはGameObject/MonoBehaviourとは違い、「いろんな状況をまるごとこいつで解決する」という姿勢で作られているように見えますね。

ちなみに Unity 2017.1 から 2023.2 になって追加されたメソッドは GameObject.TryGetComponent GameObject.FindAnyObjectByType MonoBehaviour.OnParticleSystemStopped MonoBehaviour.OnParticleUpdateJobScheduled くらいでした。

 

レベルに存在する物体 (GameObjectに対応するもの)である Actor (AActorクラス) についても少し見てみます。これはUObjectの直接の子クラスで「レベルに置かれたモノ」を表すのですが、これ自体は位置の情報を持っていません。代わりにRootComponentと呼ばれるUSceneComponentのインスタンスが持っています。というわけでコンポーネントという概念自体はUEにもあります。オブジェクト自体に挙動を記述できる他、コンポーネントを使うことで挙動をつなぎ合わせることも出来るわけです。UEはそういう「なにかをやる、という目的だけで言うと手段がたくさんある」環境であると思います。

Unityは「位置を持つ」というのはもうオブジェクトに根底から必要な要件として設計されているわけですが、UEは逆にコンポーネントとして取り外し可能な程度のものだと認識しているわけですね (Actorから取り外すことはできませんけども)。

さて、AActorのメソッドもたくさんあるので紹介できる気がしません。ただ、とりあえず「何でも出来るものを作って、その機能を切り出して使ってもらう」という姿勢である、ということは言えると思います。 例えば TakeDamage メソッドがあり、SetReplicates メソッドがあります。つまり「万物はダメージを受けるし、通信によってサーバと同期される」という想定なのです。

まとめ

以上のことを大雑把に言うと、「Unityは (できるだけ) 最小限の機能を基盤に置いて、後からつなぎ合わせる設計」「UEは、最大限の機能を基盤に置いて、好みに合わせて抜いてもらう設計」と言えるのではないかと思います。

追記: 「位置」に関しては違うのでは?という意見をもらいました。その通りだと思います。 UEはC++ベースなので「位置」概念が希薄だったのかなと、ほとんどの概念系クラスには位置は要らないですよね。 Unityは代わりに「全て (概念を含めて) を物体化」させることで (概念を含めた) 管理をしやすくする意図があったのかなと思っています。概念と物体の区別は必要ない、という単純化。Unity"3D"って言ってますし。

これは結局選択なので、好みはあれど良し悪しは (観点が無ければ!) 言いにくいものです。逆に言えば、「ソフトウェア開発の基本ができている」という観点から見ればUnityは良く出来ていて、「ゲームに必要な機能がまず揃っている」という観点から見るとUEは良く出来ているわけです。

が、その観点が正しいのかどうかはまた別の話です。良い観点が見つかると良いですね。

 

というわけで、なんとなくドキュメントを眺める回をやってみました。

もっと色々書けることはあると思いますが、何にせよ「基本的なクラス設計からどういうゲームエンジンなのかは伺える」ということを主張したかったので、結果には満足です。

ちなみに GodotのObject/Node は面白いなあって思います。もっとモジュール化が徹底されている雰囲気で (個人的な) 好感度が高いです。

おわり。

 


Unityの回転表現がQuaternion (向きに依存しない標準的な回転表現) で、UEの回転表現がRotator (Euler角、地上方向基準だと使いやすい) なところから既に伺えることはかなり多いなあと個人的には思っています。