Mempelajari Dasar-Dasar Android XR: Bagian 2 - Orbiter dan Lingkungan Spasial

1. Sebelum memulai

Yang akan Anda pelajari

  • Pengalaman pengguna unik yang dapat terwujud berkat Android XR.
  • Cara mengoptimalkan aplikasi untuk headset Android XR menggunakan library Jetpack Compose XR.
  • Cara menggunakan elemen UI dari library Jetpack Compose XR.
  • Tempat menemukan referensi untuk mempelajari lebih lanjut pembuatan aplikasi di Android XR.

Tidak termasuk dalam cakupan

Yang akan Anda butuhkan

Yang akan Anda bangun

Dalam codelab ini, Anda akan mengoptimalkan aplikasi lebih lanjut dengan beberapa fitur XR yang ada dengan menambahkan elemen UI mengambang dan menyesuaikan lingkungan virtual yang mengelilingi pengguna saat menggunakan aplikasi.

Titik awal

Hasil akhir

2. Memulai persiapan

Mendapatkan kode

  1. Kode untuk codelab ini dapat ditemukan di direktori xr-fundamentals dalam repositori GitHub xr-codelabs. Untuk meng-clone repositori, jalankan perintah berikut:
git clone https://github.com/android/xr-codelabs.git
  1. Atau, Anda dapat mendownload repositori sebagai file ZIP:

Membuka project

  • Setelah memulai Android Studio, impor direktori xr-fundamentals/part1. Direktori xr-fundamentals/part2 berisi kode solusi yang dapat Anda rujuk kapan saja jika Anda mengalami kebuntuan atau hanya ingin melihat project lengkap.

Memahami kode

  • Setelah membuka project di Android Studio, luangkan waktu untuk memeriksa kode awal.
  • Jika Anda belum mengikuti codelab pertama atau belum menggunakan emulator Android XR, ikuti langkah-langkah di Menjalankan aplikasi di emulator Android XR untuk menjalankan aplikasi.

3. Mempelajari konsep XR: Orbiter

Orbiter adalah elemen UI mengambang yang tersedia dalam mode Ruang Penuh yang biasanya digunakan untuk mengontrol konten dalam panel spasial atau entitas lain tempat orbitnya ditambatkan. Dengan menggunakan orbiter untuk kontrol konten, konten akan memiliki lebih banyak ruang, yang berarti pengguna dapat dengan cepat mengakses fitur yang terdapat dalam orbiter saat konten utama tetap terlihat. Orbiter memberi Anda fleksibilitas untuk mengintegrasikan komponen UI yang ada (seperti menu navigasi) atau membuat komponen baru.

Selain itu, Orbiter API memungkinkan Anda merender konten orbiter seperti biasa saat berjalan dalam mode Ruang Utama atau di perangkat non-XR dan otomatis membaginya menjadi orbiter saat berjalan dalam mode Ruang Penuh.

Dalam mode Ruang Utama, kolom samping navigasi ini dirender dalam panel aplikasi utama.

Dalam mode Ruang Penuh, kolom samping navigasi dibagi menjadi orbiter yang terkait ke panel utama.

Pada tahap ini, aplikasi berisi tombol di panel aplikasi atas untuk beralih antara mode Ruang Utama dan mode Ruang Penuh. Tombol ini adalah contoh sempurna dari kontrol yang dapat ditempatkan dalam orbiter saat berjalan dalam mode Ruang Penuh, karena memindahkan kontrol untuk mengorbit panel utama akan membantu kontrol terlihat jelas sekaligus secara visual menunjukkan bahwa kontrol akan menciutkan konten aplikasi ke dalam panel tersebut saat diklik.

Status saat ini

Yang akan Anda terapkan

Untuk mempelajari lebih lanjut pertimbangan desain untuk orbiter, lihat UI Spasial.

4. Menambahkan Orbiter

Menggabungkan tombol mode ruang

Untuk mengubah tombol mode ruang menjadi orbiter, gabungkan composable ToggleSpaceModeButton dalam composable Orbiter.

ui/component/XRFundamentalsTopAppBar .kt

import androidx.compose.foundation.shape.CornerSize
import androidx.compose.ui.Alignment
import androidx.compose.ui.unit.dp
import androidx.xr.compose.spatial.EdgeOffset
import androidx.xr.compose.spatial.Orbiter
import androidx.xr.compose.spatial.OrbiterEdge
import androidx.xr.compose.subspace.layout.SpatialRoundedCornerShape

... 

