Skip to content

引擎服务

大多数引擎功能都是通过引擎服务实现的。引擎服务是一个实现了 IEngineService 接口的模块,用于处理特定的任务,例如执行 Naninovel 脚本、管理演出元素(Actors)或保存与加载游戏状态。

当你希望与引擎系统交互时,通常需要使用引擎服务。你可以通过 Engine.GetService<TService>() 静态方法获取服务引用,其中 TService 是你想获取的服务接口。例如,要获取 IScriptPlayer 服务:

csharp
var player = Engine.GetService<IScriptPlayer>();
player.Stop();

注意

引擎初始化过程是异步的,因此即使启用了自动初始化,在 Unity 加载场景后(例如在 AwakeStartOnEnable MonoBehaviour ↗ 方法中),引擎 API(例如 GetService 方法)也可能尚不可用。请参阅 访问引擎 API 指南了解更多信息。

当前可用的服务如下:

服务接口解释
IBackgroundManager管理 背景 演出元素。
ICharacterManager管理 角色 演出元素。
IChoiceHandlerManager管理 选项 演出元素。
ITextPrinterManager管理 文本展示窗 演出元素。
IAudioManager管理音频: 音效, 背景音乐 以及 语音
IInputManager管理玩家的 输入处理
ILocalizationManager管理 本地化 活动。
ITextManager处理 受管文本 功能。
IMoviePlayer处理 视频 播放。
IScriptManager管理 naninovel 脚本 资源。
IScriptPlayer处理 naninovel 脚本 执行。
ICameraManager管理场景渲染所需的摄像机及其他相关系统。
IResourceProviderManager管理 IResourceProvider 对象。
IStateManager处理与 IEngineService 相关的持久化数据序列化与反序列化;提供用于保存与加载游戏状态的 API。
IUIManager管理 IManagedUI 对象,并负责 UI 自定义 功能。
ICustomVariableManager提供访问并允许修改自定义变量的接口。
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 —— 用于设置数据

如有需要,单个服务可以同时实现多个状态接口。关于不同类型的引擎状态的详细信息,请参阅 状态管理指南

csharp
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 UniTask InitializeService ()
    {
        // 在此处初始化服务。
        // 现在可以安全地使用在构造函数中请求的服务。
        Debug.Log(inputManager.ProcessInput);
        Debug.Log(scriptPlayer.PlayedScript);
        return UniTask.CompletedTask;
    }

    public void ResetService ()
    {
        // 在此处重置服务状态。
    }

    public void DestroyService ()
    {
        // 在此处停止服务并释放所占用的资源。
    }
}

下面是一个自定义引擎服务的实现示例及相关注意事项。

csharp
var customService = Engine.GetService<CustomService>();

示例

另一个关于添加自定义引擎服务的示例,可参考 物品系统示例。该示例展示了如何创建一个自定义引擎服务来管理物品资源和库存 UI 配置。具体实现位于运行时脚本 Scripts/Runtime/Inventory/InventoryManager.cs

覆盖内置服务

所有内置服务在引擎源代码中均通过接口引用,因此可以用自定义实现来替换它们。

添加自定义服务的方法与上文相同,但这次不要仅实现 IEngineService 接口,而是实现某个具体的引擎接口,并在 InitializeAtRuntime 特性中指定要覆盖的类型(注意,是实现类型,而不是接口)。这样,引擎初始化时就会加载你的自定义实现,并替代内置版本。

以下示例展示了一个虚拟的 IInputManager 实现:该实现不会执行任何实际操作,只是在调用其方法时输出日志。

csharp
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, IEnumerable<string> allowedIds = null)
    {
        Debug.Log("CustomInputManager::AddMuter()");
    }

    public void RemoveMuter (object muter)
    {
        Debug.Log("CustomInputManager::RemoveMuter()");
    }

    // 省略...
}

当通过 Engine.GetService<IInputManager>() 获取输入管理器时,引擎将使用你的自定义实现来替代内置的 Naninovel.InputManager