# Сервисы движка

Большинство функций движка реализуются через его сервисы. Сервис движка – это реализация интерфейса IEngineService, который выполняет определенную работу, например, выполнение сценариев Naninovel, управление акторами или сохранение-загрузка состояния игры.

В случае, если вы хотите взаимодействовать с системой движка, вам, скорее всего, захочется использовать сервис движка. Вы можете получить ссылку на него, используя статический метод Engine.GetService<TService>(), где TService – интерфейс сервиса, который вы хотите получить; например, чтобы получить сервис IScriptPlayer:

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

В настоящее время доступны следующие сервисы:

Интерфейс сервиса Описание
IBackgroundManager Управляет акторами фонов.
ICharacterManager Управляет акторами персонажей.
IChoiceHandlerManager Управляет акторами обработчиков выбора.
ITextPrinterManager Управляет акторами текстовых принтеров.
IAudioManager Управляет аудио: SFX, BGM и озвучкой.
IInputManager Управляет пользовательским вводом.
ILocalizationManager Управляет функциями локализации.
ITextManager Обрабатывает функцию управляемого текста.
IMoviePlayer Обрабатывает воспроизведение видео.
IScriptManager Управляет ресурсами сценариев Naninovel.
IScriptPlayer Управляет воспроизведением сценариев Naninovel.
ICameraManager Управляет камерами и другими системами, необходимыми для визуализации сцены.
IResourceProviderManager Управляет объектами IResourceProvider.
IStateManager Управляет де-/сериализацией постоянной информации, связанной с IEngineService; поставляет API для сохранения и загрузки состояния игры?.
IUIManager Управляет объектами IManagedUIи обрабатывает функцию кастомизации UI.
ICustomVariableManager Предоставляет доступ к пользовательским переменных и позволяет их модифицировать.
ISpawnManager Управляет объектами, созданными с помощью команд @spawn.
IUnlockableManager Управляет разблокировываемыми предметами (предметы галерей иллюстрации и видео, советы и пр.).

Встроенные реализации сервисов можно найти в исходном коде среды выполнения, хранящемся в разделе Naninovel/Runtime.

# Добавление пользовательских сервисов

Чтобы добавить новый пользовательский сервис движка, реализуйте интерфейс IEngineService и добавьте атрибут InitializeAtRuntime к реализуемому классу. Экземпляр реализации будет автоматически создан во время инициализации движка и станет доступен через API Engine.GetService<TService>().

Вы можете принудительно инициализировать свой пользовательский сервис раньше или позже других сервисов, используя аргумент InitializationPriority атрибута InitializeAtRuntime; более низкие значения будут размещать сервис перед другими сервисами в очереди инициализации и наоборот.

Для автоматического создания экземпляра реализация сервиса должна иметь совместимый конструктор (или конструктор по умолчанию). Допускаются следующие аргументы (в любом порядке):

  • Любое количество других сервисов (производные от IEngineService)
  • Любое количество объектов конфигурации (производные от Configuration)
  • Прокси-объект Unity "MonoBehavior" (производныеIEngineBehaviour)

Имейте в виду, что использовать другие сервисы в конструкторе небезопасно. Вместо этого используйте любые действия инициализации, требующие использования других служб в методе InitializeServiceAsync; чтобы убедиться, что необходимые сервисы инициализируются при обращении к ним, перечислите их в конструкторе служб (очередь инициализации топологически сортируется на основе аргументов конструктора).

В случае, если пользовательский сервис имеет постоянное состояние, которое вы хотите де-/сериализировать с другими сервисами движка, реализуйте интерфейс IStatefulService<TState>, используйте TState или GameStateMap, GlobalStateMap или SettingsStateMap в зависимости от того, хотите ли вы сохранить состояние конкретной игровой сессии, глобального состояния или настроек. При необходимости можно реализовать все три интерфейса для одного сервиса. Для получения дополнительной информации о различных типах состояния движка см. руководство управления состояниями.

Ниже приведен пример реализации пользовательского сервиса движка с некоторыми уведомлениями об использовании.

using Naninovel;
using UniRx.Async;
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 InitializeServiceAsync ()
    {
    	// Сервис инициализируется здесь.
        // Теперь можно безопасно использовать сервисы, необходимые конструктору.
        Debug.Log(inputManager.ProcessInput);
        Debug.Log(scriptPlayer.PlayedScript);
        return UniTask.CompletedTask;
    }

    public void ResetService ()
    {
        // Сброс состояния сервиса.
    }

    public void DestroyService ()
    {
        // Остановка сервиса и разгрузка всех используемых ресурсов.
    }
}

Вышеупомянутый сервис вы можете получить его через API движка следующим образом:

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

ПРИМЕР

Другой пример добавления пользовательского сервиса движка для управления ресурсами предметов и конфигурацией UI инвентаря можно найти в примере проекта инвентаря на GitHub.

Данный пользовательский сервис движка реализован с помощью скрипта воспроизведения InventoryManager.cs.

# Переопределение встроенных сервисов

Ссылки на все встроенные сервисы происходят через интерфейсы в исходном коде движка, что позволяет заменить любой из этих сервисов пользовательской реализацией.

Добавьте пользовательский сервис таким же образом, как описано выше, но вместо IEngineService реализуйте конкретный интерфейс движка и укажите переопределенный тип (тип реализации, а не интерфейса) с помощью атрибута InitializeAtRuntime. После этого ваша пользовательская реализация будет инициализирована вместо встроенной.

Ниже приведен пример фиктивной реализации IInputManager, которая ничего не делает, но регистрирует, когда любой из ее методов вызывается.

using Naninovel;
using Naninovel.UI;
using UniRx.Async;
using UnityEngine;

[InitializeAtRuntime(@override: typeof(InputManager))]
public class CustomInputManager : IInputManager
{
    public InputConfiguration Configuration { get; }
    public bool ProcessInput { get; set; }

    public CustomInputManager (InputConfiguration config)
    {
        Configuration = config;
    }

    public UniTask InitializeServiceAsync ()
    {
        Debug.Log("CustomInputManager::InitializeServiceAsync()");
        return UniTask.CompletedTask;
    }

    public void ResetService ()
    {
        Debug.Log("CustomInputManager::ResetService()");
    }

    public void DestroyService ()
    {
        Debug.Log("CustomInputManager::DestroyService()");
    }

    public IInputSampler GetSampler (string bindingName)
    {
        Debug.Log($"CustomInputManager::GetSampler({bindingName})");
        return default;
    }

    public void AddBlockingUI (IManagedUI ui, params string[] allowedSamplers)
    {
        Debug.Log($"CustomInputManager::AddBlockingUI({ui.GetType().Name})");
    }

    public void RemoveBlockingUI (IManagedUI ui)
    {
        Debug.Log($"CustomInputManager::RemoveBlockingUI({ui.GetType().Name})");
    }
}

Теперь, когда менеджер ввода запрашивается через Engine.GetService<IInputManager>(), ваша пользовательская реализация будет предоставлена вместо встроенной Naninovel.InputManager.

Обновлено: 28 октября 2020 г.