6. 设置步骤图解
把 CGS 连接到你自己资产的可选配方——音频混音器、输入动作、IL2CPP link.xml 与 Addressables。
本章没有任何一步是开始使用框架所必需的——一切开箱即用。这些步骤把框架连接到你的资产:你的音频混音器、你的输入绑定、你的构建设置,以及(可选的)Addressables。每一步都是可以从头到尾照做的完整配方。
6.1 使用你自己的音频混音器
音频服务附带一个内置混音器,因此声音立即可用。当你准备使用自己的 Unity AudioMixer 资产时,它需要满足两个条件:
- Master 之下有三个组,名称严格为
Music、Sfx和Voice。 音频服务把每条通道路由到同名的组。 - 四个已暴露(exposed)的浮点音量参数,名称为
MasterVolume、MusicVolume、SfxVolume和VoiceVolume。暴露参数的方法:选中一个组,在 Inspector 中右键点击它的 Volume 字段,选择 “Expose ... to script”,然后在混音器的 Exposed Parameters 下拉菜单中重命名。

Audio Mixer 窗口,Master 之下是三个必需的组(Music、Sfx、Voice)。Exposed Parameters 下拉菜单(右上角)必须列出四个音量参数:MasterVolume、MusicVolume、SfxVolume、VoiceVolume。
然后在你的第一个场景里用一个小脚本把混音器交给框架。它会围绕你的混音器构建一个新的音频服务并替换进去:
using CommonGameSystem.Core;
using UnityEngine;
using UnityEngine.Audio;
[DefaultExecutionOrder(-100)] // swap before other scripts cache the audio service
public sealed class AudioSetup : MonoBehaviour
{
[SerializeField] private AudioMixer gameMixer; // assign your mixer in the Inspector
private void Awake()
{
var options = AudioOptions.Default;
options.Mixer = gameMixer;
// Used different parameter names? Point the service at them:
// options.masterParam = "MyMasterVol";
var previous = ServiceLocator.Resolve<IAudioService>() as System.IDisposable;
ServiceLocator.Replace<IAudioService>(new AudioService(
ServiceLocator.Resolve<IConfiguration>(),
ServiceLocator.Resolve<IEventBus>(),
ServiceLocator.Resolve<IObjectPoolService>(),
ServiceLocator.Resolve<ITimeService>(),
options));
previous?.Dispose(); // removes the old service's hidden helper object
}
}
你的选项菜单里的音量滑块与设置服务对话,而不是直接与混音器对话。用 Get<AudioSettings>() 读取当前设置组,修改字段,然后调用 Set(...),再调用 FlushPending<AudioSettings>()。这次冲刷会保存更改,并立即把新音量推送到混音器。完整参考:Audio 与 Configuration。
6.2 接入你自己的输入动作
输入服务读取的是你创建的 Unity Input System 资产(一个 .inputactions 文件)——框架只附带一个空的占位资产。你的资产定义动作映射(“Gameplay”、“Menu”)以及其中的绑定。
- 创建资产:在 Project 窗口中右键,选择 Create > Input Actions。取个类似
GameInput的名字。 - 打开它,添加你的动作映射和动作——例如一个带 “Move” 和 “Jump” 的 “Gameplay” 映射。
- 如果你使用框架的 UI 面板栈及其自动输入切换,还要添加两个名称严格为
ui.panel和ui.modal的映射。面板栈会在菜单打开期间启用它们。名称区分大小写。

