在 Godot 中使用空间容器

适用的 XR 设备
本指南可帮助您为以下类型的 XR 设备打造优质体验。
扩展现实头戴设备
有线扩展现实眼镜

空间容器可让您的应用在全沉浸空间模式下,在系统管理的 3D 体积边界框内渲染 3D OpenXR 内容,或在运行时过渡到全屏无边界体验。

请按照本指南在项目中配置和使用空间容器。

在 Android XR 上以全沉浸空间模式运行的多个空间容器。
图 1. 全沉浸空间模式下的空间容器,包括一个锁定在地板上的 3D 地形容器和一个与 2D 界面配对的 3D 模型。

空间容器的工作原理

在项目设置中启用空间容器后:

  • 整个活跃 3D 场景都放置在以场景原点 (0, 0, 0) 为中心的单个主要空间容器中。您无需标记或分配单个 Node3D 节点即可将其放置在容器中。
  • 您的应用会使用项目设置中配置的边界模式(Bounded 或 Immersive)和初始边界尺寸,启动进入受管全沉浸空间模式。在有界模式下,系统会自动剪裁场景中超出边界框尺寸的所有 3D 几何图形。
  • 默认情况下,Android XR 系统提供窗口控件,以便用户在 3D 空间中抓取、移动和重新定位有界容器,或将其最小化和关闭。在运行时,您的应用可以调整容器大小,在有界模式 (OpenXRSpatialContainerState.BOUNDED) 和全屏沉浸模式 (OpenXRSpatialContainerState.IMMERSIVE) 之间切换,并使用 OpenXRSpatialContainerExtension 单例对边界和状态变化做出响应。

前提条件

在开始之前,请确保您的开发环境和目标设备满足以下要求:

  • Godot 版本:Godot 4.8-dev 或更高版本。
  • 项目设置:完成 Godot 引擎项目设置指南中的所有步骤。
  • Godot OpenXR Vendors 插件:安装 5.2 或更高版本的 Godot OpenXR Vendors 插件,该插件支持 Android XR 专用扩展。
  • 目标设备:支持空间 API 级别 4 或更高级别的 Android XR 设备。在运行时,调用 OpenXRSpatialContainerExtension.is_enabled() 以验证设备上是否提供了该扩展。

在项目设置中启用空间容器

配置项目设置,以启用 OpenXR、空间容器、自动运行时权限和 Alpha 混合透视:

  1. 依次打开 Project > 项目设置。
  2. 在项目设置窗口的右上角,将高级设置切换开关切换到启用状态。
  3. 依次前往常规 > XR > 着色器,然后选择已启用。
  4. 依次前往 General > XR > OpenXR,然后配置以下设置:
    • 选择已启用,为项目开启 OpenXR。
    • 将环境混合模式设置为 Alpha 混合。
  5. 同样在 OpenXR 下,前往扩展程序部分并配置以下设置:
    • 选择自动请求运行时权限,以便应用在启动时自动提示用户授予 Android 清单中声明的所有 XR 权限。
    • 在空间容器子部分下,选择已启用。
    • 将边界模式设置为有边界,以便在 3D 边界框中启动;或设置为沉浸式,以便在全屏沉浸模式下启动。
    • 将边界设置为初始宽度、高度和深度尺寸(以米为单位),例如 Vector3(1.0, 1.0, 1.0)。
  6. 依次前往常规 > 渲染 > 视口,然后选择透明背景。

配置 Android 导出、权限和全沉浸空间模式

接下来,配置 Android 导出预设、权限和 activity 启动模式,以便应用在启用空间容器的情况下以全沉浸空间模式运行。

配置 Android 导出预设

  1. 依次前往 Project > Export…(项目 > 导出…),然后选择 Android 导出预设。
  2. 在 Gradle Build 下,选择 Use Gradle Build,并将 Min SDK 设置为 34 或更高版本。

    启用

  3. 在 XR Features 下,将 XR Mode 设置为 OpenXR,然后选择 Enable AndroidXR Plugin。

    在

  4. 在 Android XR 功能下,选择使用实验性功能。

添加场景理解权限以锁定地面

创建和渲染基本空间容器不需要危险的运行时权限(启用使用实验性功能后,android.permission.HEAD_TRACKING 会自动添加到清单中)。不过,如果您的应用使用场景理解功能(例如将空间容器锁定并锚定到物理地板),则必须在运行时声明并请求 android.permission.SCENE_UNDERSTANDING_COARSE 权限。

如需详细了解 Android XR 权限,请参阅了解 XR 权限。 如果您的应用使用地面锁定或场景理解功能,请声明并请求 android.permission.SCENE_UNDERSTANDING_COARSE:

  1. 在 Project > Export… > Android,前往权限 > 自定义权限,然后添加 android.permission.SCENE_UNDERSTANDING_COARSE。

  2. 如果您在 Project Settings > General > XR > OpenXR > Extensions 中选择 Automatically Request Runtime Permissions,您的应用会在 OpenXR 启动时自动请求权限。如果您需要在 GDScript 中手动请求权限,请调用 OS.request_permission():

    if not OS.get_granted_permissions().has("android.permission.SCENE_UNDERSTANDING_COARSE"):
        OS.request_permission("android.permission.SCENE_UNDERSTANDING_COARSE")
    

