引擎服务
大多数引擎功能都是通过引擎服务实现的。引擎服务是 IEngineService 接口的实现,用于处理特定工作,例如执行剧本脚本、管理 Actor 或保存和加载游戏状态。
与引擎系统交互时,通常会用到引擎服务。使用静态方法 Engine.GetService<TService>() 即可获取引擎服务的引用,其中 TService 是目标服务的接口。例如,要获取 IScriptPlayer 服务:
var player = Engine.GetService<IScriptPlayer>();
player.MainTrack.Stop();注意
引擎初始化过程是异步的,因此即使启用了自动初始化,在 Unity 刚加载完场景时(例如,在 Awake、Start 和 OnEnable MonoBehaviour 方法中),引擎 API(例如 GetService 方法)也可能尚不可用;有关更多信息,请参阅 访问引擎 API 指南。
目前提供以下服务:
| 服务接口 | 描述 |
|---|---|
| IBackgroundManager | 管理 背景 Actor。 |
| ICharacterManager | 管理 角色 Actor。 |
| IChoiceHandlerManager | 管理 选项处理程序 Actor。 |
| ITextPrinterManager | 管理 文本打印机 Actor。 |
| IAudioManager | 管理音频:SFX、BGM 和 配音。 |
| IInputManager | 管理 输入处理。 |
| ILocalizationManager | 管理 本地化 相关操作。 |
| ICommunityLocalization | 提供对 社区本地化 资源的访问。 |
| ITextLocalizer | 解析与 LocalizableText 值关联的本地化字符串。 |
| ITextManager | 处理 管理文本 功能。 |
| IMoviePlayer | 处理 影片 播放。 |
| IScriptManager | 管理 剧本脚本 资源。 |
| IScriptLoader | 处理与剧本脚本关联的资源的 加载和卸载。 |
| IScriptPlayer | 处理 剧本脚本 执行。 |
| ICameraManager | 管理场景渲染所需的摄像机和其他系统。 |
| IResourceProviderManager | 管理 IResourceProvider 对象。 |
| IStateManager | 处理 IEngineService 相关的持久数据反序列化/序列化;提供 保存和加载 游戏状态的 API。 |
| IUIManager | 管理 IManagedUI 对象并处理 UI 自定义 功能。 |
| IVariableManager | 提供对 剧本变量 的访问并允许修改它们。 |
| ISpawnManager | 管理使用 @spawn 命令生成的对象。 |
| IUnlockableManager | 管理 可解锁项(CG 和影片画廊项、提示等)。 |
您可以在位于 Naninovel/Runtime 的运行时源代码中找到这些服务的内置实现。
添加自定义服务
要添加新的自定义引擎服务,请实现 IEngineService 接口并将 InitializeAtRuntime 特性添加到实现类。该实现的实例将在引擎初始化期间自动创建,并可通过 Engine.GetService<TService>() API 获取。
您可以使用 InitializeAtRuntime 特性的 InitializationPriority 参数强制您的自定义服务在其他服务之前或之后初始化;较低的值会使其在初始化队列中排在其他服务之前,反之亦然。
要能被自动实例化,服务实现应具有兼容的构造函数(或默认构造函数)。允许以下构造函数参数(任意顺序):
- 任意数量的其他服务(
IEngineService派生) - 任意数量的配置对象(
Configuration派生) - Unity
MonoBehaviour代理对象(IEngineBehaviour派生)
请注意,在构造函数中使用其他服务是不安全的。请改为在 InitializeService 方法中执行所有需要其他服务的初始化操作;为确保访问所需服务时它们已完成初始化,请在服务构造函数中列出它们(初始化队列会根据构造函数参数进行拓扑排序)。
如果您的自定义服务具有希望与其他引擎服务一起反序列化/序列化的持久状态,请实现 IStatefulService<TState> 接口,其中 TState 是 GameStateMap、GlobalStateMap 或 SettingsStateMap,具体取决于您是要将状态与特定于游戏会话的数据、全局数据还是设置数据一起存储。如果需要,允许为单个服务实现所有三个接口。有关不同类型的引擎状态的更多信息,请参阅 状态管理指南。
下面是带有使用说明的自定义引擎服务实现示例。
using Naninovel;
using UnityEngine;
[InitializeAtRuntime]
public class CustomService : IEngineService
{
private readonly InputManager inputManager;
private readonly ScriptPlayer scriptPlayer;
public CustomService (InputManager inputManager, ScriptPlayer scriptPlayer)
{
// 此时服务可能尚未初始化,
// 请避免使用它们。
this.inputManager = inputManager;
this.scriptPlayer = scriptPlayer;
}
public Awaitable InitializeService ()
{
// 在此处初始化服务。
// 现在可以安全地使用构造函数中请求的服务。
Debug.Log(inputManager.Enabled);
Debug.Log(scriptPlayer.MainTrack.PlayedScript);
return Async.Completed;
}
public void ResetService ()
{
// 在此处重置服务状态。
}
public void DestroyService ()
{
// 在此处停止服务并释放所有已使用的资源。
}
}您现在可以通过以下方式访问上述自定义服务:
var customService = Engine.GetService<CustomService>();示例
在 库存示例 中可以找到添加自定义引擎服务以管理物品资源和库存 UI 配置的另一个示例。具体来说,自定义引擎服务通过 Scripts/Runtime/Inventory/InventoryManager.cs 运行时脚本实现。
覆盖内置服务
引擎源代码通过接口引用所有内置服务,因此任意内置服务都可以替换为自定义实现。
以与上述相同的方式添加自定义服务,但不要实现 IEngineService,而是实现具体的引擎接口并通过 InitializeAtRuntime 特性指定被覆盖的类型(实现类型,而不是接口)。这样,引擎将初始化您的自定义实现,而不是内置实现。
下面是一个 IInputManager 空实现的示例,除了在其任何方法被调用时记录日志外,它什么也不做。
using Naninovel;
using UnityEngine;
[InitializeAtRuntime(@override: typeof(InputManager))]
public class CustomInputManager : IInputManager
{
public InputConfiguration Configuration { get; }
public CustomInputManager (InputConfiguration config)
{
Configuration = config;
}
public void AddMuter (object muter, IReadOnlyCollection<string> allowedIds = null)
{
Debug.Log("CustomInputManager::AddMuter()");
}
public void RemoveMuter (object muter)
{
Debug.Log("CustomInputManager::RemoveMuter()");
}
// 等等...
}现在,当通过 Engine.GetService<IInputManager>() 请求输入管理器时,将使用您的自定义实现而不是内置的 Naninovel.InputManager。