向现有的 Godot XR 项目添加 Android XR 支持

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

如果您在 Godot 中有现有的 XR 项目,则无需启动新的单独项目即可添加对 Android XR 的支持。有些步骤是所有项目都必需的,而有些步骤则是可选的,具体取决于项目使用的 XR 功能。在整个步骤中,我们提供了多个已添加对 Android XR 支持的开源 Godot XR 项目的链接,以及展示了启用某些功能所需的更改的相关 pull 请求。

所有项目的必需步骤

无论您的项目支持哪种类型的 XR 功能,都请完成以下主题中的步骤。之后,请查看可选步骤 列表中的功能,以确定您的项目是否需要进行额外的工作。

更新 Godot 和 Godot OpenXR Vendors 插件

请按照以下步骤将您的项目更新到最新的必需版本,并为 Android XR 配置项目的设置:

  1. 将 Godot 版本更新到 4.6.2 或更高版本。如果您需要有关 项目的其他帮助,请参阅有关 迁移到新版本的文档。
  2. Asset StoreAsset Librarythe repository on GitHub 下载 Godot OpenXR Vendors Plugin 插件 5.1 或更高版本。

  3. 为 Android XR 配置项目的设置:

    1. 为 Android XR 添加导出预设。
    2. 启用 Use Gradle Build

    启用

    1. XR Features 部分中,为 XR Mode 选择 OpenXR, 然后选择 Enable AndroidXR Plugin

    配置

添加对手部跟踪的支持

虽然控制器可能可用,但 Android XR 头戴设备和 XR 眼镜的主要输入方法是手部跟踪。如果可能,您应向 Godot 项目添加手部跟踪支持。

添加对手部跟踪的支持:配置项目设置

首先,请按照以下步骤设置项目设置,以启用手部跟踪及其相关的 OpenXR 扩展程序。

  1. 打开项目的设置,然后依次前往 General > XR > OpenXR
  2. Extensions 部分中,选择 Hand TrackingHand Interaction Profile

    配置

  3. Extensions 部分中找到 Meta 子部分,然后选择 Hand Tracking MeshHand Tracking Aim

    配置

添加对手部跟踪的支持:添加和配置控制器节点

您无需为手部跟踪动态修改现有的 XRController3D 节点,而是添加控制器节点来跟踪和显示手部模型,以及处理来自 Hand Tracking Aim 扩展程序的输入:

  1. XROrigin3D 节点添加三个额外的 XRController3D 节点。

    1. 将一个命名为“HandTrackingLeft”,并将跟踪器属性设置为 /user/hand_tracker/left
    2. 将另一个命名为“HandTrackingRight”,并将跟踪器属性设置为 /user/hand_tracker/right
    3. 将最后一个命名为“HandTrackingAimLeft”,并将跟踪器属性设置为 /user/fbhandaim/left

    如果项目的原始 XRController3D 节点命名为“XRController3D_left”和“XRController3D_right”,则场景将如下所示:

    添加控制器节点后,场景可能如下所示。

  2. 将信号 tracking_changed on HandTrackingLeftHandTrackingRight 连接到更新相应控制器追踪器(在前面的 示例中为 XRController3D_leftXRController3D_right )可见性的各个函数。

    例如,连接到 HandTrackingLeft 上信号的函数可能如下所示:

    func _on_hand_tracking_left_hand_tracking_changed(tracking):
        $XROrigin3D/XRController3D_left.visible = not tracking
    
  3. 在手部跟踪控制器节点上启用 Show When Tracked 属性。

    现在,您的项目可以根据用户使用的是手部跟踪还是控制器,在控制器模型和手部跟踪模型之间进行视觉切换。

  4. 将一些 OpenXRFbHandTrackingMesh 节点作为子节点添加到手部跟踪控制器节点。

  5. XRHandModifier3D 节点作为子节点添加到这些 OpenXRFbHandTrackingMesh 节点,确保设置正确的 Hand Tracker 属性,以便将实时手部跟踪数据应用于模型。

    将子节点添加到控制器节点后,场景可能如下所示。

添加对手部跟踪的支持:在 OpenXR Action Map 上设置 Hand Interaction 配置文件

