创建、控制和管理实体

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

借助 Jetpack XR SDK,您可以使用 Jetpack SceneCore 创建、控制和管理 Entity实例,例如 3D 模型立体视频PanelEntity

Jetpack SceneCore 采用两种常见的架构模式来支持 3D 开发:场景图实体组件 系统 (ECS)。

使用场景图创建和控制实体

如需在 3D 空间中创建和控制对象,您可以使用 Jetpack SceneCore's 会话 API 来访问场景图。场景图与用户的真实世界保持一致,可让您将面板和 3D 模型等 3D 实体整理到分层结构中,并保留这些实体的状态。

访问场景图后,您可以使用 Jetpack Compose for XR 中的 API 在场景图中创建空间界面(例如 SpatialPanelOrbiter 实例)。对于 3D 模型等 3D 内容,您可以直接访问会话。如需了解详情,请参阅本页面的 ActivitySpace 简介

实体组件系统

实体组件系统遵循组合优于继承的原则。您可以通过附加行为定义组件来扩展实体的行为,这样您就可以将相同的行为应用于不同类型的实体。如需了解详情,请查看本页面的向实体添加常见 行为

ActivitySpace 简介

每个 Session 都有一个 ActivitySpace,该 ActivitySpace 会与 Session 一起自动创建 。ActivitySpace 是场景图中的顶级 Entity

ActivitySpace 表示一个具有右手坐标系(x 轴指向右侧,y 轴指向上方,z 轴相对于原点指向后方)的 3 维空间,其单位为米,与真实世界相符。ActivitySpace 的原点在某种程度上是任意的(因为用户可以重置 ActivitySpace 在真实世界中的位置),因此建议将内容彼此相对定位,而不是相对于原点定位。

使用实体

实体是 SceneCore 的核心。用户看到和互动的大部分内容都是表示面板、3D 模型等的实体。

由于 ActivitySpace 是场景图的顶级节点,因此默认情况下,所有新实体都直接放置在 ActivitySpace 中。您可以沿场景图重新定位 实体,方法是设置其 parent 或使用 addChild()

实体具有一些默认行为,适用于所有实体通用的内容,例如更改位置、旋转或可见性。特定的 Entity 子类(例如 GltfModelEntity)具有支持该子类的其他行为。

操控实体

当您对属于基本 Entity 类的 Entity 属性进行更改时,该更改将级联到其所有子项。例如, 调整父级 EntityPose 会导致其所有子项 进行相同的调整。在子级 Entity 中进行更改不会影响其父级。

Pose 表示实体在 3D 空间中的位置和旋转。位置是一个 Vector3,由 x、y、z 数值位置组成。旋转由 Quaternion表示。Entity 的位置始终相对于其父实体。换句话说,位置为 (0, 0, 0) 的 Entity 将放置在其父实体原点。

// Place the entity forward 2 meters
val newPosition = Vector3(0f, 0f, -2f)
// Rotate the entity by 180 degrees on the up axis (upside-down)
val newOrientation = Quaternion.fromEulerAngles(0f, 0f, 180f)
// Update the position and rotation on the entity
entity.setPose(Pose(newPosition, newOrientation))

如需停用 Entity,请使用 setEnabled()。这会使其不可见,并停止对其执行的所有处理。

// Disable the entity.
entity.setEnabled(false)

如需在保持 Entity 整体形状的同时调整其大小,请使用 setScale()

// Double the size of the entity
entity.setScale(2f)

向实体添加常见行为

您可以使用以下组件向实体添加常见行为:

实例化组件必须通过 Session 类中的相应创建方法完成。例如,如需创建 ResizableComponent,请调用 ResizableComponent.create()

如需向 Entity 添加特定组件行为,请使用 addComponent() 方法。

使用 MovableComponent 使实体可供用户移动

借助 MovableComponent,用户可以移动 Entity

当装饰与用户互动时,系统会将移动事件分派给组件。使用 MovableComponent.createSystemMovable()创建的默认系统行为会在 装饰被拖动时移动您的Entity

