항목 만들기, 제어, 관리

Jetpack XR SDK를 사용하면 Jetpack SceneCore를 사용하여 3D 모델, 스테레오스코피 비디오, PanelEntity과 같은 Entity 인스턴스를 만들고 제어하고 관리할 수 있습니다.

Jetpack SceneCore는 3D 개발을 지원하기 위해 장면 그래프엔티티-컴포넌트 시스템 (ECS)이라는 두 가지 일반적인 아키텍처 패턴을 채택합니다.

장면 그래프를 사용하여 엔티티 생성 및 제어

3D 공간에서 객체를 만들고 제어하려면 Jetpack SceneCore의 세션 API를 사용하여 장면 그래프에 액세스하면 됩니다. 장면 그래프는 사용자의 실제 환경과 일치하며 패널, 3D 모델과 같은 3D 엔티티를 계층 구조로 정리하고 이러한 엔티티의 상태를 유지할 수 있습니다.

장면 그래프에 액세스하면 Jetpack Compose for XR의 API를 사용하여 장면 그래프 내에 공간 UI (예: SpatialPanelOrbiter 인스턴스)를 만들 수 있습니다. 3D 모델과 같은 3D 콘텐츠의 경우 세션에 직접 액세스할 수 있습니다. 자세한 내용은 이 페이지의 ActivitySpace 정보를 참고하세요.

엔티티 컴포넌트 시스템

엔티티-컴포넌트 시스템은 상속보다 컴포지션 원칙을 따릅니다. 동작 정의 구성요소를 연결하여 항목의 동작을 확장할 수 있습니다. 이렇게 하면 다양한 유형의 항목에 동일한 동작을 적용할 수 있습니다. 자세한 내용은 이 페이지의 엔티티에 공통 동작 추가를 참고하세요.

ActivitySpace 정보

Session에는 Session와 함께 자동으로 생성되는 ActivitySpace가 있습니다. ActivitySpace은 장면 그래프의 최상위 Entity입니다.

ActivitySpace는 오른손 좌표계(x축은 오른쪽을 가리키고 y축은 위쪽을 가리키며 z축은 원점을 기준으로 뒤쪽을 가리킴)와 실제 세계와 일치하는 단위(미터)를 사용하는 3차원 공간을 나타냅니다. ActivitySpace의 원점은 다소 임의적입니다 (사용자가 실제 세계에서 ActivitySpace의 위치를 재설정할 수 있으므로). 따라서 원점을 기준으로 콘텐츠를 배치하는 대신 서로를 기준으로 콘텐츠를 배치하는 것이 좋습니다.

항목 작업

항목은 SceneCore의 중심입니다. 사용자가 보고 상호작용하는 대부분의 항목은 패널, 3D 모델 등을 나타내는 항목입니다.

ActivitySpace은 장면 그래프의 최상위 노드이므로 기본적으로 모든 새 항목은 ActivitySpace에 직접 배치됩니다. setParent() 또는 addChild()를 호출하여 장면 그래프를 따라 엔티티를 재배치할 수 있습니다.

엔티티에는 위치, 회전 또는 표시 여부 변경과 같이 모든 엔티티에 공통적인 사항에 관한 기본 동작이 있습니다. GltfModelEntity와 같은 특정 Entity 서브클래스에는 서브클래스를 지원하는 추가 동작이 있습니다.

항목 조작

기본 Entity 클래스에 속하는 Entity 속성을 변경하면 변경사항이 모든 하위 요소로 캐스케이드됩니다. 예를 들어 상위 EntityPose를 조정하면 모든 하위 요소가 동일하게 조정됩니다. 하위 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)

엔티티에 공통 동작 추가

다음 구성요소를 사용하여 엔티티에 일반적인 동작을 추가할 수 있습니다.

구성요소 인스턴스화는 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을 사용하여 Entity의 크기를 사용자가 조정할 수 있도록 설정

ResizableComponent를 사용하면 사용자가 Entity의 크기를 조절할 수 있습니다. ResizableComponent에는 사용자가 Entity의 크기를 조절하도록 유도하는 시각적 상호작용 신호가 포함되어 있습니다. ResizableComponent를 만들 때 최소 또는 최대 크기 (미터)를 지정할 수 있습니다. 크기를 조절할 때 너비와 높이가 서로 비례하여 조절되도록 고정된 가로세로 비율을 지정할 수도 있습니다.

ResizableComponent를 사용할 때는 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에 참여하거나 마우스를 가져갈 때와 같은 사용자로부터의 입력 이벤트를 캡처할 수 있습니다. InteractableComponent를 만들 때 입력 이벤트를 수신할 InputEventListener를 지정해야 합니다. 사용자가 입력 작업을 실행하면 InputEvent 매개변수에 제공된 특정 입력 정보와 함께 onInputEvent 메서드가 호출됩니다.

모든 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)