Orbiter(
    position = OrbiterEdge.Top,
    alignment = Alignment.End,
    offset = EdgeOffset.inner(16.dp),
    shape = SpatialRoundedCornerShape(
        CornerSize(percent = 100)
    )
) {
    ToggleSpaceModeButton()
}

Sekarang, jalankan aplikasi: Saat berjalan dalam mode Ruang Utama, Anda akan melihat bahwa tidak ada yang berubah. Namun, saat mengklik tombol dan aplikasi memasuki mode Ruang Penuh, Anda akan melihat bahwa tombol tidak lagi di panel aplikasi atas, tetapi ada di tepi kanan atas panel spasial utama.

Orbiter dikaitkan ke panel spasial utama karena merupakan entity spasial induk terdekat di hierarki UI. Posisi persis orbiter yang terkait dengan panel spasial utama ditentukan oleh parameter position, alignment, dan offset. Coba ubah parameter ini untuk melihat rentang perilaku yang didukungnya.

Mode Ruang Utama

Mode Ruang Penuh

5. Mempelajari konsep XR: Lingkungan spasial

Menggunakan Orbiter untuk menyesuaikan posisi di ruang 3D elemen UI adalah cara yang bagus untuk meningkatkan pengalaman pengguna di perangkat XR. Anda dapat meningkatkan pengalaman lebih lanjut dengan menyesuaikan lingkungan spasial tempat pengguna berada saat menggunakan aplikasi Anda.

Lingkungan spasial dapat menggabungkan aset kedalaman, tekstur, dan geometri 3D untuk menciptakan pengalaman yang kaya dan imersif secara visual. Hal ini dilakukan melalui penggunaan gambar skybox bulat (dalam format EXR) untuk memberikan latar belakang panorama yang jauh dan/atau aset geometri (dalam format glTF) untuk memberikan elemen latar depan dan latar tengah yang dapat menyatu dengan skybox. Misalnya, aplikasi streaming video dapat menggunakan skybox malam dengan glTF bioskop drive-in dengan layar proyeksi dan mobil. Saat membuat aset untuk menetapkan lingkungan spasial pengguna, pastikan aset Anda mencapai resolusi berkualitas tinggi sekaligus mempertahankan ukuran file yang wajar. Lihat Mengoptimalkan aset lingkungan untuk mengetahui informasi selengkapnya.

Selain itu, opasitas lingkungan spasial dapat dikontrol. Hal ini memungkinkan streaming video dunia nyata menembus dan menyatu dengan lingkungan virtual, yang dapat membantu pengguna mempertahankan posisinya.

Seseorang berdiri di lingkungan spasial berbatu dengan panel UI besar di tengah.

Pada langkah berikutnya, Anda akan menambahkan aset geometri ke aplikasi dan membuat menu untuk memungkinkan pengguna memilih lingkungan mereka.

Untuk mengetahui semua detail tentang mendesain dan menerapkan lingkungan spasial, lihat Lingkungan spasial dan Menambahkan lingkungan spasial ke aplikasi Anda.

6. Mengizinkan pengguna mengubah lingkungan spasial

Cara aplikasi mengontrol lingkungan spasial

Sebelum memulai, sebaiknya pahami cara aplikasi dapat mengontrol lingkungan spasial.

Tidak seperti konten dalam panel, aplikasi tidak mengontrol lingkungan secara langsung. Sebagai gantinya, aplikasi dapat berinteraksi dengan sesi SceneCore untuk memberikan preferensi bagi lingkungan yang ingin digunakan sistem. Preferensi ini diwakili oleh SpatialEnvironmentPreference, yang terdiri dari gambar EXR skybox dan/atau glTF geometri. Hal yang terjadi saat aplikasi memberikan preferensi bergantung pada kemampuan aplikasi saat menetapkan preferensi. Jika aplikasi Anda memiliki kemampuan untuk mengubah lingkungan, sistem akan segera menggunakannya. Jika aplikasi tidak memiliki kemampuan tersebut, preferensi akan diterapkan saat aplikasi Anda mendapatkan kemampuan tersebut.

Misalnya, aplikasi umumnya tidak memiliki kemampuan untuk mengubah lingkungan saat berjalan dalam mode Ruang Utama, tetapi umumnya dapat melakukannya saat berjalan dalam mode Ruang Penuh. Jadi, jika Anda mengizinkan pengguna menetapkan preferensi lingkungan saat dalam mode Ruang Utama, preferensi tersebut umumnya tidak akan berlaku hingga aplikasi Anda berjalan dalam mode Ruang Penuh.

Menambahkan dependensi pada library XR SceneCore

