config.jsonCustomComponentsSample project in Android Studio, connect your Quest device, and run the app. The sample loads a 3D collaboration room with a robot that tracks a basketball and a welcome panel that rotates to face the viewer.| File / Scene | What it demonstrates | Key concepts |
|---|---|---|
Custom component schema with typed attributes and enum definition | XML component schema, EntityAttribute, BooleanAttribute, EnumAttribute, FloatAttribute, Vector3Attribute, default values | |
Registration of custom components and systems, loading scenes, attaching components at runtime | componentManager.registerComponent(), systemManager.registerSystem(), GLXF inflation, runtime component attachment | |
ECS system that processes LookAt components every frame | SystemBase, execute(), Query.where, smooth rotation with slerp(), viewer pose | |
Custom debug metrics for tracking rotation state | OVRMetricsGroup, position overlays, pitch/yaw/roll graphs | |
Compose UI panel with spatial theme | ComposeViewPanelRegistration, SpatialTheme, UISet library | |
Scene-authored custom component on panel node, grabbable entities | Editor-authored LookAt component, lookAtHead: true, Y-axis rotation, Grabbable components | |
Configuration linking Meta Spatial Editor to custom component schemas | spatial.editor.customComponentXmlsPath, editor-code bridge |
Grabbable component), and the robot tracks it in real time. A welcome panel displays “Custom Components Sample” with descriptive text and always faces your head using Y-axis-only rotation, so it stays upright while smoothly rotating to follow you as you move around the room.LookAt component in LookAt.xml using an XML schema. The schema specifies typed attributes with default values:<Component name="LookAt"> <EntityAttribute name="target" /> <BooleanAttribute name="lookAtHead" defaultValue="false" /> <EnumAttribute name="axis" defaultValue="LookAtAxis.ALL" /> <!-- plus FloatAttribute "speed" and Vector3Attribute "offset" -->
LookAtAxis enum with two values (ALL and Y) and several typed attributes: an entity reference for the target, a boolean for tracking the viewer’s head, an enum for rotation constraints, a float for interpolation speed, and a Vector3 for position offset. The Spatial SDK Gradle plugin generates a Kotlin class from this schema at build time.LookAtSystem extends SystemBase and overrides execute(), which the SDK calls every frame. The system queries for entities that have both LookAt and Transform components:override fun execute() {
Query.where { has(LookAt.id, Transform.id) }.eval().forEach { entity ->
val lookAt = entity.getComponent<LookAt>()
// ... compute rotation and update transform
LookAt component to determine the target, calculates the desired rotation using Quaternion.lookRotation() or Quaternion.lookRotationAroundY() based on the axis enum, and applies smooth interpolation via slerp(). It writes the updated rotation back to the entity’s Transform component using entity.setComponent().CustomComponentsSampleActivity.onCreate() after calling super.onCreate():componentManager.registerComponent<LookAt>(LookAt.Companion) systemManager.registerSystem(LookAtSystem())
componentManager and systemManager are inherited properties from AppSystemActivity. Registration makes the component type available to both runtime code and the Meta Spatial Editor. The component must be registered before any entities using it are loaded or created, which is why registration happens early in onCreate().LookAt component to the robot entity after loading the scene:robot.setComponent(LookAt(basketBall, offset = Vector3(x = 0f, y = 180f, z = 0f)))
Main.scene, where the panel node has a LookAt component with lookAtHead: true and axis: 1 (Y-axis only). This demonstrates that custom components work identically whether attached in code or authored in the editor.config.json file tells Meta Spatial Editor where to find custom component XML schemas:"spatial.editor.customComponentXmlsPath": ["../src/main/components/"]
LookAt component definition and expose it in the component palette. Without this bridge, the editor cannot author custom components defined in your codebase. If you add new component XML files to a different directory, update this path to include them.LookAt.xml schema to add a maxDistance float attribute that disables rotation when the target is beyond a threshold, and implement the distance check in LookAtSystem.execute().Orbit) that makes an entity circle around a target at a configurable radius and speed, with a corresponding OrbitSystem.Main.scene using Meta Spatial Editor to see how editor-authored and code-authored components interact on the same entity.