val movableComponent = MovableComponent.createSystemMovable(session)
entity.addComponent(movableComponent)

可选的 scaleInZ 参数(默认设置为 true)使实体 在远离用户时自动调整其比例, 类似于系统在 主空间中缩放面板的方式。 由于实体组件系统的“级联”特性,父级的比例会影响其所有子项。

您还可以指定实体是否可以锚定到水平或垂直表面等表面类型,或锚定到表、墙壁或天花板等特定语义表面。如需指定锚定选项,请在创建 MovableComponent 时指定一组 AnchorPlacement 。在此示例中,实体可以移动并锚定到任何地板或桌面水平表面:

val anchorPlacement = AnchorPlacement.createForPlanes(
    anchorablePlaneOrientations = setOf(PlaneOrientation.VERTICAL),
    anchorablePlaneSemanticTypes = setOf(PlaneSemanticType.FLOOR, PlaneSemanticType.TABLE)
)

val movableComponent = MovableComponent.createAnchorable(
    session = session,
    anchorPlacement = setOf(anchorPlacement)
)
entity.addComponent(movableComponent)

使用 ResizableComponent 使实体可供用户调整大小

借助 ResizableComponent,用户可以调整 Entity 的大小。ResizableComponent 包含视觉互动提示,邀请用户调整 Entity 的大小。创建 ResizableComponent 时,您可以指定最小或最大尺寸(以米为单位)。您还可以选择在调整大小时指定固定的宽高比,以便宽度和高度按比例相互调整。

创建 ResizableComponent 时,请指定处理更新事件的 resizeEventListener。您可以响应不同的 ResizeState 事件,例如 RESIZE_STATE_ONGOINGRESIZE_STATE_END

以下示例展示了如何在 SurfaceEntity 上使用具有固定宽高比的 ResizableComponent

val resizableComponent = ResizableComponent.create(session) { event ->
    if (event.resizeState == ResizeEvent.ResizeState.END) {
        // update the Entity to reflect the new size
        surfaceEntity.shape = SurfaceEntity.Shape.Quad(FloatSize2d(event.newSize.width, event.newSize.height))
    }
}
resizableComponent.minimumEntitySize = FloatSize3d(177f, 100f, 1f)
resizableComponent.isFixedAspectRatioEnabled = true // Maintain a fixed aspect ratio when resizing

surfaceEntity.addComponent(resizableComponent)

使用 InteractableComponent 捕获用户输入事件

借助 InteractableComponent,您可以捕获用户的输入事件, 例如用户与 Entity 互动或将鼠标悬停在 Entity 上时。创建 InteractableComponent 时,请指定接收输入事件的监听器。 当用户执行任何输入操作时,系统将使用 输入信息调用 InputEvent 参数中提供的监听器。

如需查看所有 InputEvent 常量的完整列表,请参阅 参考 文档

以下代码段展示了如何使用 InteractableComponent 来增加实体的尺寸(使用右手)和减小实体的尺寸(使用左手)。

val executor = Executors.newSingleThreadExecutor()
val interactableComponent = InteractableComponent.create(session, executor) {
    // when the user disengages with the entity with their hands
    if (it.source == InputEvent.Source.HANDS && it.action == InputEvent.Action.UP) {
        // increase size with right hand and decrease with left
        if (it.pointerType == InputEvent.Pointer.RIGHT) {
            entity.setScale(1.5f)
        } else if (it.pointerType == InputEvent.Pointer.LEFT) {
            entity.setScale(0.5f)
        }
    }
}
entity.addComponent(interactableComponent)

在运行时创建自定义 3D 模型

借助 Custom Mesh API,您可以直接在代码中以编程方式生成 3D 形状,而不是加载 glTF 文件等静态资源。通过即时构建自定义几何图形,您可以渲染程序化数据、动态自定义形状和看似无限的 3D 环境,例如随着用户探索而不断生成的地形。此外,在运行时生成网格还有助于缩小二进制文件大小,因为无需打包单个 3D 资源的无数变体。