Кращий сценарій запуску XR

У Налаштування XR ми представили сценарій запуску, який ініціалізує наші налаштування, які ми використовували як наш сценарій на нашому головному вузлі. Цей сценарій виконує мінімальну кількість кроків, необхідних для будь-якого заданого інтерфейсу.

Під час використання OpenXR ми маємо зробити ряд покращень. Для цього ми створили більш складний стартовий сценарій. Ви знайдете їх у наших демонстраційних проектах.

Крім того, якщо ви використовуєте інструменти XR (див. Представляємо інструменти XR), він містить версію цього сценарію, оновлену деякими функціями, пов’язаними з інструментами XR.

Нижче ми докладно розглянемо сценарій, який використовується в наших демонстраціях, і пояснимо частини, які додаються.

Сигнали для нашого сценарію

Ми вводимо 3 сигнали до нашого сценарію, щоб наша гра могла додати додаткову логіку:

  • focus_lost видається, коли гравець знімає гарнітуру або коли гравець входить до системи меню гарнітури.

  • focus_gained видається, коли гравець знову одягає гарнітуру або виходить із системи меню та повертається до гри.

  • pose_recentered видається, коли гарнітура запитує скидання позиції гравців.

Наша гра має відповідним чином реагувати на ці сигнали.

extends Node3D

signal focus_lost
signal focus_gained
signal pose_recentered

...

Змінні для нашого сценарію

Ми також вводимо кілька нових змінних у наш сценарій:

  • maximum_refresh_rate керуватиме частотою оновлення гарнітури, якщо це підтримується гарнітурою.

  • xr_interface містить посилання на наш інтерфейс XR, він уже існував, але тепер ми вводимо його, щоб отримати повний доступ до нашого XRInterface API.

  • xr_is_focussed буде встановлюватися на true щоразу, коли наша гра має фокус.

...

@export var maximum_refresh_rate : int = 90

var xr_interface : OpenXRInterface
var xr_is_focussed = false

...

Наша оновлена готова функція

Ми додаємо кілька речей до готової функції.

Якщо ми використовуємо мобільний рендерер або рендерер forward+, ми встановлюємо вікна перегляду vrs_mode на VRS_XR. На платформах, які підтримують це, це увімкне фовеативне рендеринг.

Якщо ми використовуємо рендерер сумісності, ми перевіряємо, чи налаштовано параметри візуалізації OpenXR foveated, і якщо ні, ми виводимо попередження. Перегляньте OpenXR Settings для отримання додаткової інформації.

Ми підключаємо декілька сигналів, які випромінює XRInterface. Ми надамо більше деталей про ці сигнали, коли ми їх запровадимо.

Ми також закриваємо нашу програму, якщо не можемо успішно ініціалізувати OpenXR. Тепер це може бути вибір. Якщо ви створюєте гру в змішаному режимі, ви встановлюєте режим віртуальної реальності вашої гри в разі успіху та встановлюєте режим без віртуальної реальності вашої гри в разі невдачі. Однак під час запуску лише програми VR на автономній гарнітурі зручніше вийти у разі помилки, ніж зависати систему.

...

# Called when the node enters the scene tree for the first time.
func _ready():
    xr_interface = XRServer.find_interface("OpenXR")
    if xr_interface and xr_interface.is_initialized():
        print("OpenXR instantiated successfully.")
        var vp : Viewport = get_viewport()

        # Enable XR on our viewport
        vp.use_xr = true

        # Make sure v-sync is off, v-sync is handled by OpenXR
        DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED)

        # Enable VRS
        if RenderingServer.get_rendering_device():
            vp.vrs_mode = Viewport.VRS_XR
        elif int(ProjectSettings.get_setting("xr/openxr/foveation_level")) == 0:
            push_warning("OpenXR: Recommend setting Foveation level to High in Project Settings")

        # Connect the OpenXR events
        xr_interface.session_begun.connect(_on_openxr_session_begun)
        xr_interface.session_visible.connect(_on_openxr_visible_state)
        xr_interface.session_focussed.connect(_on_openxr_focused_state)
        xr_interface.session_stopping.connect(_on_openxr_stopping)
        xr_interface.pose_recentered.connect(_on_openxr_pose_recentered)
    else:
        # We couldn't start OpenXR.
        print("OpenXR not instantiated!")
        get_tree().quit()

...

На початку сесії

Цей сигнал випромінює OpenXR під час налаштування нашого сеансу. Це означає, що гарнітура пройшла всі налаштування та готова почати отримувати від нас вміст. Тільки в цей час різноманітна інформація доступна належним чином.

Головне, що ми робимо тут, це перевірити частоту оновлення наших гарнітур. Ми також перевіряємо доступні частоти оновлення, які повідомляє середовище виконання XR, щоб визначити, чи хочемо ми налаштувати гарнітуру на вищу частоту оновлення.

Нарешті ми порівнюємо швидкість оновлення фізики з частотою оновлення гарнітури. Godot за замовчуванням працює зі швидкістю оновлення фізики 60 оновлень на секунду, тоді як гарнітури працюють із мінімумом 72, а для сучасних гарнітур часто до 144 кадрів на секунду. Невідповідність швидкості оновлення фізики спричинить заїкання, оскільки кадри візуалізуються без рухомих об’єктів.

...