Untuk mulai mengubah lingkungan spasial, tambahkan dependensi pada library XR SceneCore, yang akan Anda gunakan untuk memuat aset lingkungan dan menetapkan preferensi lingkungan. Anda juga harus menambahkan dependensi pada artefak kotlinx-coroutines-guava, karena beberapa API untuk memuat aset menggunakan jenis data ListenableFuture.

libs.version.toml

[versions]
...
xrSceneCore = "1.0.0-alpha04"
kotlinxCoroutinesGuava = "1.10.2"

[libraries]
...
androidx-xr-scenecore = { group = "androidx.xr.scenecore", name = "scenecore", version.ref = "xrSceneCore"}
jetbrains-kotlinx-coroutines-guava = {group = "org.jetbrains.kotlinx", name="kotlinx-coroutines-guava", version.ref = "kotlinxCoroutinesGuava"}

app/build.gradle.kts

dependencies {
    ...
    implementation(libs.androidx.xr.scenecore)
    implementation(libs.jetbrains.kotlinx.coroutines.guava)
    ...
}

Menambahkan aset lingkungan ke project Anda

Untuk menentukan preferensi lingkungan yang unik, Anda memerlukan skybox dan/atau aset geometri. Untuk codelab ini, Anda hanya akan menggunakan aset geometri green_hills_ktx2_mipmap.glb, yang dapat Anda temukan di folder part2 yang berisi kode solusi atau di GitHub.

  1. Klik kanan modul aplikasi di jendela Project dalam Android Studio. Kemudian, pilih New > Folder > Assets Folder, lalu klik Finish untuk membuat folder.
  2. Tambahkan file GLB ke folder app/src/main/assets yang baru saja Anda buat.

Membuat model opsi lingkungan

Untuk menyederhanakan interaksi antara kode UI dan API sistem, Anda dapat membuat class data Kotlin untuk membuat model setiap opsi lingkungan.

  1. Klik kanan paket com.example.android.xrfundamentals di jendela Project, lalu pilih New > Package. Masukkan com.example.android.xrfundamentals.environment sebagai nama paket.
  2. Klik kanan pada paket tersebut, lalu pilih New > Kotlin Class/File. Masukkan EnvironmentOption sebagai nama, lalu klik jenis Data class.
  3. Tambahkan kode berikut di file yang baru saja Anda buat:

EnvironmentOption.kt

data class EnvironmentOption(val name: String, val skyboxPath: String?, val geometryPath: String?)

val DEFAULT_ENVIRONMENT = EnvironmentOption("Default", null, null)

val ENVIRONMENT_OPTIONS = listOf(
    DEFAULT_ENVIRONMENT,
    EnvironmentOption("Green Hills", null, "green_hills_ktx2_mipmap.glb")
)

Menambahkan helper untuk membuat aset pemuatan dan menampilkan SpatialEnvironmentPreference

Selanjutnya, Anda dapat menambahkan metode bantuan ke class data untuk memudahkan konversi EnvironmentOption ke SpatialEnvrionmentPreference yang sesuai.

EnvironmentOption.kt

import androidx.xr.runtime.Session
import androidx.xr.scenecore.ExrImage
import androidx.xr.scenecore.GltfModel
import androidx.xr.scenecore.SpatialEnvironment
import kotlinx.coroutines.guava.await

...

data class EnvironmentOption(val name: String, val skyboxPath: String?, val geometryPath: String?) {
    suspend fun toSpatialEnvironmentPreference(session: Session): SpatialEnvironmentPreference? {
        if (skyboxPath == null && geometryPath == null) {
            return null
        } else {
            val skybox = skyboxPath?.let {
                ExrImage.create(session, it).await()
            }

            val geometry = geometryPath?.let {
                GltfModel.create(session, it).await()
            }

            return SpatialEnvironmentPreference(skybox, geometry)
        }
    }
}

Ada beberapa hal yang perlu diperhatikan di sini:

  • Jika skybox dan geometri bernilai null, null akan ditampilkan untuk menunjukkan bahwa preferensi lingkungan sistem default harus digunakan. Lihat setSpatialEnvironmentPreference untuk mengetahui informasi selengkapnya.
  • Resource skybox dan geometry dibuat secara asinkron karena aset ini sering kali cukup besar dan memerlukan waktu untuk dibaca ke dalam memori. Di aplikasi produksi, sebaiknya pertimbangkan untuk meng-cache aset ini di memori jika sering beralih lingkungan.

Mengimplementasikan UI pemilihan lingkungan

