ベースゲームも Mod として扱う ― Everything is a Mod という設計
本編のキャラクターやステージを「native」という名前の Mod として扱う設計にした理由と、そのためにコードとデータをどう分けたかをまとめます。
- Unity
- 設計
- Mod
いま作っている 3D ベルトスクロールアクションでは、最初の段階から 「本編のコンテンツも Mod の一つとして扱う」というルールを決めています。 プロジェクト内では Everything is a Mod と呼んでいるルールです。 この記事では、なぜそうしたのか、そして実際にどう分けているのかを書きます。
何を決めたのか
ゲームに登場するキャラクター、ステージ、テクスチャ、3D モデル、サウンド、
モーション。これらのデータはすべて、ゲーム本体のビルドには含めず、
mods というフォルダの下に置きます。本編のデータも例外ではなく、
native という名前の Mod として同じ場所に置かれます。
ゲーム本体に含まれるのは、コード、シェーダ、基本的なマテリアル、 そしてエディタ用のツールだけです。ゲームの中身は、すべて外にあります。
なぜそうしたのか
Mod 対応を後から付け足そうとすると、ほぼ確実に苦しむことになります。 本編のデータは本編専用の経路で読み込まれ、Mod のデータは Mod 用に後付けされた 別の経路で読み込まれる。すると、本編ではできるのに Mod ではできないことが 必ず生まれます。本編のキャラクターだけが使える特別な処理が、 気づかないうちにあちこちに入り込んでしまうからです。
本編そのものを Mod として作れば、この問題は起きません。 本編で作れるものは、必ず Mod でも作れる。 本編の開発が、そのまま Mod の仕組みのテストにもなります。 本編を作るために足りない機能があれば、それは Mod 作者にとっても 足りない機能なので、同時に解決されます。
読み込み順と上書き
複数の Mod を入れた場合は、読み込み順を記述したファイルで順番を決めます。
先頭が native、その後にユーザーが追加した Mod が続く形です。
同じ ID のデータが複数の Mod に存在した場合は、後から読み込まれたほうが勝つ という単純なルールにしています。たとえば、本編のあるキャラクターの体力を 変えたいなら、同じ ID のキャラクターデータを持った Mod を作って後ろに並べるだけです。 本編のファイルを直接書き換える必要がありません。
後勝ちにした理由は、挙動を予想しやすいからです。 データをマージするような賢い仕組みも考えられますが、 どのフィールドがどの Mod から来たのかが分かりにくくなり、 不具合が起きたときの原因追跡が極端に難しくなります。 「最後に読んだものがそのまま使われる」なら、誰でも結果を予想できます。
データの読み書きは一箇所に集める
もう一つのルールとして、Mod のデータの読み書きは、 専用のローダークラスを必ず経由することにしています。 ファイルを直接開く処理を、コードのあちこちに書かないということです。
これには実利があります。データの置き場所は OS によって違い、 Windows と macOS ではまったく別のパスになります。 読み書きが一箇所に集まっていれば、その違いを吸収するのも一箇所で済みます。 読み込みの失敗時の扱いや、キャッシュの仕組みを後から入れるときも、 そこだけを変えれば全体に反映されます。
代わりに引き受けたもの
この設計には代償もあります。一番大きいのは、 Unity の便利な仕組みの一部が使えなくなることです。
Mod のデータはプレイヤーの端末に置かれ、ゲームの実行中に読み込まれます。 つまり、Unity エディタが提供している、インポート時にアセットを変換する仕組みの 恩恵を受けられません。FBX のモデルや、Unity 独自形式のアニメーションは そのままでは Mod に置けないのです。この問題にどう対処したかは、 別の記事で詳しく書く予定です。
それでも、この設計を選んだことは正しかったと考えています。 後から Mod 対応を入れる苦労に比べれば、最初から制約を受け入れて 作るほうが、ずっと見通しが良いからです。