接下来,您将在 OpenXR Action Map 上设置 Hand Interaction 配置文件:

  1. 打开编辑器底部的 OpenXR Action Map 菜单。
  2. 删除 Simple Controller 配置文件,以避免与 Galaxy XR 控制器出现兼容性问题。
  3. 点击 Add Profile ,选择 Hand Interaction ,然后点击 OK
  4. 您可以随意将此配置文件映射到一个或多个操作集。

根据应用的要求,您可能还需要调整应用处理手部跟踪用户输入的方式

添加对手部跟踪的支持:为 Android XR 设置菜单手势

最后,您可以为 Android XR 实现菜单手势。当玩家的左手处于执行菜单手势的正确位置时,系统会显示一个图标,并在用户执行手势时显示或隐藏菜单。您将 使用之前添加的 HandTrackingAimLeft 节点来处理此问题。

  1. 向左手部跟踪节点添加一个广告牌四边形,以显示您选择的图标(请参阅您之前添加的控制器节点中的 MenuIcon 节点,如下图所示)。

    将子节点添加到控制器节点后,场景可能如下所示。

  2. HandTrackingAimLeft 上的 button_pressedbutton_released 信号连接到如下所示的函数:

    @onready var menu_icon: MeshInstance3D = $XROrigin3D/HandTrackingLeft/MenuIcon
    
    func _on_hand_tracking_aim_left_button_pressed(p_name):
      if p_name == "menu_pressed":
    toggle_menu()
      elif p_name == "menu_gesture":
        if OS.has_feature("androidxr"):
          menu_icon.visible = true
    
    func _on_hand_tracking_aim_left_button_released(p_name):
      if p_name == "menu_gesture":
        menu_icon.visible = false
    

某些功能的可选步骤

完成项目的必需步骤后,您可以根据应用的要求和功能,决定是否需要为某些功能执行额外的工作。如需详细了解这些可选功能中的每一个,请参阅以下部分。

将捏合注册为按钮按压

在 Android XR 中,捏合用于许多基本的系统操作,例如 选择项、滚动、移动或调整窗口大小,以及在 2D 和 3D 空间中移动界面 元素或对象。为了与这些模式保持一致并提升用户体验,您的应用在使用手部跟踪时,应将捏合注册为与控制器上的按钮按压类似。

如需以这种方式配置应用,请使用您创建的 Hand Interaction 配置文件提供的浮点值来创建虚拟操作:

const PRESSED_THRESHOLD := 0.8
const RELEASED_THRESHOLD := 0.6

@onready var left_controller: XRController3D = $XROrigin/XRController3D_left

func _on_xr_controller_3d_left_input_float_changed(p_name: String, value: float):
    if p_name == "pinch":
        var xr_tracker = XRServer.get_tracker(left_controller.tracker)
        if _left_hand_pinching:
            if value < RELEASED_THRESHOLD:
                _left_hand_pinching = false
                xr_tracker.set_input("pinch_pressed", false)
        else:
            if value > PRESSED_THRESHOLD:
                _left_hand_pinching = true
                xr_tracker.set_input("pinch_pressed", true)

代码要点

  • 检查 float 值是否大于或小于特定阈值 在 XRController3D input_float_changed 信号中。
  • 创建一个名为 pinch_pressed 的虚拟操作。

将 XR Tools 函数与手部跟踪结合使用

许多 Godot XR 项目都使用 Godot XR Tools,包括一些 本页中链接的开源项目。为了使某些 XR Tools 函数正常运行(例如用于菜单互动的 FunctionPointer),您需要一些额外的代码,以便在用户切换到手部跟踪时交换其正在寻找的操作。

例如,当使用 FunctionPointer 进行菜单互动时,请根据手部跟踪的 XRController3D 节点的 tracking_changed 信号(在前面的 hand tracking setup steps 中,这些 节点为 HandTrackingLeftHandTrackingRight ),将 active_button_action 属性更新为手部跟踪操作。

const TRIGGER_POINTER_ACTION = "trigger_click"
const PINCH_POINTER_ACTION = "pinch_pressed"

@onready var func_point_left: XRToolsFunctionPointer = %FunctionPointerLeft

func _on_hand_tracking_left_tracking_changed(tracking: bool) -> void:
    if tracking:
        func_point_left.active_button_action = PINCH_POINTER_ACTION
else:
func_point_left.active_button_action = TRIGGER_POINTER_ACTION

代码要点

将手部跟踪与人工移动结合使用

