Creare, controllare e gestire entità

L'SDK Jetpack XR ti consente di utilizzare Jetpack SceneCore per creare, controllare e gestire istanze Entity come modelli 3D, video stereoscopici e PanelEntity utilizzando Jetpack SceneCore.

Jetpack SceneCore adotta due pattern architetturali comuni per supportare lo sviluppo 3D: un grafo della scena e un sistema entità-componente (ECS).

Utilizzare il grafico della scena per creare e controllare le entità

Per creare e controllare oggetti nello spazio 3D, puoi utilizzare l'API Session di Jetpack SceneCore per accedere al grafico della scena. Il grafico della scena si allinea al mondo reale dell'utente e ti consente di organizzare entità 3D come pannelli e modelli 3D in una struttura gerarchica e di conservare lo stato di queste entità.

Una volta ottenuto l'accesso al grafico della scena, puoi utilizzare le API in Jetpack Compose per XR per creare UI spaziali (ad esempio, istanze SpatialPanel e Orbiter) all'interno del grafico della scena. Per i contenuti 3D come i modelli 3D, puoi accedere direttamente alla sessione. Per saperne di più, consulta Informazioni su ActivitySpace in questa pagina.

Sistema a componenti di entità

Un sistema di componenti di entità segue il principio della composizione rispetto all'ereditarietà. Puoi espandere il comportamento delle entità collegando componenti che definiscono il comportamento, il che ti consente di applicare lo stesso comportamento a diversi tipi di entità. Per saperne di più, consulta Aggiungere un comportamento comune alle entità in questa pagina.

Informazioni su ActivitySpace

Ogni Session ha un ActivitySpace creato automaticamente con il Session. ActivitySpace è il Entity di primo livello nel grafico della scena.

ActivitySpace rappresenta uno spazio tridimensionale con un sistema di coordinate destrorso (l'asse x punta a destra, l'asse y punta verso l'alto e l'asse z punta all'indietro rispetto all'origine) e con unità di misura in metri che corrispondono al mondo reale. L'origine di ActivitySpace è in qualche modo arbitraria (in quanto gli utenti possono reimpostare la posizione di ActivitySpace nel mondo reale), pertanto è consigliabile posizionare i contenuti in relazione tra loro anziché in relazione all'origine.

Utilizzare le entità

Le entità sono fondamentali per SceneCore. Quasi tutto ciò che l'utente vede e con cui interagisce sono entità che rappresentano pannelli, modelli 3D e altro ancora.

Poiché ActivitySpace è il nodo di primo livello del grafico della scena, per impostazione predefinita, tutte le nuove entità vengono inserite direttamente in ActivitySpace. Puoi riposizionare le entità lungo il grafico della scena impostando il relativo parent o utilizzando addChild().

Le entità hanno alcuni comportamenti predefiniti per elementi universali per tutte le entità, come la modifica di posizione, rotazione o visibilità. Sottoclassi Entity specifiche, come GltfModelEntity, hanno comportamenti aggiuntivi che supportano la sottoclasse.

Manipolare le entità

Quando apporti una modifica a una proprietà Entity che appartiene alla classe Entity di base, la modifica viene applicata a tutti i relativi elementi secondari. Ad esempio, se modifichi il Pose di un Entity principale, tutti i relativi elementi secondari avranno lo stesso aggiustamento. Una modifica apportata a un Entity secondario non influisce su quello principale.

Un Pose rappresenta la posizione e la rotazione dell'entità nello spazio 3D. La posizione è un Vector3 costituito da posizioni numeriche x, y e z. La rotazione è rappresentata da un Quaternion. La posizione di un Entity è sempre relativa alla sua entità principale. In altre parole, un Entity la cui posizione è (0, 0, 0) verrà posizionato all'origine della relativa entità principale.

// 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))

Per disattivare un Entity, utilizza setEnabled(). In questo modo diventa invisibile e viene interrotta tutta l'elaborazione.

// Disable the entity.
entity.setEnabled(false)

Per ridimensionare un Entity mantenendone la forma complessiva, utilizza setScale().

// Double the size of the entity
entity.setScale(2f)

Aggiungere un comportamento comune alle entità

Puoi utilizzare i seguenti componenti per aggiungere un comportamento comune alle entità:

L'istanza dei componenti deve essere creata tramite il metodo di creazione appropriato nella classe Session. Ad esempio, per creare un ResizableComponent, chiama ResizableComponent.create().

