Jetpack XR SDK を使用すると、Jetpack SceneCore を使用して、3D モデル、立体視動画、PanelEntity
などの Entity
インスタンスを作成、制御、管理できます。
Jetpack SceneCore は、3D 開発をサポートするために、シーングラフとエンティティ コンポーネント システム(ECS)という 2 つの一般的なアーキテクチャ パターンを採用しています。
シーングラフを使用してエンティティを作成、制御する
3D 空間でオブジェクトを作成して制御するには、Jetpack SceneCore の Session API を使用してシーングラフにアクセスします。シーングラフはユーザーの現実世界と連動しており、パネルや 3D モデルなどの 3D エンティティを階層構造に整理し、それらのエンティティの状態を保持できます。
シーングラフにアクセスしたら、Jetpack Compose for XR の API を使用して、シーングラフ内に空間 UI(SpatialPanel
や Orbiter
など)を作成できます。3D モデルなどの 3D コンテンツの場合は、セッションに直接アクセスできます。詳細については、このページのActivitySpace についてをご覧ください。
エンティティ コンポーネント システム
エンティティ コンポーネント システムは、継承よりもコンポジションの原則に従います。動作を定義するコンポーネントを接続することで、エンティティの動作を拡張できます。これにより、同じ動作を異なるタイプのエンティティに適用できます。詳細については、このページのエンティティに一般的な動作を追加するをご覧ください。
ActivitySpace について
各 Session
には、Session
とともに自動的に作成される ActivitySpace
があります。ActivitySpace
は、シーングラフの最上位の Entity
です。
ActivitySpace は、右手系の座標系(原点に対して x 軸は右、y 軸は上、z 軸は後ろ)の 3 次元空間を表します。単位はメートルで、現実世界に対応しています。ActivitySpace
の原点は任意です(ユーザーは現実世界で ActivitySpace
の位置をリセットできるため)。そのため、コンテンツは原点ではなく、互いに対して配置することをおすすめします。
エンティティを操作する
エンティティは SceneCore の中核です。ユーザーが表示、操作するほとんどのものは、パネルや 3D モデルなどを表すエンティティです。
ActivitySpace
はシーングラフの最上位ノードであるため、デフォルトでは、すべての新しいエンティティが ActivitySpace
に直接配置されます。シーングラフに沿ってエンティティを再配置するには、setParent
または addChild
を呼び出します。
エンティティには、位置、回転、可視性の変更など、すべてのエンティティに共通するデフォルトの動作があります。GltfEntity
などの特定の Entity
サブクラスには、サブクラスをサポートする追加の動作があります。
エンティティを操作する
ベースの Entity
クラスに属する Entity
プロパティを変更すると、その変更はすべての子にカスケードされます。たとえば、親 Entity
の Pose
を調整すると、すべての子に同じ調整が適用されます。子 Entity
を変更しても、親には影響しません。
Pose
は、3D 空間内のエンティティの位置と回転を表します。ロケーションは、x、y、z の数字の位置で構成される Vector3
です。回転は 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 を使用して、ユーザーがエンティティを移動できるようにする
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
のサイズ変更をユーザーに促す視覚的なインタラクション キューが含まれています。ResizeableComponent
を作成するときに、最小サイズまたは最大サイズ(メートル単位)を指定できます。サイズ変更時に固定のアスペクト比を指定すると、幅と高さが互いに比例してサイズ変更されます。
ResieableComponent
を使用する場合は、onResizeUpdate
や onResizeEnd
などの特定のサイズ変更イベントに応答するように ResizeListener
を指定する必要があります。
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
を指定する必要があります。ユーザーが入力アクションを実行すると、InputEvent
パラメータで指定された特定の入力情報とともに onInputEvent
メソッドが呼び出されます。
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)