Untuk menerapkan UI, Anda akan menambahkan orbiter kedua yang berputar melalui opsi lingkungan saat diklik.

Menambahkan orbiter

  1. Klik kanan modul app di jendela Project, lalu pilih New > Vector Asset. Klik kolom Clip art, lalu cari dan pilih aset landscape (dari grup ikon Filled), lalu klik OK, lalu Next untuk membuat aset.
  2. Klik kanan paket com.example.android.xrfundamentals.ui.component, lalu pilih New > Kotlin Class/File. Masukkan EnvironmentSelectionOrbiter sebagai nama dan klik jenis File.
  3. Dalam file yang baru saja Anda buat, tambahkan implementasi composable EnvironmentSelectionOrbiter berikut

EnvironmentSelectionOrbiter.kt

import androidx.compose.foundation.shape.CornerSize
import androidx.compose.material3.FilledTonalIconButton
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.xr.compose.spatial.EdgeOffset
import androidx.xr.compose.spatial.Orbiter
import androidx.xr.compose.spatial.OrbiterEdge
import androidx.xr.compose.subspace.layout.SpatialRoundedCornerShape
import com.example.android.xrfundamentals.R

@Composable
fun EnvironmentSelectionOrbiter(
    modifier: Modifier = Modifier,
    onClick: () -> Unit = {},
) {
    Orbiter(
        position = OrbiterEdge.Top,
        alignment = Alignment.Start,
        offset = EdgeOffset.inner(16.dp),
        shape = SpatialRoundedCornerShape(
            CornerSize(100)
        )
    ) {
        FilledTonalIconButton(
            modifier = modifier,
            onClick = onClick,
        ) {
            Icon(painterResource(R.drawable.baseline_landscape_24), "Show environment selection dialog")
        }
    }
}
  1. Terakhir, tambahkan EnvironmentSelectionOrbiter dalam panel spasial utama

XRFundamentalsApp.kt

import androidx.xr.compose.platform.LocalSpatialCapabilities
import com.example.android.xrfundamentals.ui.component.EnvironmentSelectionOrbiter

...

SpatialPanel(...) {

    // Only show the environment selection orbiter if the app is actually able to
    // change the environment
    if (LocalSpatialCapabilities.current.isAppEnvironmentEnabled) {
        EnvironmentSelectionOrbiter(
            onClick = { TODO() }
        )
    }
    ...
}

Mengubah lingkungan saat orbiter diklik

Agar semuanya berfungsi, ada satu langkah terakhir, yaitu memanggil setSpatialEnvironmentPreference di handler klik EnvironmentSelectionOrbiter.

  1. Siapkan variabel untuk melacak opsi lingkungan saat ini (di luar Subspace sehingga status dipertahankan saat beralih antara mode Ruang Utama dan mode Ruang Penuh). Selain itu, buat variabel untuk sesi XR saat ini dan cakupan coroutine untuk memanggil helper toSpatialEnvironmentPreference

XRFundamentalsApp.kt

import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.xr.compose.platform.LocalSession

... 

var currentEnvironmentOptionIndex by remember { mutableStateOf(0) }

Subspace {
    val session = checkNotNull(LocalSession.current)
    val scope = rememberCoroutineScope()
    ...
}
  1. Implementasikan callback onClick untuk memutar opsi lingkungan.

XRFundamentalsApp.kt

EnvironmentSelectionOrbiter(
    onClick = {
        scope.launch {
            currentEnvironmentOptionIndex =
                (currentEnvironmentOptionIndex + 1) % ENVIRONMENT_OPTIONS.size
            session.scene.spatialEnvironment.setSpatialEnvironmentPreference(
                ENVIRONMENT_OPTIONS[currentEnvironmentOptionIndex].toSpatialEnvironmentPreference(
                    session
                )
            )
        }
    }
)

Jalankan aplikasi Anda sekali lagi dan lihat, Anda dapat beralih antara Green Hills dan lingkungan default.

b0e9571ef5f5597b.gif

7. Selamat

Untuk terus mempelajari cara mengoptimalkan XR, lihat referensi dan latihan berikut:

Bacaan lebih lanjut

Tantangan

  • Temukan atau buat aset lingkungan tambahan dan tambahkan sebagai opsi.
  • Ubah pengontrol lingkungan dan UI untuk memungkinkan pengguna menetapkan preferensi passthrough menggunakan setPassthroughOpacityPreference API. Perhatikan bahwa mengontrol passthrough dibatasi oleh kemampuan yang berbeda dengan mengubah aset lingkungan.

Dokumen referensi