Jetpack XR SDK 允许您使用 Jetpack SceneCore 创建、控制和管理 Entity
实例,例如3D 模型、立体视频以及PanelEntity
>。
Jetpack SceneCore 采用两种常见的架构模式来支持 3D 开发:场景图和实体-组件系统 (ECS)。
使用场景图创建和控制实体
要在 3D 空间中创建和控制对象,您可以使用 Jetpack SceneCore 的 Session API 来访问场景图。场景图与用户的真实世界对齐,允许您将面板和 3D 模型等 3D 实体组织成层次结构,并保存这些实体的状态。
一旦您获得场景图的访问权限,就可以使用 Jetpack Compose for XR 中的 API 在场景图中创建空间 UI(例如,SpatialPanel
和 Orbiter
实例)。对于 3D 模型等 3D 内容,您可以直接访问 Session。要了解更多信息,请参阅本页上的关于 ActivitySpace。
实体组件系统
实体-组件系统遵循组合优于继承的原则。您可以通过附加定义行为的组件来扩展实体的行为,这使您可以将相同的行为应用于不同类型的实体。要了解更多信息,请查看本页上的向实体添加常见行为。
关于 ActivitySpace
每个 Session
都有一个随 Session
自动创建的 ActivitySpace
。ActivitySpace
是场景图中的顶级 Entity
。
ActivitySpace 代表一个三维空间,具有右手坐标系(x 轴指向右侧,y 轴指向上方,z 轴相对于原点向后)并以米为单位,与真实世界相符。ActivitySpace
的原点有些随意(因为用户可以在真实世界中重置 ActivitySpace
的位置),因此建议将内容相互定位,而不是相对于原点定位。
使用实体
实体是 SceneCore 的核心。用户看到和互动的大部分内容都是实体,代表面板、3D 模型等。
由于 ActivitySpace
是场景图的顶级节点,默认情况下,所有新实体都直接放置在 ActivitySpace
中。您可以通过调用 setParent()
或 addChild()
来沿着场景图重新定位实体。
实体具有一些对所有实体通用的默认行为,例如更改位置、旋转或可见性。特定的 Entity
子类,例如 GltfEntity
,具有支持该子类的附加行为。
操作实体
当您更改属于基本 Entity
类的 Entity
属性时,该更改将级联到其所有子项。例如,调整父 Entity
的 Pose
会导致其所有子项都具有相同的调整。在子 Entity
中进行更改不会影响其父项。
A 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
的可见性,请使用 setHidden()
。
// Hide the entity entity.setHidden(true)
要调整 Entity
的大小同时保持其整体形状,请使用 setScale()
。
// Double the size of the entity entity.setScale(2f)
向实体添加常见行为
您可以使用以下组件向实体添加常见行为
MovableComponent
:允许用户移动实体ResizableComponent
:允许用户以一致的 UI 模式调整实体大小InteractableComponent
:允许您捕获输入事件以进行自定义交互
实例化组件必须通过 Session
类中适当的创建方法完成。例如,要创建 ResizableComponent
,请调用 ResizableComponent.create()
。
要将特定组件行为添加到 Entity
,请使用 addComponent()
方法。
使用 MovableComponent 使实体可由用户移动
The MovableComponent
允许用户移动 Entity
。您还可以指定实体是否可以锚定到水平或垂直表面等表面类型,或桌子、墙壁或天花板等特定语义表面。要指定锚点选项,请在创建 MovableComponent
时指定一组 AnchorPlacement
。
下面是一个实体示例,它可以移动并锚定到任何垂直表面以及仅限地板和天花板的水平表面。
val anchorPlacement = AnchorPlacement.createForPlanes( planeTypeFilter = setOf(PlaneSemantic.FLOOR, PlaneSemantic.TABLE), planeSemanticFilter = setOf(PlaneType.VERTICAL) ) val movableComponent = MovableComponent.create( session = session, systemMovable = false, scaleInZ = false, anchorPlacement = setOf(anchorPlacement) ) entity.addComponent(movableComponent)
当用户移动实体时,scaleInZ
参数会自动调整实体的比例,使其在远离用户时以类似于系统在主空间中缩放面板的方式进行缩放。由于实体组件系统的“级联”特性,父级的比例将影响其所有子级。
使用 ResizableComponent 使实体可由用户调整大小
The ResizableComponent
允许用户调整 Entity
的大小。ResizableComponent
包含视觉交互提示,邀请用户调整 Entity
的大小。创建 ResizeableComponent
时,您可以指定最小或最大尺寸(以米为单位)。您还可以选择在调整大小时指定固定宽高比,以便宽度和高度按比例调整。
使用 ResieableComponent
时,您必须指定一个 ResizeListener
以响应特定的调整大小事件,例如 onResizeUpdate
或 onResizeEnd
。
下面是一个在 SurfaceEntity
上使用具有固定宽高比的 ResizableComponent
的示例
val resizableComponent = ResizableComponent.create(session) resizableComponent.minimumSize = Dimensions(177f, 100f, 1f) resizableComponent.fixedAspectRatio = 16f / 9f // Specify a 16:9 aspect ratio resizableComponent.addResizeListener( executor, object : ResizeListener { override fun onResizeEnd(entity: Entity, finalSize: Dimensions) { // update the size in the component resizableComponent.size = finalSize // update the Entity to reflect the new size (entity as SurfaceEntity).canvasShape = SurfaceEntity.CanvasShape.Quad(finalSize.width, finalSize.height) } }, ) entity.addComponent(resizableComponent)
使用 InteractableComponent 捕获用户输入事件
The InteractableComponent
允许您捕获来自用户的输入事件,例如当用户与 Entity
交互或悬停在 Entity
上时。创建 InteractableComponent
时,您必须指定一个 InputEventListener
来接收输入事件。当用户执行任何输入操作时,将调用 onInputEvent
方法,并提供 InputEvent
参数中提供的特定输入信息。
InputEvent.action
指定输入类型,例如悬停或轻触实体InputEvent.source
指定输入来源,例如手部或控制器输入InputEvent.pointerType
指定输入是来自右手还是左手
有关所有 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_TYPE_RIGHT) { entity.setScale(1.5f) } else if (it.pointerType == InputEvent.POINTER_TYPE_LEFT) { entity.setScale(0.5f) } } } entity.addComponent(interactableComponent)