配置全沉浸空间模式

Android XR 上的空间容器仅支持全沉浸空间 (XR_ACTIVITY_START_MODE_FULL_SPACE_MANAGED)。请确保您的 Android activity 在自定义 build 模板(android/build/AndroidManifest.xml,使用 Project > Install Android Build Template… 安装)中声明 XR_ACTIVITY_START_MODE_FULL_SPACE_MANAGED:

<manifest ...>
    <application ...>
        <property
            android:name="android.window.PROPERTY_XR_ACTIVITY_START_MODE"
            android:value="XR_ACTIVITY_START_MODE_FULL_SPACE_MANAGED"
            tools:replace="android:value" />
        ...
    </application>
</manifest>

在 GDScript 中使用空间容器

OpenXRSpatialContainerExtension 单例和 OpenXRSpatialContainerState 类向 GDScript 公开了 XR_EXT_spatial_container 和 XR_EXT_spatial_container_self_rendering 扩展。在运行时,使用 OpenXRSpatialContainerExtension 检查主空间容器的状态和边界,监听容器信号,并请求在 BOUNDED 模式(3D 边界框)和 IMMERSIVE 模式(全屏无边界视图)之间切换。

连接到空间容器信号

检查 OpenXRSpatialContainerExtension.is_enabled(),验证当前设备是否支持并启用了空间容器扩展,并在 _ready() 中连接到容器边界和状态信号。

OpenXRSpatialContainerExtension 会针对空间容器发出以下状态信号:

  • spatial_container_visibility_changed:在容器显示 (true) 或隐藏 (false) 时,与 (spatial_container, is_visible) 一起发出。当 is_visible 为 false 时,暂停动画、音频或模拟更新。
  • spatial_container_interactability_changed:当容器获得 (true) 或失去 (false) 用户输入焦点时,与 (spatial_container, is_interactive) 一起发出。在焦点发生变化时启用或停用互动式突出显示和输入处理。
  • spatial_container_bounds_mode_changed:当容器在 OpenXRSpatialContainerState.BOUNDED 和 OpenXRSpatialContainerState.IMMERSIVE 之间切换时,与 (spatial_container, bounds_mode) 一起发出。切换环境视觉效果(例如天空盒或透传)和特定于模式的界面控件。
func _ready() -> void:
    if OpenXRSpatialContainerExtension.is_enabled():
        OpenXRSpatialContainerExtension.spatial_container_bounds_changed.connect(
            _on_bounds_changed
        )
        OpenXRSpatialContainerExtension.spatial_container_bounds_mode_changed.connect(
            _on_bounds_mode_changed
        )
        OpenXRSpatialContainerExtension.spatial_container_visibility_changed.connect(
            _on_visibility_changed
        )
        OpenXRSpatialContainerExtension.spatial_container_interactability_changed.connect(
            _on_interactability_changed
        )

func _on_visibility_changed(_rid: RID, is_visible: bool) -> void:
    # Pause or resume simulation and audio based on visibility
    pass

func _on_interactability_changed(_rid: RID, is_interactive: bool) -> void:
    # Enable or disable input handling and focus highlights
    pass

将 3D 内容缩放到容器边界

在 BOUNDED 模式下,按 bounds 的最小尺寸统一缩放 3D 内容,使其保持宽高比,并始终适合容器的最小边。过渡到 IMMERSIVE 模式时,将场景恢复为 1:1 比例:

func _on_bounds_changed(_rid: RID, bounds: Vector3) -> void:
    var uniform_scale: float = min(bounds.x, bounds.y, bounds.z)
    content_root.scale = Vector3.ONE * uniform_scale

func _on_bounds_mode_changed(
    _rid: RID, bounds_mode: OpenXRSpatialContainerState.BoundsMode
) -> void:
    if bounds_mode == OpenXRSpatialContainerState.IMMERSIVE:
        content_root.scale = Vector3.ONE

在有界模式和沉浸模式之间切换

使用 OpenXRSpatialContainerState.BOUNDED 或 OpenXRSpatialContainerState.IMMERSIVE 调用 request_main_spatial_container_bounds_mode,以便在有界 3D 体积和全屏沉浸式视图之间切换:

# Request transitioning to immersive mode (or pass BOUNDED to return)
OpenXRSpatialContainerExtension.request_main_spatial_container_bounds_mode(
    OpenXRSpatialContainerState.IMMERSIVE
)

另请参阅


OpenXR™ 和 OpenXR 徽标是 The Khronos Group Inc. 拥有的商标,并在中国、欧盟、日本和英国注册为商标。