# Handle OpenXR session ready
func _on_openxr_session_begun() -> void:
    # Get the reported refresh rate
    var current_refresh_rate = xr_interface.get_display_refresh_rate()
    if current_refresh_rate > 0:
        print("OpenXR: Refresh rate reported as ", str(current_refresh_rate))
    else:
        print("OpenXR: No refresh rate given by XR runtime")

    # See if we have a better refresh rate available
    var new_rate = current_refresh_rate
    var available_rates : Array = xr_interface.get_available_display_refresh_rates()
    if available_rates.size() == 0:
        print("OpenXR: Target does not support refresh rate extension")
    elif available_rates.size() == 1:
        # Only one available, so use it
        new_rate = available_rates[0]
    else:
        for rate in available_rates:
            if rate > new_rate and rate <= maximum_refresh_rate:
                new_rate = rate

    # Did we find a better rate?
    if current_refresh_rate != new_rate:
        print("OpenXR: Setting refresh rate to ", str(new_rate))
        xr_interface.set_display_refresh_rate(new_rate)
        current_refresh_rate = new_rate

    # Now match our physics rate
    Engine.physics_ticks_per_second = current_refresh_rate

...

На видимому стані

Цей сигнал випромінює OpenXR, коли наша гра стає видимою, але не фокусується. Це дещо дивний опис у OpenXR, але в основному це означає, що наша гра щойно почалася, і ми збираємося перейти у фокусований стан, що користувач відкрив системне меню або користувач щойно зняв гарнітуру .

Отримавши цей сигнал, ми оновимо наш сфокусований стан, ми змінимо режим процесу нашого вузла на вимкнений, що призупинить обробку на цьому вузлі та його дочірніх елементах і видасть наш сигнал focus_lost.

Якщо ви додали цей сценарій до свого кореневого вузла, це означає, що ваша гра буде автоматично призупинятися, коли потрібно. Якщо ви цього не зробили, ви можете підключити до сигналу метод, який виконує додаткові зміни.

Примітка

Поки ваша гра перебуває у видимому стані, оскільки користувач відкрив системне меню, Godot продовжуватиме відображати кадри, а відстеження голови залишатиметься активним, тому ваша гра залишатиметься видимою у фоновому режимі. Однак контролер і відстеження рук будуть вимкнені, доки користувач не вийде із системного меню.

...

# Handle OpenXR visible state
func _on_openxr_visible_state() -> void:
    # We always pass this state at startup,
    # but the second time we get this it means our player took off their headset
    if xr_is_focussed:
        print("OpenXR lost focus")

        xr_is_focussed = false

        # pause our game
        get_tree().paused = true

        emit_signal("focus_lost")

...

У зосередженому стані

Цей сигнал випромінює OpenXR, коли наша гра отримує фокус. Це робиться після завершення нашого запуску, але також може видаватись, коли користувач виходить із системного меню або знову вдягає гарнітуру.

Зауважте також, що коли ваша гра починається, коли користувач не носить гарнітуру, гра залишається у «видимому» стані, доки користувач не одягне гарнітуру.

Попередження

Тому важливо тримати вашу гру на паузі під час видимого режиму. Якщо ви цього не зробите, гра працюватиме, поки ваш користувач не взаємодітиме з вашою грою. Крім того, коли гра повертається до зосередженого режиму, раптово всі контролери та відстеження рук знову вмикаються, і це може призвести до поломки гри, якщо ви не відреагуєте на це належним чином. Обов’язково перевірте цю поведінку у своїй грі!

Під час обробки нашого сигналу ми будемо оновлювати стан фокусів, відключати паузу вашого носа та видавати наш сигнал фокус отримано.

...

# Handle OpenXR focused state
func _on_openxr_focused_state() -> void:
    print("OpenXR gained focus")
    xr_is_focussed = true

    # unpause our game
    get_tree().paused = false

    emit_signal("focus_gained")

...

У стані зупинки

Цей сигнал випромінює OpenXR, коли ми входимо в стан зупинки. Існують певні відмінності між платформами, коли це відбувається. На деяких платформах це видається лише під час закриття гри. Але на інших платформах це також видаватиметься кожного разу, коли гравець зніматиме гарнітуру.

Наразі цей метод є лише заповнювачем.

...

# Handle OpenXR stopping state
func _on_openxr_stopping() -> void:
    # Our session is being stopped.
    print("OpenXR is stopping")

...

У позі відцентровано

Цей сигнал випромінює OpenXR, коли користувач запитує повторне центрування свого перегляду. По суті, це повідомляє вашій грі, що користувач зараз дивиться вперед, і ви повинні переорієнтувати гравця, щоб він дивився вперед у віртуальному світі.

Оскільки це залежить від вашої гри, ваша гра повинна відповідним чином реагувати.

Все, що ми робимо тут, це випромінюємо сигнал pose_recentered. Ви можете підключитися до цього сигналу та впровадити фактичний код рецентратора. Часто достатньо викликати center_on_hmd().

...

# Handle OpenXR pose recentered signal
func _on_openxr_pose_recentered() -> void:
    # User recentered view, we have to react to this by recentering the view.
    # This is game implementation dependent.
    emit_signal("pose_recentered")

На цьому наш сценарій закінчився. Він був написаний для того, щоб його можна було повторно використовувати в кількох проектах. Просто додайте його як сценарій на вашому головному вузлі (і розширте його, якщо потрібно) або додайте його на дочірньому вузлі, спеціальному для цього сценарію.