Input Actions 编辑器,显示一个包含 “Gameplay” 映射外加两个面板栈映射 ui.panel 与 ui.modal(高亮显示)的资产——这就是要达到的最终状态。框架从不编辑这个资产——死区、长按和绑定完全由你掌控。
在你的第一个场景里用一个脚本接入该资产。它会围绕你的资产重建输入服务,并重建面板栈,让菜单导航也使用它:
using CommonGameSystem.Core;
using UnityEngine;
using UnityEngine.InputSystem;
[DefaultExecutionOrder(-100)] // swap before other scripts cache these services
public sealed class InputSetup : MonoBehaviour
{
[SerializeField] private InputActionAsset actions; // your .inputactions asset
private void Awake()
{
var source = new HardcodedInputKeyMapSource(actions);
var bus = ServiceLocator.Resolve<IEventBus>();
var time = ServiceLocator.Resolve<ITimeService>();
var oldInput = ServiceLocator.Resolve<IInputService>() as System.IDisposable;
var oldUi = ServiceLocator.Resolve<IPanelStack>() as System.IDisposable;
ServiceLocator.Replace<IInputKeyMapSource>(source);
var input = new DefaultInputService(source, bus);
ServiceLocator.Replace<IInputService>(input);
ServiceLocator.Replace<IPanelStack>(
new PanelStack(bus, input, time, PanelStackOptions.Default));
oldInput?.Dispose();
oldUi?.Dispose();
}
}
如果你不使用面板栈,删掉两行 IPanelStack 相关代码和 oldUi 相关的行。如果你的脚本放在自己的程序集定义中,这个脚本需要引用 CommonGameSystem.Input、CommonGameSystem.UI 和 Unity.InputSystem —— 见第 7.1 章的表格。
能在重启后保留的重绑定。 当玩家重新映射某个控制键后(服务的交互式重绑定会引导他们按下新键),随后调用 SaveBindingOverrides()。覆盖值存储在 Unity 的 PlayerPrefs 中。在启动时调用一次 LoadBindingOverrides() 来恢复它们;“恢复默认”按钮则调用 ResetBindingOverrides()。完整参考:Input。
6.3 IL2CPP 构建:保住你的存档类(link.xml)
IL2CPP 构建会剥离看起来未被使用的代码,以缩小游戏体积。只通过序列化创建的类——你的存档数据、你的自定义设置组、你序列化的事件类——在剥离器眼里就恰好像是没被使用的。结果:存档在编辑器里正常,到了构建出的游戏里却悄悄失效。
修复方法是一个 link.xml 文件,它告诉 Unity“永远不要剥离这些”。在 Assets/ 下的任意位置(放在根目录即可)创建一个名称严格为 link.xml 的文件,列出你自己的类:
<linker>
<assembly fullname="Assembly-CSharp">
<type fullname="MyGame.PlayerSave" preserve="all"/>
<type fullname="MyGame.OptionsSave" preserve="all"/>
</assembly>
</linker>
这个文件放在哪? 保存为
Assets/link.xml。每一行<type>都写你自己的一个类,并带上完整命名空间。“Assembly-CSharp” 是项目脚本的默认程序集——如果你的代码使用程序集定义,就改用那个程序集的名称。
经验法则:
- 你传给
Save<T>(...)或作为设置组存储的每个类,都加一行<type>。 fullname是包含命名空间的类名。这里的拼写错误会静默失败,所以请从你的代码里复制。- 框架自身的类型已经受到保护。你只需列出你的类。
- 这只影响 IL2CPP 构建。编辑器和 Mono 构建从不剥离,这正是这个 bug 直到真正构建时才现身的原因。
6.4 Addressables 设置(仅当你按地址加载资产时需要)
有两个服务需要 Unity 的 Addressables 包:资产提供器(按文本地址加载预制体、精灵和音频,带自动引用计数)和可寻址场景服务(来自 Addressables 的叠加场景)。如果两者你都不用,跳过本节——没有这个包框架也运行良好。
- 从 Window > Package Manager 的 Unity Registry 选项卡安装 Addressables(
com.unity.addressables)。 - 打开 Window > Asset Management > Addressables > Groups,点击一次 Create Addressables Settings。
- 选中一个你想在运行时加载的资产,勾选其 Inspector 顶部的 Addressable。复选框旁边的文本框就是它的地址。
- 这个地址字符串就是你传给框架的键:
await assets.LoadAsync<GameObject>("characters/player")。
在编辑器中,Play 模式直接从项目里加载可寻址资产,因此你可以自由迭代。在发布玩家构建之前,从 Groups 窗口构建一次 Addressables 内容(Build > New Build > Default Build Script)。装好这个包后,两个服务都会在启动时自动注册——框架侧不需要任何额外设置。
下一章:7. 让框架适配你的项目