如果您的项目使用人工移动,则仍可以支持手部跟踪。例如,您可以构建一个移动系统,让玩家通过手势绘制路径来穿越,也可以让玩家上下挥动手来加速,并使用其他手势进行跳跃、攀爬和滑行。

万物博物馆使用控制器上的 拇指摇杆进行人工移动。手部跟踪移动是通过添加“虚拟拇指摇杆”来实现的,玩家可以通过在空中捏合来触发这些摇杆,并朝着他们希望拇指摇杆移动的方向移动手。

以下是实现此支持的 拉取请求 的一些关键要点

  • 检测到捏合时,系统会实例化 XRVirtualThumbstick 场景。
  • 在捏合期间,与原始捏合位置的相对距离和方向会转换为 Vector2,并以虚拟方式映射到正常的拇指摇杆输入。
  • 系统还会以两个广告牌四边形网格的形式向玩家提供此输入的视觉反馈,以说明拇指摇杆的位置。

您可以尝试类似的方法,让现有的拇指摇杆驱动的移动代码在进行最少更改的情况下正常运行。不过,您的项目可能仍然需要针对手部跟踪的自定义移动解决方案。

添加透视支持

您可以向应用添加透视支持,以便用户可以看到现实世界中的周围环境。

如需为应用执行此操作,请进行以下代码更改:

  • 将 OpenXR XRInterfaceenvironment_blend_mode 设置为 XR_ENV_BLEND_MODE_ALPHA_BLEND
  • WorldEnvironment 节点的 background_mode 设置为 BG_COLOR
  • WorldEnvironment 节点的 background_color 设置为任何完全透明的颜色。
  • Viewport transparent_bg 属性设置为 true

使用光照估算扩展程序

启用透视时,请考虑使用 Android XR Light Estimation OpenXR 扩展程序。此扩展程序会调整 WorldEnvironmentDirectionalLight3D 的属性,以更好地模拟用户现实世界环境的光照,从而使虚拟对象更好地融入现实世界的光照条件。您可以在项目的设置中启用此扩展程序。

  1. 打开项目的设置,然后依次前往 General > XR > OpenXR
  2. Androidxr 部分中,选择 Light Estimation

    配置

  3. 向场景树添加 OpenXRAndroidLightEstimation 节点,并将其连接到场景的 WorldEnvironmentDirectionalLight3D

    的选项

示例:启用或停用透视和光照估算

以下代码可启用或停用透视和光照估算:

@onready var world_environment = $WorldEnvironment
@onready var directional_light = $DirectionalLight3D
@onready var directional_light_orig_transform: Transform3D = directional_light.transform

func set_passthrough_enabled(p_enabled: bool) -> void:
    var xr_interface = XRServer.find_interface("OpenXR")
    if xr_interface == null:
        return

    var supported_blend_modes = xr_interface.get_supported_environment_blend_modes()
    if not supported_blend_modes.has(XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND):
        return

    # Passthrough
    if p_enabled:
        xr_interface.set_play_area_mode(XRInterface.XR_PLAY_AREA_STAGE)
        xr_interface.environment_blend_mode = XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND
        world_environment.environment.background_mode = Environment.BG_COLOR
        world_environment.environment.background_color = Color(0.0, 0.0, 0.0, 0.0)
        get_viewport().transparent_bg = true
    else:
        xr_interface.set_play_area_mode(XRInterface.XR_PLAY_AREA_ROOMSCALE)
        xr_interface.environment_blend_mode = XRInterface.XR_ENV_BLEND_MODE_OPAQUE
        world_environment.environment.background_mode = Environment.BG_SKY
        get_viewport().transparent_bg = false

    # Light Estimation
    if OS.has_feature("androidxr"):
        var light_estimation = Engine.get_singleton("OpenXRAndroidLightEstimationExtension")
        if p_enabled and light_estimation.is_light_estimation_supported():
            light_estimation.start_light_estimation()
        elif light_estimation.is_light_estimation_started():
            light_estimation.stop_light_estimation()
            directional_light.transform = directional_light_orig_transform
代码要点
  • 停用光照估算时,必须手动恢复 DirectionalLight3D 的原始方向。
  • 如需查看使用透视和光照估算的项目完整示例, 请查看 Expedition to Blobotopia 在 GitLab 上。