Focus showcase - User interaction and positioning
Updated: Sep 29, 2025
Spawn objects relative to user’s position
Ensuring that new objects spawn within the user’s field of view is crucial. The visibility confirms to the user that the object has been created.
To ensure new elements appear in front of a user, their location and viewing direction must be considered.
Steps to use:
- Get the user’s position using the
getHeadPose() function. - Determine the distance from the user where you want the object to be shown.
- Calculate the new position using
Vector3.Forward and modify the height to match the user’s head height. - Ensure the spatial object faces the user by calculating the lookRotation and rotating it 180 degrees on the Y axis.
- Use the
placeInFront() function to place the object in front of the user, incorporating an offset vector for positioning elements in specific poses relative to the user.
// Utils.kt
fun getHeadPose(): Pose {
val head =
Query.where { has(AvatarAttachment.id) }
.eval()
.filter { it.isLocal() && it.getComponent<AvatarAttachment>().type == "head"}
.first()
return head.getComponent<Transform>().transform
}
fun placeInFront(entity: Entity?, offset: Vector3 = Vector3(0f), bigPanel:Boolean = false, nonPanel:Boolean = false) {
val headPose:Pose = getHeadPose();
val isToolbar = entity!! == ImmersiveActivity.instance.get()?.toolbarPanel
// We treat toolbar and big panels differently from other objects.
val height: Float = if (isToolbar) 0.35f else 0.1f
var distanceFromUser: Float = if (bigPanel) 0.9f else 0.7f
// Having the users position, we place the entity in front of it, at a particular distance and height
var newPos = headPose.t + headPose.q * Vector3.Forward * distanceFromUser
newPos.y = headPose.t.y -height
// If there is an offset vector, we place the object at the vector position (using user's position as reference)
if (offset != Vector3(0f)) {
newPos = headPose.t +
headPose.q * Vector3.Right * offset.x +
headPose.q * Vector3.Forward * offset.z
newPos.y = headPose.t.y + offset.y
}
// Add rotation to look in same vector direction as user
var newRot = Quaternion.lookRotation(newPos - headPose.t)
// Rotate 180 degrees to face user in case of non panel objects
if (nonPanel) newRot *= Quaternion(0f, 180f, 0f)
entity.setComponent(Transform(Pose(newPos, newRot)))
}
Create panels and interaction with spatial objects
Focus Projects features 12 main spatial panels, including the Home Panel, the Toolbar, and its sub-panels. Users can create additional panels by generating Sticky Notes, Timers, WebViews, or Spatial Tasks.
You can create panels, make them grabbable, and give them functionality.
Create transparency and spatial text panels
To effectively use spatial text, design a new theme style and set the windowBackground property to transparent, creating a panel with a transparent background.
//themes.xml
<style name="Theme.Focus.Transparent" parent="Base.Theme.Focus">
…
<item name="android:windowBackground">@android:color/transparent</item>
…
</style>
Next, apply the theme to the panel configuration. Set the includeGlass attribute to false in the PanelConfigOptions if you want to hide the panel.
val panelRegistration = PanelRegistration( R.layout.activity_main) {
config {
themeResourceId = R.style.Theme_Focus_Transparent
width = 0.5f
height = 0.5f
includeGlass = false
}
activityClass = MainActivity::class.java
}