7. Adapting the Framework to Your Project

The seven assemblies, removing optional packages, replacing services with your own, switching subsystems off, and where files live on disk.

The framework is built to be reshaped. Every service can be replaced with your own implementation or switched off entirely, and the optional parts remove themselves cleanly when you do not want them. This chapter shows each of those levers, plus where the framework keeps its files on disk.

7.1 The seven assemblies, and which ones to reference

The runtime code is split into seven assemblies. The split exists so that optional Unity packages stay optional: each add-on assembly compiles only when its package is installed. All of them share one namespace, CommonGameSystem.Core, so your using line never changes.

If your scripts live in the default Assembly-CSharp (you have not created any assembly definition files), skip this section — Unity references everything for you automatically.

If your code uses its own assembly definition (.asmdef), add references based on what you use:

AssemblyWhat is insideReference it when...
CommonGameSystem.Core18 of the 23 services: logging, events, time, saving, settings, object pooling, audio, scene flow, achievements, timers, tweens, tween sequences, random numbers, state machines, state stacks, the deferred event queue, the command registry, and the UI panel stack contract.Always. This is the heart, and it needs no Unity packages at all.
CommonGameSystem.InputThe input service: action-map contexts and rebinding.Your code uses IInputService. Requires the Input System package.
CommonGameSystem.UIThe uGUI implementation of the panel stack.Your code constructs PanelStack or uses its uGUI helpers. Just resolving IPanelStack needs only Core.
CommonGameSystem.LocalizationThe localization service and its text-binding component.Your code uses ILocalizationService. Requires uGUI.
CommonGameSystem.AssetsThe asset provider (loading by address, reference-counted).Your code uses IAssetProvider. Requires Addressables.
CommonGameSystem.AddressableSceneAdditive scene loading from Addressables.Your code uses IAddressableSceneService. Requires Addressables.
CommonGameSystem.BootstrapThe automatic start-up sequence.Never. It has nothing for you to call.

To add a framework reference to your own assembly definition:

  1. Select your .asmdef file in the Project window.
  2. In the Inspector, find Assembly Definition References and click +.
  3. Pick CommonGameSystem.Core, plus any add-on assemblies you use (for example CommonGameSystem.Input).
  4. Click Apply at the bottom of the Inspector.

7.2 Removing an optional Unity package

Three Unity packages are optional. Removing one never breaks the framework: the assembly that needed it excludes itself from compilation, and everything else keeps working. Start-up still completes normally.

Package you removeWhat switches offWhat still works
Input System<br>com.unity.inputsystemThe input service disappears (its types are not compiled). The UI panel stack falls back to a no-op version, because focus navigation needs input.Everything else. Every remaining service runs normally; panel stack calls simply do nothing.
uGUI<br>com.unity.uguiThe uGUI panel stack falls back to the no-op version. The localization service disappears. The demo scene's scripts exclude themselves too.Everything else, including input.
Addressables<br>com.unity.addressablesThe asset provider and the addressable scene service disappear.Everything else, including the regular scene service.

Two notes. First, your scripts that mention a removed service will stop compiling — remove those references as well, or check availability with ServiceLocator.TryResolve<T>(...) and keep the code behind your own scripting define. Second, reinstalling the package brings everything back with no further setup.

7.3 Replacing a service with your own implementation

Every service is used only through its interface, so you can substitute your own version and no caller will notice. The pattern is always the same three lines: grab the old instance, register yours with Replace, then dispose the old one.

Here is a complete, real example. The save service accepts a pluggable serializer — the piece that turns your data into text. This custom serializer stores saves as Base64 instead of readable JSON, so players cannot casually edit them:

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);
    }
}

And the swap, in a script placed in your first scene:

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();
    }
}

The same pattern works for any service: write a class that implements the interface (or build one of the framework's classes with different options, as in chapters 6.1 and 6.2), then Replace it. Three rules make swaps clean:

  • Swap early. Scripts keep the reference they resolved, so replace services in your first scene, at execution order -100, before anything else caches the old one. A few framework services also wire themselves together at start-up — for example, settings persistence and achievements hold the save service they were started with — so late swaps only affect code that resolves afterward.
  • Dispose what you displace. Replace does not destroy the old instance. Seven services (time, object pool, audio, UI panel stack, scheduler, tween, tween sequences) each own a hidden helper object named "[CGS] ..." in the hierarchy. Disposing the old instance removes it; skipping the dispose leaves it running all session.
  • Dispose after the swap, never before, so no script can resolve an already-dead instance in between.

7.4 Switching a subsystem off

To turn a feature off completely, replace it with its built-in no-op version. All callers keep working; the calls just do nothing. Reads return safe defaults (zero, false, empty, or "not found").

ServiceLocator.Replace<IAudioService>(new NullAudio()); // total silence, no code changes

Every service has one:

Service (interface)No-op class
ILoggerNullLogger
IObjectPoolServiceNullObjectPool
ITimeServiceNullTimeService
IEventBusNullEventBus
ISaveServiceNullSaveService
IConfigurationNullConfiguration
IInputServiceNullInputService
IAudioServiceNullAudio
IPanelStackNullPanelStack
ISceneServiceNullSceneService
ILocalizationServiceNullLocalization
ISchedulerNullScheduler
ITweenServiceNullTweenService
IAssetProviderNullAssetProvider
IRandomServiceNullRandomService
IStateMachineServiceNullStateMachineService
IPushdownStackServiceNullPushdownStackService
ITweenSequenceServiceNullTweenSequenceService
IAddressableSceneServiceNullAddressableSceneService
IDeferredBusNullDeferredBus
ICommandRegistryNullCommandRegistry
IAchievementServiceNullAchievementService

The disposal rules from 7.3 apply here too: when switching off one of the seven services that own a "[CGS] ..." helper object, dispose the instance you displaced.

7.5 Where your files live on disk

Everything the framework writes goes under Unity's standard per-game data folder, Application.persistentDataPath:

  • Windows: C:\Users\<you>\AppData\LocalLow\<Company>\<Product>
  • macOS: ~/Library/Application Support/<Company>/<Product>
  • Linux: ~/.config/unity3d/<Company>/<Product>

Inside it:

  • Savessaves/<slot>.json, one file per slot, plus a .bak backup of the previous version. During a write, the service writes a temporary file first and swaps it in atomically, so a crash or power loss never corrupts an existing save.
  • Settings — files starting with config_ in the same saves/ folder (for example config_AudioSettings.json). If you switch the save service off, settings fall back to Unity's PlayerPrefs automatically.
  • Achievements — stored through the save service in the same folder.
  • Input rebinds — stored in Unity's PlayerPrefs (on Windows, the registry; on macOS, a plist file).

Company and product names come from Edit > Project Settings > Player. Cloud-save systems that sync files (such as Steam Auto-Cloud) can simply be pointed at the saves/ folder.


Next: 8. Troubleshooting and FAQ