跳转到内容

引擎服务 ​

大多数引擎功能都是通过引擎服务实现的。引擎服务是 IEngineService 接口的实现,用于处理特定工作,例如执行剧本脚本、管理 Actor 或保存和加载游戏状态。

与引擎系统交互时,通常会用到引擎服务。使用静态方法 Engine.GetService<TService>() 即可获取引擎服务的引用,其中 TService 是目标服务的接口。例如,要获取 IScriptPlayer 服务:

cs
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,具体取决于您是要将状态与特定于游戏会话的数据、全局数据还是设置数据一起存储。如果需要,允许为单个服务实现所有三个接口。有关不同类型的引擎状态的更多信息,请参阅 状态管理指南。

下面是带有使用说明的自定义引擎服务实现示例。

cs
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 ()
    {
        // 在此处停止服务并释放所有已使用的资源。
    }
}

您现在可以通过以下方式访问上述自定义服务:

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

示例

在 库存示例 中可以找到添加自定义引擎服务以管理物品资源和库存 UI 配置的另一个示例。具体来说,自定义引擎服务通过 Scripts/Runtime/Inventory/InventoryManager.cs 运行时脚本实现。

覆盖内置服务 ​

引擎源代码通过接口引用所有内置服务,因此任意内置服务都可以替换为自定义实现。

以与上述相同的方式添加自定义服务,但不要实现 IEngineService,而是实现具体的引擎接口并通过 InitializeAtRuntime 特性指定被覆盖的类型(实现类型,而不是接口)。这样,引擎将初始化您的自定义实现,而不是内置实现。

下面是一个 IInputManager 空实现的示例,除了在其任何方法被调用时记录日志外,它什么也不做。

cs
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。