7. フレームワークをプロジェクトに合わせて調整する
7 つのアセンブリ、任意パッケージの削除、サービスの独自実装への差し替え、サブシステムのオフ、そしてファイルのディスク上の場所。
このフレームワークは、形を変えられるように作られています。すべてのサービスは独自実装に差し替えることも、完全にオフにすることもでき、不要な任意パーツは自分できれいに退場します。この章では、それぞれのレバーの使い方と、フレームワークがディスク上のどこにファイルを保存するかを示します。
7.1 7 つのアセンブリと、参照すべきもの
ランタイムコードは 7 つのアセンブリに分割されています。この分割は、任意の Unity パッケージを任意のままにしておくためにあります: 各アドオンアセンブリは、対応するパッケージがインストールされているときだけコンパイルされます。すべてのアセンブリは 1 つの名前空間 CommonGameSystem.Core を共有するため、あなたの using 行が変わることはありません。
あなたのスクリプトがデフォルトの Assembly-CSharp に属しているなら(アセンブリ定義ファイルを 1 つも作っていないなら)、この節はスキップしてください — Unity が自動的にすべてを参照してくれます。
コードが独自のアセンブリ定義(.asmdef)を使っている場合は、使うものに応じて参照を追加してください:
| アセンブリ | 中身 | 参照するのは... |
|---|---|---|
CommonGameSystem.Core | 23 のうち 18 のサービス: ロギング、イベント、時間、セーブ、設定、オブジェクトプーリング、オーディオ、シーンフロー、実績、タイマー、トゥイーン、トゥイーンシーケンス、乱数、ステートマシン、ステートスタック、遅延イベントキュー、コマンドレジストリ、そして UI パネルスタックのコントラクト。 | 常に。ここが心臓部で、Unity パッケージを一切必要としません。 |
CommonGameSystem.Input | 入力サービス: アクションマップコンテキストとリバインド。 | コードが IInputService を使うとき。Input System パッケージが必要です。 |
CommonGameSystem.UI | パネルスタックの uGUI 実装。 | コードが PanelStack を構築するか、その uGUI ヘルパーを使うとき。IPanelStack を解決するだけなら Core だけで足ります。 |
CommonGameSystem.Localization | ローカライゼーションサービスとそのテキストバインディングコンポーネント。 | コードが ILocalizationService を使うとき。uGUI が必要です。 |
CommonGameSystem.Assets | アセットプロバイダー(アドレスによるロード、参照カウントつき)。 | コードが IAssetProvider を使うとき。Addressables が必要です。 |
CommonGameSystem.AddressableScene | Addressables からの加算シーンロード。 | コードが IAddressableSceneService を使うとき。Addressables が必要です。 |
CommonGameSystem.Bootstrap | 自動起動シーケンス。 | 参照は不要。あなたが呼び出せるものは何もありません。 |
あなた自身のアセンブリ定義にフレームワークへの参照を追加するには:
- Project ウィンドウで自分の
.asmdefファイルを選択します。 - Inspector で Assembly Definition References を見つけて + をクリックします。
CommonGameSystem.Coreと、使用するアドオンアセンブリ(例えばCommonGameSystem.Input)を選びます。- Inspector 下部の Apply をクリックします。
7.2 任意の Unity パッケージを削除する
3 つの Unity パッケージは任意です。どれを削除してもフレームワークは壊れません: そのパッケージを必要としていたアセンブリが自らコンパイル対象から外れ、他のすべては動き続けます。起動もこれまでどおり正常に完了します。
| 削除するパッケージ | オフになるもの | 動き続けるもの |
|---|---|---|
Input System<br>com.unity.inputsystem | 入力サービスが消えます(その型はコンパイルされません)。フォーカスナビゲーションには入力が必要なため、UI パネルスタックは何もしない(no-op)版に退避します。 | それ以外のすべて。残りのサービスはすべて正常に動作し、パネルスタックの呼び出しは単に何もしなくなります。 |
uGUI<br>com.unity.ugui | uGUI パネルスタックが no-op 版に退避します。ローカライゼーションサービスが消えます。デモシーンのスクリプトも自ら除外されます。 | 入力を含む、それ以外のすべて。 |
Addressables<br>com.unity.addressables | アセットプロバイダーと Addressable シーンサービスが消えます。 | 通常のシーンサービスを含む、それ以外のすべて。 |
注意点が 2 つ。第一に、削除されたサービスに言及しているあなたのスクリプトはコンパイルできなくなります — その参照も一緒に取り除くか、ServiceLocator.TryResolve<T>(...) で利用可能性を確認し、コードを独自のスクリプティングディファインの背後に置いてください。第二に、パッケージを再インストールすれば、追加のセットアップなしですべてが戻ります。
7.3 サービスを独自実装に差し替える
すべてのサービスはインターフェイス経由でのみ使用されるため、独自バージョンに差し替えても、呼び出し側は誰も気づきません。パターンは常に同じ 3 行です: 旧インスタンスを取得し、Replace で自分のものを登録し、旧インスタンスを破棄します。
完全な、実際に動く例を示します。セーブサービスは差し替え可能なシリアライザー — データをテキストに変換する部品 — を受け入れます。このカスタムシリアライザーは、セーブを読める JSON ではなく Base64 で保存するため、プレイヤーが気軽に編集できなくなります:
using System;
using CommonGameSystem.Core;
using UnityEngine;
// The framework's serializer seam has just two methods.
public sealed class Base64SaveSerializer : ISaveSerializer
{
public string Serialize(object o)
{
string json = JsonUtility.ToJson(o);
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(json);
return Convert.ToBase64String(bytes);
}
public object Deserialize(Type t, string s)
{
byte[] bytes = Convert.FromBase64String(s);
string json = System.Text.Encoding.UTF8.GetString(bytes);
return JsonUtility.FromJson(json, t);
}
}
そして差し替えは、最初のシーンに置くスクリプトで行います:
using CommonGameSystem.Core;
using UnityEngine;
[DefaultExecutionOrder(-100)] // swap before other scripts cache the save service
public sealed class SaveSetup : MonoBehaviour
{
private void Awake()
{
var previous = ServiceLocator.Resolve<ISaveService>() as System.IDisposable;
ServiceLocator.Replace<ISaveService>(
new SaveService(new Base64SaveSerializer()));
previous?.Dispose();
}
}
同じパターンがどのサービスにも使えます: インターフェイスを実装するクラスを書く(あるいは、第 6.1 章と 6.2 章のように、フレームワークのクラスを別のオプションで構築する)か、それを Replace するだけです。差し替えをきれいに行うルールは 3 つです:
- 早めに差し替える。 スクリプトは解決した参照を保持し続けるので、サービスの差し替えは最初のシーンで、実行順 -100 で、他の何かが旧インスタンスをキャッシュする前に行ってください。いくつかのフレームワークサービスは起動時に相互に配線されます — 例えば、設定の永続化と実績は、起動時に渡されたセーブサービスを保持し続けます — そのため、遅い差し替えは、それ以降に解決するコードにしか影響しません。
- 押しのけたものは破棄する。
Replaceは旧インスタンスを破棄しません。7 つのサービス(時間、オブジェクトプール、オーディオ、UI パネルスタック、スケジューラー、トゥイーン、トゥイーンシーケンス)は、それぞれヒエラルキーに "[CGS] ..." という名前の隠れたヘルパーオブジェクトを持っています。旧インスタンスを破棄するとそれも取り除かれます。破棄をスキップすると、セッション中ずっと動き続けます。 - 破棄は差し替えの後に。 決して前にしないでください。その間に、すでに死んだインスタンスを解決してしまうスクリプトが出ないようにするためです。
7.4 サブシステムをオフにする
機能を完全にオフにするには、組み込みの no-op 版に差し替えます。すべての呼び出し側は動き続け、呼び出しはただ何もしなくなります。読み取りは安全なデフォルト(ゼロ、false、空、「見つからない」)を返します。
ServiceLocator.Replace<IAudioService>(new NullAudio()); // total silence, no code changes
すべてのサービスに 1 つずつあります:
| サービス(インターフェイス) | No-op クラス |
|---|---|
| ILogger | NullLogger |
| IObjectPoolService | NullObjectPool |
| ITimeService | NullTimeService |
| IEventBus | NullEventBus |
| ISaveService | NullSaveService |
| IConfiguration | NullConfiguration |
| IInputService | NullInputService |
| IAudioService | NullAudio |
| IPanelStack | NullPanelStack |
| ISceneService | NullSceneService |
| ILocalizationService | NullLocalization |
| IScheduler | NullScheduler |
| ITweenService | NullTweenService |
| IAssetProvider | NullAssetProvider |
| IRandomService | NullRandomService |
| IStateMachineService | NullStateMachineService |
| IPushdownStackService | NullPushdownStackService |
| ITweenSequenceService | NullTweenSequenceService |
| IAddressableSceneService | NullAddressableSceneService |
| IDeferredBus | NullDeferredBus |
| ICommandRegistry | NullCommandRegistry |
| IAchievementService | NullAchievementService |
7.3 の破棄ルールはここにも当てはまります: "[CGS] ..." ヘルパーオブジェクトを持つ 7 つのサービスのどれかをオフにするときは、押しのけたインスタンスを破棄してください。
7.5 ファイルはディスク上のどこにあるか
フレームワークが書き込むものはすべて、Unity 標準のゲームごとのデータフォルダ Application.persistentDataPath の配下に置かれます:
- Windows:
C:\Users\<you>\AppData\LocalLow\<Company>\<Product> - macOS:
~/Library/Application Support/<Company>/<Product> - Linux:
~/.config/unity3d/<Company>/<Product>
その中身は:
- セーブ —
saves/<slot>.json、スロットごとに 1 ファイルと、直前バージョンの.bakバックアップ。書き込み中、サービスはまず一時ファイルに書いてからアトミックに入れ替えるため、クラッシュや電源断が既存のセーブを壊すことはありません。 - 設定 — 同じ
saves/フォルダ内のconfig_で始まるファイル(例えばconfig_AudioSettings.json)。セーブサービスをオフにすると、設定は自動的に Unity の PlayerPrefs にフォールバックします。 - 実績 — 同じフォルダに、セーブサービス経由で保存されます。
- 入力リバインド — Unity の PlayerPrefs に保存されます(Windows ではレジストリ、macOS では plist ファイル)。
会社名と製品名は Edit > Project Settings > Player から取られます。ファイルを同期するクラウドセーブシステム(Steam Auto-Cloud など)は、saves/ フォルダをそのまま対象に指定できます。