Per aggiungere il comportamento specifico del componente a un Entity, utilizza il metodo addComponent().

Utilizzare MovableComponent per rendere un'entità spostabile dall'utente

Il MovableComponent consente all'utente di spostare un Entity.

Gli eventi di movimento vengono inviati al componente quando si interagisce con le decorazioni. Il comportamento predefinito del sistema, creato con MovableComponent.createSystemMovable(), sposta Entity quando le decorazioni vengono trascinate:

val movableComponent = MovableComponent.createSystemMovable(session)
entity.addComponent(movableComponent)

Il parametro facoltativo scaleInZ (impostato per impostazione predefinita su true) fa sì che l'entità regoli automaticamente la sua scala man mano che si allontana dall'utente, in modo simile a come i pannelli vengono scalati dal sistema nello spazio della casa. A causa della natura "a cascata" del sistema di componenti delle entità, la scala dell'entità principale influirà su tutte le entità secondarie.

Puoi anche specificare se l'entità può essere ancorata a un tipo di superficie come superfici orizzontali o verticali o a superfici semantiche specifiche come tavolo, parete o soffitto. Per specificare le opzioni di ancoraggio, specifica un insieme di AnchorPlacement durante la creazione di MovableComponent. In questo esempio, l'entità che può essere spostata e ancorata a qualsiasi piano o superficie orizzontale del tavolo:

val anchorPlacement = AnchorPlacement.createForPlanes(
    anchorablePlaneOrientations = setOf(PlaneOrientation.VERTICAL),
    anchorablePlaneSemanticTypes = setOf(PlaneSemanticType.FLOOR, PlaneSemanticType.TABLE)
)

val movableComponent = MovableComponent.createAnchorable(
    session = session,
    anchorPlacement = setOf(anchorPlacement)
)
entity.addComponent(movableComponent)

Utilizza ResizableComponent per rendere ridimensionabile un'entità

Il ResizableComponent consente agli utenti di ridimensionare un Entity. ResizableComponent include segnali visivi di interazione che invitano l'utente a ridimensionare un Entity. Quando crei il ResizableComponent, puoi specificare una dimensione minima o massima (in metri). Hai anche la possibilità di specificare proporzioni fisse durante il ridimensionamento, in modo che la larghezza e l'altezza vengano ridimensionate in modo proporzionale tra loro.

Quando crei un ResizableComponent, specifica un resizeEventListener che gestisce gli eventi di aggiornamento. Puoi rispondere a diversi eventi ResizeState, come RESIZE_STATE_ONGOING o RESIZE_STATE_END.

Ecco un esempio di utilizzo di ResizableComponent con proporzioni fisse su un SurfaceEntity:

val resizableComponent = ResizableComponent.create(session) { event ->
    if (event.resizeState == ResizeEvent.ResizeState.RESIZE_STATE_END) {
        // update the Entity to reflect the new size
        surfaceEntity.canvasShape = SurfaceEntity.CanvasShape.Quad(event.newSize.width, event.newSize.height)
    }
}
resizableComponent.minimumEntitySize = FloatSize3d(177f, 100f, 1f)
resizableComponent.fixedAspectRatio = 16f / 9f // Specify a 16:9 aspect ratio

surfaceEntity.addComponent(resizableComponent)

Utilizzare InteractableComponent per acquisire gli eventi di input dell'utente

InteractableComponent consente di acquisire gli eventi di input dell'utente, ad esempio quando l'utente interagisce o passa il mouse sopra un Entity. Quando crei un InteractableComponent, specifica un listener che riceva gli eventi di input. Quando l'utente esegue un'azione di input, il listener viene chiamato con le informazioni di input fornite nel parametro InputEvent.

Per un elenco completo di tutte le costanti InputEvent, consulta la documentazione di riferimento.

Il seguente snippet di codice mostra un esempio di utilizzo di un InteractableComponent per aumentare le dimensioni di un'entità con la mano destra e diminuirle con la mano sinistra.

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.SOURCE_HANDS && it.action == InputEvent.Action.ACTION_UP) {
        // increase size with right hand and decrease with left
        if (it.pointerType == InputEvent.Pointer.POINTER_TYPE_RIGHT) {
            entity.setScale(1.5f)
        } else if (it.pointerType == InputEvent.Pointer.POINTER_TYPE_LEFT) {
            entity.setScale(0.5f)
        }
    }
}
entity.addComponent(interactableComponent)