Jetpack XR の ARCore を使用する

Jetpack XR の ARCore を使用すると、アプリは低レベルのシーンの理解プリミティブとモーション トラッキングを使用して、拡張現実(AR)の基本コンセプトを操作できます。AR エクスペリエンスを構築する際に、平面データを使用するか、空間内の固定された場所にコンテンツを固定する必要がある場合は、Jetpack XR の ARCore を使用します。

セッションのライフサイクルについて

Jetpack XR の ARCore によってトラッキングされるオブジェクトにはすべて、セッションを介してアクセスする必要があります。アクティビティのライフサイクルと同様に、セッション オブジェクトにもライフサイクルがあり、アプリでセッション オブジェクトの機能をどのように使用しているかに応じて維持する必要があります。アプリに XR 対応のアクティビティが 1 つしかない場合は、ライフサイクル対応コンポーネントを使用してセッションのライフサイクルを処理することを検討してください。

セッションを作成する

セッションを使用するには、作成する必要があります。セッションを作成するには、ユーザーがアプリに android.permission.SCENE_UNDERSTANDING 権限を付与している必要があります。

セッションを作成するには:

when (val result = Session.create(owner)) {
  is SessionCreateSuccess -> {
    session = result.session
  }
  is SessionCreatePermissionsNotGranted -> {
   // Request android.permission.SCENE_UNDERSTANDING.
  }
}

セッションの作成に失敗する理由については、SessionCreateResult をご覧ください。

セッションを再開する

セッションを再開するタイミングは、アプリが ARCore for Jetpack XR からの状態変化を処理する準備ができたときです。多くの場合、これはアクティビティの onResume() コールバックで行われます。ただし、アプリではユーザー操作まで処理を遅らせる場合があります。

次のコード スニペットは、セッションを再開する例を示しています。

when (val result = session.resume()) {
  is SessionResumeSuccess -> {
    // Session has been created successfully.
    // Attach any successful handlers here.
  }
  is SessionResumePermissionsNotGranted -> {
    // Request android.permission.SCENE_UNDERSTANDING.
}

セッションが再開されない理由については、SessionResumeResult をご覧ください。

セッションを一時停止する

アクティビティがバックグラウンドに移動したら、Session.pause() を使用してセッションを一時停止します。セッションを一時停止すると、セッションが再開されるまでトラッキングが一時的に停止し、認識システムの状態が維持されます。

セッションを破棄する

セッションを完全に破棄するには、Session.destroy() を使用します。これにより、セッションで使用されているリソースが解放され、すべてのセッション状態が破棄されます。

認識された平面の状態を取得する

Jetpack XR の ARCore は、平面の状態を出力する StateFlow を介して平面の状態を提供します。セッションで飛行機に登録すると、飛行機が追加、更新、削除されたときにアプリに通知されます。

Plane.subscribe(session).collect { planes ->
 // Planes have changed; update plane rendering
}

平面には次のプロパティがあります。

  • label: 特定の Plane のセマンティックな説明。WallFloorCeilingTable のいずれかです。
  • centerPose: 検出された平面の中心の姿勢。
  • extents: 検出された平面の寸法(メートル単位)。
  • vertices: 平面を近似する凸多角形の頂点のリスト。

プレーンに対してヒットテストを実行する

ヒットテストは、セッションでトラッキングされたオブジェクトとのレイの交差を計算する方法です。ヒットテストの一般的な用途は、テーブルを指してその場所にオブジェクトを配置することです。ヒットテストを実行すると、ヒット オブジェクトのリストが返されます。つまり、ヒットテストは最初のオブジェクトのヒットで停止しません。ただし、多くの場合、特定のタイプの最初のオブジェクトヒットのみに関心がある場合があります。

ヒットテストを実行するには、Interaction.hitTest()Ray を使用します。

val results = Interaction.hitTest(session, ray)
// When interested in the first Table hit:
val tableHit = results.firstOrNull {
  val trackable = it.trackable
  trackable is Plane && trackable.state.value.label == Plane.Label.Table
}

コンテンツを空間内の固定された場所に固定する

仮想オブジェクトに現実世界での位置を指定するには、Anchor を使用します。Anchor オブジェクトは、アプリが物理空間内の固定された位置を追跡するのに役立ちます。

アンカーは Pose を使用して作成されます。これは、既存の Trackable を基準に解釈することも、解釈しないようにすることもできます。

トラッキング対象を基準とするアンカーを作成する

アンカーが TrackablePlane など)を基準に作成されている場合、アンカーは、空間を移動するときに、アタッチされた Trackable に追従します。

val anchor = trackable.createAnchor(pose)

トラッキング対象なしでアンカーを作成する

Trackable に接続されていないアンカーを作成するには:

when (val result = Anchor.create(session, pose)) {
  is AnchorCreateSuccess -> // ...
  else -> // handle failure
}

エンティティをアンカーにアタッチする

この場所にモデルをレンダリングするには、GltfModel を作成し、そのポーズをアンカーのポーズに設定します。アンカーの TrackingStateStopped の場合、モデルが非表示になることを確認します。

// renderSession is androidx.xr.core.Session
anchor.state.collect { state ->
  if (state.trackingState == TrackingState.Tracking) {
    gltfEntity.setPose(
      renderSession.perceptionSpace.transformPoseTo(state.pose, renderSession.activitySpace)
    )
  } else if (state.trackingState == TrackingState.Stopped) {
    entity.setHidden(true)
  }
}

TrackingState について

Trackable には、使用する前に確認する必要がある TrackingState があります。TrackableStateTrackingTrackable は、Pose がシステムによってアクティブに更新されます。PausedTrackable は将来 Tracking になる可能性がありますが、StoppedTrackableTracking になることはありません。

セッション全体でアンカーを保持する

永続化されていないアンカーは、セッションが破棄されると消えます。アンカーを保持することで、アプリはプライベート アプリデータにそのアンカーの位置を記憶します。このアンカーは後続のセッションで取得でき、世界中の同じ場所に固定されます。

アンカーを保持するには、次のように anchor.persist() を使用します。

val uuid = anchor.persist()

アプリは、今後のセッションで UUID を使用してアンカーを取得できます。

when (val result = Anchor.load(session, uuid)) {
  is AnchorCreateSuccess -> // Loading was successful. The anchor is stored in result.anchor.
  else -> // handle failure
}

アンカーが不要になったら、unpersist() を呼び出します。これにより、アプリのストレージからアンカーが削除され、指定された UUID を Anchor.load() の呼び出しで取得できなくなります。

Anchor.unpersist(session, uuid)

アプリは、アプリのストレージにまだ存在する永続化されたすべてのアンカーのリストをリクエストすることもできます。

val uuids = Anchor.getPersistedAnchorUuids(session)