空间容器可让您的应用在全沉浸空间模式下,在系统管理的 3D 体积边界框内渲染 3D OpenXR 内容,或在运行时过渡到全屏无边界体验。
请按照本指南在项目中配置和使用空间容器。
空间容器的工作原理
在项目设置中启用空间容器后:
- 整个活跃 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 混合透视:
- 依次打开 Project > 项目设置。
- 在项目设置窗口的右上角,将高级设置切换开关切换到启用状态。
- 依次前往常规 > XR > 着色器,然后选择已启用。
- 依次前往 General > XR > OpenXR,然后配置以下设置:
- 选择已启用,为项目开启 OpenXR。
- 将环境混合模式设置为 Alpha 混合。
- 同样在 OpenXR 下,前往扩展程序部分并配置以下设置:
- 选择自动请求运行时权限,以便应用在启动时自动提示用户授予 Android 清单中声明的所有 XR 权限。
- 在空间容器子部分下,选择已启用。
- 将边界模式设置为有边界,以便在 3D 边界框中启动;或设置为沉浸式,以便在全屏沉浸模式下启动。
- 将边界设置为初始宽度、高度和深度尺寸(以米为单位),例如
Vector3(1.0, 1.0, 1.0)。
- 依次前往常规 > 渲染 > 视口,然后选择透明背景。
配置 Android 导出、权限和全沉浸空间模式
接下来,配置 Android 导出预设、权限和 activity 启动模式,以便应用在启用空间容器的情况下以全沉浸空间模式运行。
配置 Android 导出预设
- 依次前往 Project > Export…(项目 > 导出…),然后选择 Android 导出预设。
在 Gradle Build 下,选择 Use Gradle Build,并将 Min SDK 设置为
34或更高版本。
在 XR Features 下,将 XR Mode 设置为 OpenXR,然后选择 Enable AndroidXR Plugin。
在 Android XR 功能下,选择使用实验性功能。
添加场景理解权限以锁定地面
创建和渲染基本空间容器不需要危险的运行时权限(启用使用实验性功能后,android.permission.HEAD_TRACKING 会自动添加到清单中)。不过,如果您的应用使用场景理解功能(例如将空间容器锁定并锚定到物理地板),则必须在运行时声明并请求 android.permission.SCENE_UNDERSTANDING_COARSE 权限。
如需详细了解 Android XR 权限,请参阅了解 XR 权限。
如果您的应用使用地面锁定或场景理解功能,请声明并请求 android.permission.SCENE_UNDERSTANDING_COARSE:
在 Project > Export… > Android,前往权限 > 自定义权限,然后添加
android.permission.SCENE_UNDERSTANDING_COARSE。如果您在 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. 拥有的商标,并在中国、欧盟、日本和英国注册为商标。