1. 시작하기 전에
학습할 내용
- Android XR로 제공되는 고유한 사용자 환경
- Jetpack Compose XR 라이브러리를 사용하여 Android XR 헤드셋에 맞게 앱을 최적화하는 방법
- Jetpack Compose XR 라이브러리의 UI 요소를 사용하는 방법
- Android XR용 앱 빌드에 관해 자세히 알아볼 수 있는 곳
이 Codelab의 의도하지 않은 용도
- Compose를 사용하지 않고 Android XR 앱을 빌드하는 방법에 관한 가이드. Android 뷰 기반 앱의 UI 개발을 참고하세요.
- Android XR용 Unity 또는 OpenXR 앱을 빌드하는 방법에 관한 가이드. Android XR용 Unity로 개발 및 OpenXR로 개발을 참고하세요.
필요한 항목
- 최신 Android 스튜디오 프리뷰
- Android XR 에뮬레이터를 실행할 수 있는 컴퓨터
- Compose 사용 시 알아야 하는 Android 기본사항 과정을 완료하는 등 Kotlin 및 Jetpack Compose 사용 경험
- Android Virtual Device를 생성하고 Android Virtual Device에서 앱을 실행한 경험
- Android XR 기본사항 알아보기: 1부 - 모드 및 공간 패널 Codelab을 완료하는 등 Android XR의 모드 및 공간 패널을 사용해 본 경험
빌드할 항목
이 Codelab에서는 플로팅 UI 요소를 추가하고 앱을 사용하는 동안 사용자를 둘러싼 가상 환경을 맞춤설정하여 기존 XR 기능을 사용하여 앱을 더욱 최적화합니다.
시작점 | 최종 결과 |
2. 설정
코드 가져오기
- 이 Codelab의 코드는
xr-codelabs
GitHub 저장소의xr-fundamentals
디렉터리에서 찾을 수 있습니다. 저장소를 클론하려면 다음 명령어를 실행하세요.
git clone https://github.com/android/xr-codelabs.git
- 또는 저장소를 ZIP 파일로 다운로드할 수도 있습니다.
프로젝트 열기
- Android 스튜디오를 시작한 후
xr-fundamentals/part1
디렉터리를 가져옵니다.xr-fundamentals/part2
디렉터리에는 솔루션 코드가 포함되어 있습니다. 솔루션 코드는 도움이 필요한 경우 또는 전체 프로젝트를 살펴보고 싶을 때 언제든지 참조할 수 있습니다.
코드 숙지하기
- Android 스튜디오에서 프로젝트를 열고 시작 코드를 살펴봅니다.
- 첫 번째 Codelab을 수강하지 않았거나 아직 Android XR 에뮬레이터를 사용하지 않은 경우 Android XR 에뮬레이터에서 앱 실행의 단계에 따라 앱을 실행합니다.
3. XR 개념 알아보기: Orbiter
Orbiter는 전체 공간 모드에서 사용할 수 있는 플로팅 UI 요소로, 일반적으로 공간 패널 내의 콘텐츠 또는 궤도가 고정된 다른 항목을 제어하는 데 사용됩니다. 콘텐츠 컨트롤에 Orbiter를 사용하면 콘텐츠에 더 많은 공간이 확보되므로 사용자가 기본 콘텐츠가 계속 표시되는 동안 Orbiter에 포함된 기능에 빠르게 액세스할 수 있습니다. Orbiter를 사용하면 기존 UI 구성요소(예: 탐색 메뉴)를 통합하거나 새 구성요소를 만들 수 있습니다.
또한 Orbiter API를 사용하면 홈 공간 모드 또는 XR 기기가 아닌 기기에서 실행할 때와 같이 일반적으로 Orbiter의 콘텐츠를 렌더링하고 전체 공간 모드에서 실행할 때는 자동으로 Orbiter로 분할할 수 있습니다.
홈 공간 모드에서는 이 탐색 레일이 기본 앱 패널 내에 렌더링됩니다. | 전체 공간 모드에서는 탐색 레일이 기본 패널에 연결된 Orbiter로 분할됩니다. |
이 시점에서 앱의 상단 앱 바에는 홈 공간 모드와 전체 공간 모드 간에 전환하는 버튼이 포함됩니다. 이 버튼은 전체 공간 모드에서 실행할 때 Orbiter 내부에 포함될 수 있는 컨트롤의 완벽한 예입니다. 컨트롤을 이동하여 기본 패널을 공전하면 컨트롤이 눈에 띄게 표시되는 동시에 클릭 시 앱의 콘텐츠가 해당 패널로 접히는 것을 시각적으로 나타낼 수 있기 때문입니다.
현재 상태입니다. | 구현할 내용 |
Orbiter의 설계 고려사항에 관한 자세한 내용은 공간 UI를 참고하세요.
4. Orbiter 추가
공간 모드 전환 버튼 래핑
공간 모드 전환 버튼을 Orbiter로 전환하려면 ToggleSpaceModeButton
컴포저블을 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()
}
이제 앱을 실행합니다. 홈 공간 모드에서 실행하면 아무것도 변경되지 않은 것을 알 수 있습니다. 하지만 전환 버튼을 클릭하고 앱이 전체 공간 모드로 전환되면 버튼이 더 이상 상단 앱 바에 있지 않고 기본 공간 패널의 오른쪽 상단 가장자리에 있는 것을 볼 수 있습니다.
Orbiter는 기본 공간 패널에 고정됩니다. UI 트리의 가장 가까운 상위 공간 항목이기 때문입니다. 기본 공간 패널을 기준으로 한 Orbiter의 정확한 위치는 position
, alignment
, offset
매개변수에 의해 결정됩니다. 이러한 매개변수를 수정하여 지원되는 동작의 범위를 확인해 보세요.
홈 공간 모드 | 전체 공간 모드 |
5. XR 개념 알아보기: 공간 환경
Orbiter
를 사용하여 UI 요소의 3D 공간에서 위치를 맞춤설정하는 것은 XR 기기의 사용자 환경을 개선하는 좋은 방법입니다. 앱을 사용할 때 사용자가 있는 공간 환경을 맞춤설정하여 환경을 더욱 개선할 수 있습니다.
공간 환경은 깊이, 텍스처, 3D 도형 애셋을 통합하여 시각적으로 몰입도 높은 풍부한 환경을 만들 수 있습니다. 이는 구형 스카이박스 이미지(EXR 형식)를 사용하여 먼 파노라마 배경을 제공하거나 도형 애셋(glTF 형식)을 사용하여 스카이박스에 블렌딩할 수 있는 전경 및 중경 요소를 제공하는 방식으로 이루어집니다. 예를 들어 동영상 스트리밍 앱은 프로젝션 화면과 자동차가 있는 드라이브인 영화관의 glTF와 함께 야간 스카이박스를 사용할 수 있습니다. 사용자의 공간 환경을 설정하기 위한 애셋을 만들 때는 적절한 파일 크기를 유지하면서 애셋의 해상도를 높여야 합니다. 자세한 내용은 환경 애셋 최적화를 참고하세요.
또한 공간 환경의 불투명도를 제어할 수 있습니다. 이렇게 하면 실제 환경의 동영상 스트림이 가상 환경을 통과하여 혼합되므로 사용자가 방향을 유지하는 데 도움이 됩니다.
다음 단계에서는 앱에 도형 애셋을 추가하고 사용자가 환경을 선택할 수 있는 메뉴를 만듭니다.
공간 환경 설계 및 구현에 관한 모든 세부정보는 공간 환경 및 앱에 공간 환경 추가를 참고하세요.
6. 사용자가 공간 환경을 변경하도록 허용
앱이 공간 환경을 제어하는 방법
시작하기 전에 앱이 공간 환경을 정확하게 제어하는 방법을 이해하는 것이 좋습니다.
패널 내 콘텐츠와 달리 앱은 환경을 직접 제어하지 않습니다. 대신 SceneCore 세션과 상호작용하여 시스템에서 사용하려는 환경에 대한 환경설정을 제공할 수 있습니다. 이 환경설정은 스카이박스 EXR 이미지 또는 도형 glTF로 구성된 SpatialEnvironmentPreference
로 표시됩니다. 앱이 환경설정을 제공할 때 어떤 일이 발생하는지는 앱이 환경설정을 지정할 때의 기능에 따라 다릅니다. 앱에 환경을 변경하는 기능이 있는 경우 시스템은 즉시 사용합니다. 기능이 없는 경우에는 앱에서 해당 기능을 획득할 때 환경설정이 적용됩니다.
예를 들어 앱은 일반적으로 홈 공간 모드에서 실행되는 동안 환경을 변경할 수 없지만 전체 공간 모드에서 실행되는 동안에는 일반적으로 환경을 변경할 수 있습니다. 따라서 사용자가 홈 공간 모드에서 환경 설정을 설정하도록 허용하면 일반적으로 앱이 전체 공간 모드에서 실행될 때까지 해당 설정이 적용되지 않습니다.
XR SceneCore 라이브러리의 종속 항목 추가
공간 환경 수정을 시작하려면 환경 애셋을 로드하고 환경 설정을 지정하는 데 사용할 XR SceneCore 라이브러리의 종속 항목을 추가합니다. 애셋을 로드하는 일부 API에서 ListenableFuture
데이터 유형을 사용하므로 kotlinx-coroutines-guava
아티팩트의 종속 항목도 추가해야 합니다.
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)
...
}
프로젝트에 환경 애셋 추가
고유한 환경 설정을 지정하려면 스카이박스 또는 도형 애셋이 필요합니다. 이 Codelab에서는 green_hills_ktx2_mipmap.glb
도형 애셋만 사용합니다. 이 애셋은 솔루션 코드가 포함된 part2
폴더 또는 GitHub에서 찾을 수 있습니다.
- Android 스튜디오의 Project 창에서 앱 모듈을 마우스 오른쪽 버튼으로 클릭합니다. 그런 다음 New > Folder > Assets Folder를 선택하고 Finish를 클릭하여 폴더를 만듭니다.
- 방금 만든
app/src/main/assets
폴더에 GLB 파일을 추가합니다.
환경 옵션 모델링
UI 코드와 시스템 API 간의 상호작용을 간소화하려면 Kotlin 데이터 클래스를 만들어 각 환경 옵션을 모델링할 수 있습니다.
- Project 창에서
com.example.android.xrfundamentals
패키지를 마우스 오른쪽 버튼으로 클릭하고 New > Package를 선택합니다. 패키지 이름으로com.example.android.xrfundamentals.environment
를 입력합니다. - 패키지를 마우스 오른쪽 버튼으로 클릭하고 New > Kotlin Class/File을 선택합니다. 이름으로
EnvironmentOption
을 입력하고 Data class 유형을 클릭합니다. - 방금 만든 파일에 다음 코드를 추가합니다.
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")
)
로드 애셋을 만들고 SpatialEnvironmentPreference를 반환하는 도우미 추가
이제 데이터 클래스에 도우미 메서드를 추가하면 EnvironmentOption
을 해당하는 SpatialEnvrionmentPreference
로 쉽게 변환할 수 있습니다.
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)
}
}
}
여기서 몇 가지 주목할 점이 있습니다.
- 스카이박스와 도형이 모두 null이면 기본 시스템 환경 설정을 사용해야 함을 나타내기 위해 null이 반환됩니다. 자세한 내용은
setSpatialEnvironmentPreference
를 참고하세요. skybox
및geometry
리소스는 비동기식으로 생성됩니다. 이러한 애셋은 상당히 클 수 있고 메모리로 읽는 데 시간이 걸리기 때문입니다. 프로덕션 앱에서는 환경을 자주 전환하는 경우 이러한 애셋을 메모리에 캐시하는 것이 좋습니다.
환경 선택 UI 구현
UI를 구현하려면 클릭 시 환경 옵션을 순환하는 두 번째 Orbiter를 추가합니다.
Orbiter 추가
- Project 창에서
app
모듈을 마우스 오른쪽 버튼으로 클릭하고 New > Vector Asset을 선택합니다. Clip art 필드를 클릭하고landscape
애셋(채워진 아이콘 모음)을 검색하여 선택한 다음 OK를 클릭하고 Next를 클릭하여 애셋을 만듭니다. com.example.android.xrfundamentals.ui.component
패키지를 마우스 오른쪽 버튼으로 클릭하고 New > Kotlin Class/File을 선택합니다. 이름으로EnvironmentSelectionOrbiter
를 입력하고 File 유형을 클릭합니다.- 방금 만든 파일 내에 다음과 같은
EnvironmentSelectionOrbiter
컴포저블 구현을 추가합니다.
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")
}
}
}
- 마지막으로 기본 공간 패널 내에
EnvironmentSelectionOrbiter
를 추가합니다.
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() }
)
}
...
}
Orbiter를 클릭할 때 환경 변경
모든 것이 작동하도록 하려면 마지막 단계로 EnvironmentSelectionOrbiter
클릭 핸들러에서 setSpatialEnvironmentPreference
를 호출해야 합니다.
- 현재 환경 옵션을 추적하는 변수를 설정합니다(홈 공간 모드와 전체 공간 모드 간에 전환할 때 상태가 유지되도록
Subspace
외부). 또한 현재 XR 세션의 변수와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()
...
}
onClick
콜백을 구현하여 환경 옵션을 순환합니다.
XRFundamentalsApp.kt
EnvironmentSelectionOrbiter(
onClick = {
scope.launch {
currentEnvironmentOptionIndex =
(currentEnvironmentOptionIndex + 1) % ENVIRONMENT_OPTIONS.size
session.scene.spatialEnvironment.setSpatialEnvironmentPreference(
ENVIRONMENT_OPTIONS[currentEnvironmentOptionIndex].toSpatialEnvironmentPreference(
session
)
)
}
}
)
앱을 다시 실행하면 Green Hills 환경과 기본 환경 간에 전환할 수 있습니다.
7. 축하합니다
XR을 최대한 활용하는 방법에 관해 계속 알아보려면 다음 리소스와 연습을 확인하세요.
추가 자료
- XR 디자인은 Android XR 앱을 빌드할 때 사용할 디자인 원칙과 권장사항을 다룹니다.
- Jetpack XR SDK로 개발에는 Android XR 환경을 빌드하는 데 사용할 수 있는 API와 도구에 관한 기술 가이드가 포함되어 있습니다.
- Android XR 앱 품질 가이드라인 페이지에서는 우수한 사용자 환경을 만드는 기준을 설명합니다.
- Hello Android XR 샘플을 살펴보시기 바랍니다.
도전과제
- 추가 환경 애셋을 찾거나 만들고 옵션으로 추가합니다.
- 사용자가
setPassthroughOpacityPreference
API를 사용하여 패스 스루 환경설정을 지정할 수 있도록 환경 컨트롤러와 UI를 수정합니다. 패스 스루를 제어하는 것은 환경 애셋을 변경하는 것과는 다른 기능에 의해 관리됩니다.