建立、控制及管理實體

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 (例如 SpatialPanelOrbiter 執行個體)。如果是 3D 模型等 3D 內容,可以直接存取工作階段。詳情請參閱本頁的「關於 ActivitySpace」一文。

實體元件系統

實體元件系統遵循組合而非繼承的原則。您可以附加定義行為的元件來擴充實體的行為,以便將相同行為套用至不同類型的實體。詳情請參閱本頁的「為實體新增常見行為」。

關於 ActivitySpace

每個 Session 都有一個 ActivitySpace,會隨著 Session 自動建立。ActivitySpace 是場景圖中的頂層 Entity

ActivitySpace 代表 3 維空間,採用右手座標系統 (x 軸指向右側、y 軸指向上方,而 z 軸則相對於原點指向後方),單位為公尺,與現實世界相符。ActivitySpace 的原點有些任意 (因為使用者可以在現實世界中重設 ActivitySpace 的位置),因此建議您以彼此相對的方式放置內容,而非以原點相對的方式放置。

使用實體

實體是 SceneCore 的核心。使用者看到及互動的大多數內容,都是代表面板、3D 模型等的實體。

由於 ActivitySpace 是場景圖的頂層節點,因此根據預設,所有新實體都會直接放置在 ActivitySpace 中。您可以呼叫 setParent()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 的瀏覽權限,請使用 setHidden()

// Hide the entity
entity.setHidden(true)

如要調整 Entity 大小,同時維持整體形狀,請使用 setScale()

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

為實體新增常見行為

您可以使用下列元件,為實體新增常見行為:

您必須透過 Session 類別中的適當建立方法,將元件例項化。舉例來說,如要建立 ResizableComponent,請呼叫 ResizableComponent.create()

如要將特定元件行為新增至 Entity,請使用 addComponent() 方法。

使用 MovableComponent 將 Entity 設為可供使用者移動

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 將實體設為可供使用者調整大小

使用者可以透過 ResizableComponent 調整 Entity 的大小。ResizableComponent 包含視覺互動提示,邀請使用者調整 Entity 的大小。建立 ResizableComponent 時,您可以指定最小或最大尺寸 (以公尺為單位)。您也可以在調整大小時指定固定顯示比例,讓寬度和高度按比例調整。

使用 ResizableComponent 時,您必須指定 ResizeListener,才能回應特定大小調整事件,例如 onResizeUpdateonResizeEnd

以下範例說明如何在 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 擷取使用者輸入事件

InteractableComponent 可讓您擷取使用者的輸入事件,例如使用者與 Entity 互動或將游標懸停在 Entity 上時。建立 InteractableComponent 時,您必須指定 InputEventListener 來接收輸入事件。使用者執行任何輸入動作時,系統會呼叫 onInputEvent 方法,並在 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_TYPE_RIGHT) {
            entity.setScale(1.5f)
        } else if (it.pointerType == InputEvent.POINTER_TYPE_LEFT) {
            entity.setScale(0.5f)
        }
    }
}
entity.addComponent(interactableComponent)