reckfellENGINE

Запуск OpenXR: ошибки и события сеанса

Полный C#-контроллер, фокус, отписка событий и независимая политика паузы.

На этой странице

Этот контроллер обрабатывает отказ запуска и события OpenXR, сохраняя политику игры за вашим кодом. Он не меняет частоту физики, не выбирает частоту дисплея, не включает VRS и не снимает общую паузу при возвращении фокуса.

Сначала выполните настройку XR и подготовьте карту действий. Сохраните код как XrStartup.cs и прикрепите к отдельной Entity XRSession, которая не содержит ветку игрового процесса. Используйте один такой контроллер на Viewport.

Полный скрипт

C#
using Reckfell;

public partial class XrStartup : Entity
{
    [Signal] public delegate void FocusChangedEventHandler(bool focused);
    [Signal] public delegate void SessionStoppedEventHandler();
    [Signal] public delegate void PoseRecenteredEventHandler();

    public string FailureReason { get; private set; } = "";
    public bool HasFocus { get; private set; }
    private OpenXRInterface? xr;
    private Viewport? target;
    private bool previousUseXr;

    public override void _Ready()
    {
        ProcessMode = ProcessModeEnum.Always;
        if (!ProjectSettings.GetSetting("xr/openxr/enabled").AsBool())
        {
            Fail("openxr-disabled"); return;
        }
        if (!ProjectSettings.GetSetting("xr/shaders/enabled").AsBool())
        {
            Fail("xr-shaders-disabled"); return;
        }
        xr = XRServer.FindInterface("OpenXR") as OpenXRInterface;
        if (xr == null) { Fail("openxr-interface-unavailable"); return; }

        xr.SessionVisible += OnNotFocused;
        xr.SessionSynchronized += OnNotFocused;
        xr.SessionFocussed += OnFocused;
        xr.SessionStopping += OnStopped;
        xr.SessionLossPending += OnStopped;
        xr.InstanceExiting += OnStopped;
        xr.PoseRecentered += OnRecentered;
        if (!xr.IsInitialized() && !xr.Initialize())
        {
            Detach(); Fail("openxr-initialization-failed"); return;
        }
        target = GetViewport();
        previousUseXr = target.UseXR;
        target.UseXR = true;
        SetFocus(xr.GetSessionState() == OpenXRInterface.SessionState.Focused);
    }

    private void Fail(string reason)
    {
        FailureReason = reason;
        ReckfellApi.Print("XR startup: " + reason);
    }
    private void SetFocus(bool focused)
    {
        if (HasFocus == focused) return;
        HasFocus = focused;
        EmitSignal(SignalName.FocusChanged, focused);
    }
    private void OnFocused() => SetFocus(true);
    private void OnNotFocused() => SetFocus(false);
    private void OnStopped()
    {
        SetFocus(false);
        EmitSignal(SignalName.SessionStopped);
    }
    private void OnRecentered() => EmitSignal(SignalName.PoseRecentered);
    private void Detach()
    {
        if (xr == null) return;
        xr.SessionVisible -= OnNotFocused;
        xr.SessionSynchronized -= OnNotFocused;
        xr.SessionFocussed -= OnFocused;
        xr.SessionStopping -= OnStopped;
        xr.SessionLossPending -= OnStopped;
        xr.InstanceExiting -= OnStopped;
        xr.PoseRecentered -= OnRecentered;
        xr = null; // Borrowed from XRServer: do not Dispose/Free/Uninitialize it here.
    }
    public override void _ExitTree()
    {
        Detach();
        if (target != null && ReckfellObject.IsInstanceValid(target))
            target.UseXR = previousUseXr;
        target = null; // Borrowed Viewport.
    }
}

Обработка результата

После _Ready() прочитайте FailureReason. Пустая строка означает, что данный контроллер не зафиксировал отказ настройки; готовность ввода определяйте отдельно по HasFocus.

FailureReasonДействие
openxr-disabledВключите xr/openxr/enabled, сохраните проект и перезапустите
xr-shaders-disabledВключите xr/shaders/enabled до запуска
openxr-interface-unavailableПроверьте наличие OpenXR в сборке и правильный запуск приложения
openxr-initialization-failedПроверьте активный runtime, подключение устройства и журнал native-инициализации; проверьте, не запущен ли проект с --xr-mode off

Скрипт возвращается после ошибки и оставляет Viewport в прежнем режиме. Для обычного режима без XR покажите пользователю понятное сообщение либо загрузите соответствующий интерфейс игры. Не продолжайте XR-перемещение по несуществующим позам. Контроллер не реализует автоматическое переподключение потерянного сеанса.

Фокус и пауза — разные состояния

FocusChanged сообщает только об изменении XR-фокуса. Visible и Synchronized сбрасывают HasFocus; SessionFocussed устанавливает его. Написание Focussed у сигнала соответствует текущему C# SDK, а значение enum — SessionState.Focused. Значение состояний OpenXR.

Используйте HasFocus как условие игровых действий. Потеря фокуса может добавить отдельную причину паузы, но возвращение фокуса должно снимать только эту причину. Если игра уже стоит на паузе из-за меню, нельзя безусловно выполнять GetTree().Paused=false.

ProcessMode.Always установлен у контроллера, чтобы он оставался доступен при паузе. Дочерние Entity с Inherit могут унаследовать этот режим: не помещайте Gameplay под этот контроллер. В иерархии из руководства Gameplay выделен в отдельную ветку Pausable, а XR-риг и необходимый интерфейс продолжают работать. При Visible не останавливайте весь XR-вывод вместе с игровыми действиями.

Остановка и центрирование

SessionStopped пересылает SessionStopping, SessionLossPending и InstanceExiting. Обработчик игры должен переносить повторный вызов: несколько событий могут потребовать одного и того же завершения XR. Выберите политику — выход, возврат в обычный режим или предложение перезапуска. Этот пример не вызывает Quit автоматически и не управляет уничтожением native-сеанса.

PoseRecentered передаёт уведомление. Оно не вызывает CenterOnHmd автоматически: дополнительное центрирование зависит от вашего origin, перемещения игрока и выбранного пространства. Не прибавляйте повторно смещение головы к уже отслеживаемой камере.

При выходе из дерева контроллер отписывается от интерфейса и восстанавливает прежний UseXR. Он предполагает единоличное управление этим флагом выбранного Viewport. Интерфейс принадлежит XRServer: скрипт не вызывает для него Dispose, Free или Uninitialize. Изменения общей частоты физики, refresh rate, VRS и качества требуют отдельного измерения и не являются обязательной частью старта.

Что проверено

В установленном движке с --xr-mode off выполнены сборка полного файла, ветки выключенных настроек и отказа инициализации. Локальные обработчики фокуса, остановки и центрирования вызваны искусственно: проверены сигналы, отсутствие повторного FocusChanged и сохранение независимой паузы. Частота физики не изменилась.

Это не проверка реальных событий гарнитуры. Успешная инициализация, подписки при работающем runtime, восстановление UseXR после успешного старта, стереовывод и tracking не проверялись. На устройстве отдельно проверьте старт, потерю/возврат фокуса, остановку, потерю подключения и удаление контроллера из дерева.

Документация Reckfell EngineНаверх ↑
БЫСТРЫЙ ПЕРЕХОДНачало работыEntity и иерархияC# & .NET 10Сцены и префабы
Руководства и полный API Reckfell K
reckfellENGINE
RECKFELL ACCOUNT

Вход в аккаунт

Войдите в аккаунт Reckfell.

Один аккаунт для всей платформы Reckfell.